小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。
TIP 👉 老当益壮,宁移白首之心;穷且益坚,不坠青云之志。——王勃《滕王阁序》
前言
主要的目的将其中一些共同的业务场景封装为组件,避免去编写重复的代码,将精力放在更加核心的部分,因此就需要将这些重复的代码抽取出来,封装成公共的组件,提高开发效率,于是产生了这个组件,API不算多,欢迎大家指出问题。一、弹出窗口组件(只PC端可用)
1. Popup 组件
Popup 属性
1. value
- 是否显示弹框
- 值为布尔类型
- 默认值:false
2. showClose
- 是否显示关闭按钮
- 值为布尔类型
- 默认值为true
3. title
- 弹出窗口标题
- 值为字符串
- Popup 默认为'提示',Alert 和Confirm 默认为空
4. showClose
- 是否显示右上角关闭按钮
- 值为布尔类型
- Popup 默认为true,Alert 和Confirm 默认为false
5. message
- 信息内容
- 值为字符串
如果有默认插槽则该配置无效
6. html
- html内容
- 值为字符串
如果有默认插槽则该配置无效 如果message有值则该配置无效
7. content
- 弹框内容组件
- 值为Vue组件
如果有默认插槽则该配置无效 如果message或者html有值则该配置无效
8. contentProps
- 弹窗内容组件的props
- 值为Object类型,属性名为组件的props的属性
content 有值时该配置才有效
9. contentEvents
- 弹窗内容组件的事件
- 值为Object类型,属性名为组件事件名,属性值为事件回调方法
content 有值时该配置才有效
10. contentWrapperStyle
- 内容包裹器样式
- 值为Object类型,属性名为样式
<BasePopup v-model="isShow" message="保存成功!"
:contentWrapperStyle="{ fontWeight: 'bold' }"></BasePopup>
11. width
- 弹出窗口宽度
- 值为数值类型
- 默认值:500,单位为像素
12. height
- 弹出窗口宽度
- 值为数值类型
- 默认值:300,单位为像素
13. className
- 弹框自定义样式名
- 值为字符串
14. scroll
- 内容区域是否需要滚动条(scroll为false时如果内容溢出会自动显示滚动条)
- 值为布尔类型
- 默认值:false
15. cover
- 遮罩层覆盖范围
- 值为字符串
- 默认值:“full”
- full 覆盖整个屏幕
- tab 覆盖tab页功能区域,左侧菜单栏、顶部区域、tab页签选择区域不会被覆盖
插槽
1. default
- 默认插槽为弹框内容
2. bottom
- 弹框底部插槽,一般用于放操作按钮
脚本调用特有属性
1.store
- Vuex 的 store 对象,当弹窗中需要使用 Vuex 的全局数据时设置
当使用
vm.$popup()或者vm.$popupAlert()或者vm.$popupConfirm()时会默认设置为当前 Vue 实例的 $store
2.router
- Vue Router 的 router 对象,当弹窗中需要使用路由时设置
当使用
vm.$popup()或者vm.$popupAlert()或者vm.$popupConfirm()时会默认设置为当前 Vue 实例的 $router
Alert 组件特有属性
1.btnText
- 按钮文字
- 值为字符串
- 默认值:'确定'
Alert 组件特有事件
1.ok
- 点击按钮后触发的事件
- 无参数
Confirm 组件特有属性
1.okBtnText
- “确定”按钮文字
- 值为字符串
- 默认值:'确定'
2.cancelBtnText
- “取消”按钮文字
- 值为字符串
- 默认值:'取消'
Confirm 组件特有事件
1.ok
- 点击“确定”按钮后触发的事件
- 无参数
2.cancel
- 点击“取消”按钮后触发的事件
- 无参数
二、弹出窗口示例
1. Popup 示例
简单示例
<template>
<div>
<BaseButton @click="doOpen()">简单弹窗</BaseButton>
<!-- 简单弹框 -->
<BasePopup v-model="isShow" title="提示信息" message="已修改成功!"></BasePopup>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BasePopup from '@/components/pc/popup/Popup.vue'
export default {
name: 'PcPopupSimpleDemo',
components: {
BaseButton,
BasePopup
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
}
}
}
</script>
自定义内容示例
<template>
<div>
<BaseButton @click="doOpen()">自定义内容</BaseButton>
<!-- 自定义内容的弹框 -->
<BasePopup v-model="isShow" title="提示信息" :width="300" :height="280">
<div class="info-content">
<p><Icon name="cry" class="info-icon"></Icon></p>
<p>修改失败了</p>
</div>
</BasePopup>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BasePopup from '@/components/pc/popup/Popup.vue'
export default {
name: 'PcPopupDemo',
components: {
BaseButton,
BasePopup
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
}
}
}
</script>
<style lang="scss" scoped px2rem="false">
.info-content{
padding: 50px 24px;
font-size: 16px;
text-align: center;
.info-icon{
font-size: 84px;
@include primary-font-color();
margin-bottom: 10px;
}
}
</style>
带滚动条示例
<template>
<div>
<BaseButton @click="doOpen()">带滚动条</BaseButton>
<!-- 带滚动条的自定义内容的弹框 -->
<BasePopup v-model="isShow" title="银行列表" :width="260" :height="320" :scroll="true">
<div class="info-content">
<ul>
<li>中国建设银行</li>
<li>中国工商银行</li>
<li>中国农业银行</li>
<li>中国交通银行</li>
<li>中国银行</li>
<li>中国招商银行</li>
<li>邮政储蓄银行</li>
</ul>
</div>
</BasePopup>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BasePopup from '@/components/pc/popup/Popup.vue'
export default {
name: 'PcPopupScrollDemo',
components: {
BaseButton,
BasePopup
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
}
}
}
</script>
<style lang="scss" scoped px2rem="false">
.info-content{
padding: 24px;
font-size: 16px;
line-height: 50px;
ul {
list-style: none;
}
}
</style>
动态指定组件作为内容示例
<template>
<div>
<BaseButton @click="doOpen()">动态指定组件作为内容</BaseButton>
<!-- 动态指定组件作为内容的弹框 -->
<BasePopup v-model="isShow" title="用户信息" :width="300" :height="200" :scroll="true"
:content="contentComponent" :contentProps="contentProps"
:contentEvents="contentEvents" :contentWrapperStyle="contentWrapperStyle"></BasePopup>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BasePopup from '@/components/pc/popup/Popup.vue'
import Content from './Content.vue'
export default {
name: 'PcPopupDynamicDemo',
components: {
BaseButton,
BasePopup
},
data () {
return {
isShow: false,
contentComponent: Content,
contentProps: {
name: 'Tom',
phone: 18399998888
},
contentEvents: {
click () {
console.log('点击了用户信息')
}
},
contentWrapperStyle: {
padding: '30px 20px 30px 50px',
textAlign: 'left'
}
}
},
methods: {
doOpen () {
this.isShow = true
}
}
}
</script>
2. Alert 示例
简单带一个按钮示例
<template>
<div>
<BaseButton @click="doOpen()">带一个按钮</BaseButton>
<!-- 带一个按钮的警告弹框 -->
<BaseAlert v-model="isShow" message="保存成功!" btnText="继续" @ok="doOK"></BaseAlert>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BaseAlert from '@/components/pc/popup/Alert.vue'
export default {
name: 'PcAlertDemo',
components: {
BaseButton,
BaseAlert
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
},
doOK () {
console.log('触发ok事件')
}
}
}
</script>
带一个按钮(有标题、可关闭)示例
<template>
<div>
<BaseButton @click="doOpen()">带一个按钮(有标题、可关闭)</BaseButton>
<!-- 带一个按钮(有标题、可关闭)的警告弹框 -->
<BaseAlert v-model="isShow" message="保存成功!" title="提示" :showClose="true" btnText="确定" @ok="doOK"></BaseAlert>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BaseAlert from '@/components/pc/popup/Alert.vue'
export default {
name: 'PcAlertHeaderDemo',
components: {
BaseButton,
BaseAlert
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
},
doOK () {
console.log('触发ok事件')
}
}
}
</script>
3. Confirm 示例
带确认、取消按钮示例
<template>
<div>
<BaseButton @click="doOpen()">带确认、取消按钮</BaseButton>
<!-- 带确定和取消按钮的确认弹框 -->
<BaseConfirm v-model="isShow" message="确定删除订单?" @ok="doOK" @cancel="doCancel"></BaseConfirm>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BaseConfirm from '@/components/pc/popup/Confirm.vue'
export default {
name: 'PcConfirmDemo',
components: {
BaseButton,
BaseConfirm
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
},
doOK () {
console.log('触发ok事件')
},
doCancel () {
console.log('触发cancel事件')
}
}
}
</script>
带确认、取消按钮(有标题、可关闭)示例
<template>
<div>
<BaseButton @click="doOpen()">带确认、取消按钮(有标题、可关闭)</BaseButton>
<!-- 带确定和取消按钮(有标题、可关闭)的确认弹框 -->
<BaseConfirm v-model="isShow" message="确定删除订单?" title="提示" :showClose="true" @ok="doOK" @cancel="doCancel"></BaseConfirm>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import BaseConfirm from '@/components/pc/popup/Confirm.vue'
export default {
name: 'PcConfirmHeaderDemo',
components: {
BaseButton,
BaseConfirm
},
data () {
return {
isShow: false
}
},
methods: {
doOpen () {
this.isShow = true
},
doOK () {
console.log('触发ok事件')
},
doCancel () {
console.log('触发cancel事件')
}
}
}
</script>
三、脚本方式调用示例
1. Popup 示例
简单弹窗示例
<template>
<div>
<BaseButton @click="doOpen()">简单弹窗</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popup } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsPopupSimpleDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popup('保存成功!')
}
}
}
</script>
设置弹窗参数示例
<template>
<div>
<BaseButton @click="doOpen()">设置弹窗参数</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popup } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsPopupDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popup({
title: '提示信息',
message: '保存成功!',
width: 250,
height: 200
})
}
}
}
</script>
html作为内容示例
<template>
<div>
<BaseButton @click="doOpen()">html作为内容</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popup } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsHtmlPopupDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popup({ html: `<div class="message">
<p style="color: red;font-size: 30px;margin-bottom: 20px">导入失败!</p>
<p>第1行:身份证号不能为空!</p>
<p>第3行:手机号码不能为空!</p>
</div>` })
}
}
}
</script>
2. Alert 示例
简单带一个按钮示例
<template>
<div>
<BaseButton @click="doOpen()">带一个按钮</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popupAlert } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsAlertDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popupAlert({
message: '确定要删除?',
btnText: '确认',
onOK () {
console.log('触发ok事件')
}
})
}
}
}
</script>
带一个按钮(有标题、可关闭)示例
<template>
<div>
<BaseButton @click="doOpen()">带一个按钮(有标题、可关闭)</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popupAlert } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsAlertHeaderDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popupAlert({
title: '提示',
showClose: true,
message: '确定要删除?',
btnText: '确认',
onOK () {
console.log('触发ok事件')
}
})
}
}
}
</script>
3. Confirm 示例
带确认、取消按钮示例
<template>
<div>
<BaseButton @click="doOpen()">带确认、取消按钮</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popupConfirm } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsConfirmDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popupConfirm({
message: '是否继续?',
okBtnText: '继续',
cancelBtnText: '不了',
onOK: () => {
console.log('触发ok事件')
},
onCancel: () => {
console.log('触发cancel事件')
}
})
}
}
}
</script>
带确认、取消按钮(有标题、可关闭)示例
<template>
<div>
<BaseButton @click="doOpen()">带确认、取消按钮(有标题、可关闭)</BaseButton>
</div>
</template>
<script>
import BaseButton from '@/components/base/button/index.vue'
import { popupConfirm } from '@/components/pc/popup/index.js'
export default {
name: 'PcJsConfirmHeaderDemo',
components: {
BaseButton
},
methods: {
doOpen () {
popupConfirm({
title: '提示',
showClose: true,
message: '是否继续?',
okBtnText: '继续',
cancelBtnText: '不了',
onOK: () => {
console.log('触发ok事件')
},
onCancel: () => {
console.log('触发cancel事件')
}
})
}
}
}
</script>
等等
实现
实现一个popup.vue
<!-- 弹出窗口组件 -->
<template>
<div class="popup" :class="{'full-screen': fullScreen, 'fold-menu': foldMenu, 'show-tabs': isShowTabs,
'cover-full': cover==='full', 'cover-tab': cover==='tab'}">
<transition name="mask">
<div ref="popupMask" class="popup-mask" v-show="visible"></div>
</transition>
<transition name="pop">
<div class="popup-box" v-show="visible" :class="className" :style="popupStyle" ref="popBox">
<div class="close" v-show="showClose" @click="doClose"><Icon name="close" class="close-icon"></Icon></div>
<div class="popup-pane" :class="noHeader? 'no-header' : ''">
<div class="title" v-show="title">{{title}}</div>
<div ref="contentWrap" class="content-wrap" :style="{overflow: needScroll ? 'auto' : 'hidden', ...contentWrapperStyle}">
<slot>
<div v-if="message" :class="needScroll ? 'scroll-message' : 'center-message'">{{message}}</div>
<div v-else-if="html" class="html-message" v-html="html"></div>
<component v-else-if="content" :is="content" v-bind="contentProps" v-on="contentEvents"></component>
</slot>
</div>
<div class="bottom">
<slot name="bottom"></slot>
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
/* eslint-disable no-undef */
const isShowTabs = APP_SHOW_TABS // 是否显示功能切换tab页签
export default {
name: 'Popup',
props: {
// 是否显示弹框
value: {
type: Boolean,
default: false
},
// 标题
title: String,
// 是否显示关闭按钮
showClose: {
type: Boolean,
default: true
},
// 信息内容
message: String,
// html字符串内容
html: String,
// 弹窗内容组件
content: Object,
// 弹窗内容组件的props
contentProps: Object,
// 弹窗内容组件的事件
contentEvents: Object,
// 内容包裹器样式
contentWrapperStyle: Object,
// 弹框宽度
width: {
type: Number,
default: 300
},
// 弹框高度
height: {
type: Number,
default: 220
},
// 样式名
className: {
type: String,
default: ''
},
// 内容区域是否需要滚动条
scroll: {
type: Boolean,
default: false
},
// 遮罩层覆盖范围(full: 覆盖整个页面, tab: 覆盖当前tab页功能区域)
cover: {
type: String,
default: 'full'
}
},
data () {
return {
// 是否显示
visible: this.value,
// 内容是否溢出
contentOverflow: false,
// 弹窗宽度
boxWidth: this.width,
// 弹窗高度
boxHeight: this.height,
// 是否显示功能切换tab页签
isShowTabs: isShowTabs,
// scrollEventId: 'popup' + new Date().getTime()
// 定时器ID
timeoutId: null
}
},
computed: {
// 是否不显示头部(标题和关闭按钮)
noHeader () {
return !this.title && !this.showClose
},
// 计算弹框宽高位置相关样式
popupStyle () {
let styleObj = {}
let boxWidth = this.boxWidth
let boxHeight = this.boxHeight
styleObj.width = boxWidth + 'px'
styleObj.height = boxHeight + 'px'
styleObj.marginLeft = `-${boxWidth / 2}px`
styleObj.marginTop = `-${boxHeight / 2}px`
if (boxWidth < 150) {
styleObj.display = 'none'
}
return styleObj
},
// 是否需要显示滚动条
needScroll () {
return this.scroll || this.contentOverflow
},
fullScreen () {
if (this.$store && this.$store.getters && this.$store.getters.fullScreen) {
return true
}
return false
},
foldMenu () {
if (this.$store && this.$store.getters && this.$store.getters.foldMenu) {
return true
}
return false
}
},
watch: {
value (val) {
this.visible = val
},
visible (val) {
if (val && !this.scroll) {
setTimeout(() => {
const contentWrap = this.$refs.contentWrap
const content = this.$refs.contentWrap.firstChild
const contentWrapHeight = contentWrap.getBoundingClientRect().height
const contentHeight = content.getBoundingClientRect().height
console.log('弹窗内容包裹元素高度=', contentWrapHeight, ',弹窗内容元素高度=', contentHeight)
if (contentHeight > contentWrapHeight) {
this.contentOverflow = true
}
}, 100)
}
/* if (val && this.scroll) {
this.$nextTick(() => {
this.$eventBus.$emit('init-scroll-' + this.scrollEventId)
})
} */
},
cover (val) {
if (val === 'tab') {
// 弹出只遮罩页签功能区域的弹窗时,触发事件控制横向滚动条
setTimeout(() => {
this.$eventBus.$emit('TAB_POPUP')
}, 0)
}
},
width (val) {
this.initSize()
},
height (val) {
this.initSize()
},
fullScreen (val) {
if (this.cover === 'tab') {
setTimeout(() => {
this.initSize()
}, 300)
}
},
foldMenu (val) {
if (this.cover === 'tab') {
setTimeout(() => {
this.initSize()
}, 300)
}
}
},
mounted () {
// 初始化弹窗大小,设置弹窗大小不超过浏览器窗口大小
setTimeout(() => {
this.initSize()
}, 50)
window.addEventListener('resize', this.initSize)
// 当用户登录失效时由于要跳转到登录页,关闭弹出窗口
this.$eventBus.$on('NO_AUTH_EVENT', this.doClose)
},
beforeDestroy () {
if (this.timeoutId) {
clearTimeout(this.timeoutId)
}
window.removeEventListener('resize', this.initSize)
this.$eventBus.$off('NO_AUTH_EVENT', this.doClose)
// 关闭弹出窗口时,如果是只遮罩页签功能区域的弹窗,触发事件控制横向滚动条
if (this.cover === 'tab') {
this.$eventBus.$emit('TAB_POPUP')
}
},
methods: {
// 初始化弹窗大小,设置弹窗大小不超过浏览器窗口大小
initSize () {
let maxWidth = window.innerWidth
let maxHeight = window.innerHeight
let coverElement = document.getElementById('tabsViewContent')
if (!coverElement) {
coverElement = document.getElementById('viewContent')
}
if (this.cover === 'tab' && coverElement) {
if (coverElement.clientWidth < maxWidth) {
maxWidth = coverElement.clientWidth
}
if (coverElement.clientHeight < maxHeight) {
maxHeight = coverElement.clientHeight
}
let offsetLeft = coverElement.getBoundingClientRect().left
let winWidth = window.innerWidth
let mainViewWidth = winWidth - offsetLeft
if (mainViewWidth < maxWidth) {
maxWidth = mainViewWidth
}
}
if (maxWidth && (maxWidth - 10) < this.width) {
this.boxWidth = maxWidth - 10
} else {
this.boxWidth = this.width
}
if (maxHeight && (maxHeight - 10) < this.height) {
this.boxHeight = maxHeight - 10
} else {
this.boxHeight = this.height
}
},
doClose () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('close')
},
close (timeout) {
if (typeof timeout === 'number' && timeout > 0) {
this.timeoutId = setTimeout(() => {
this.doClose()
}, timeout)
} else {
this.doClose()
}
}
}
}
</script>
<style lang="scss" scoped px2rem="false">
$box-border-radius: 3px;
$title-height: 54px;
$close-size: 12px;
$content-tb-padding: 18px;
$content-lr-padding: 24px;
.popup{
z-index: 1000;
.popup-mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,.5);
transition: all .3s ease;
z-index: 1000;
}
.popup-box{
position: fixed;
width: 500px;
height: 300px;
top: 50%;
left: 50%;
margin-left: -250px;
margin-top: -150px;
background-color: #FFF;
border-radius: $box-border-radius;
transition: all .2s ease;
z-index: 1000;
.close{
height: $title-height;
width: $title-height;
position: absolute;
top: 0;
right: 0;
text-align: center;
padding-top: ($title-height - $close-size) / 2;
z-index: 1003;
.close-icon{
line-height: 1;
font-size: $close-size;
color: #666;
}
}
.popup-pane{
height: 100%;
padding: 20px 0;
.title{
margin-top: -20px;
height: $title-height;
line-height: $title-height;
border-bottom: solid 1px #E5E5E5;
font-size: 16px;
text-align: left;
padding: 0 ($content-lr-padding * 2 + $close-size) 0 $content-lr-padding;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #666;
font-weight: bold;
}
.content-wrap{
position: absolute;
top: $title-height;
bottom: 0;
left: 0;
right: 0;
.center-message{
position: absolute;
width: 100%;
top: 50%;
transform: translateY(-50%);
padding: $content-tb-padding $content-lr-padding;
box-sizing: border-box;
font-size: 16px;
line-height: 1.5;
text-align: center;
word-break: break-all;
white-space: pre-wrap;
}
.scroll-message {
padding: $content-tb-padding $content-lr-padding;
font-size: 16px;
line-height: 1.5;
}
.html-message {
font-size: 16px;
line-height: 1.5;
::v-deep .message{
padding: $content-tb-padding $content-lr-padding;
text-align: center;
}
}
}
&.no-header .content-wrap {
top: 0;
}
.scroll{
overflow-y: auto;
}
.bottom{
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
}
}
}
&.cover-tab {
.popup-mask {
left: $app-menu-width;
top: $app-header-height;
transition: all .2s ease;
transition-delay: .1s;
}
.popup-box {
transform: translate($app-menu-width / 2, ($app-header-height) / 2);
}
&.fold-menu {
.popup-mask {
left: $app-menu-fold-width;
}
.popup-box {
transform: translate($app-menu-fold-width / 2, $app-header-height / 2);
}
}
&.show-tabs {
.popup-mask {
top: $app-header-height + $app-tabs-height;
}
.popup-box {
transform: translate($app-menu-width / 2, ($app-header-height + $app-tabs-height) / 2);
}
&.fold-menu {
.popup-box {
transform: translate($app-menu-fold-width / 2, ($app-header-height + $app-tabs-height) / 2);
}
}
&.full-screen {
.popup-mask {
left: 0;
top: $app-tabs-height;
transition-delay: .1s;
}
.popup-box {
transform: translate(0, $app-tabs-height / 2);
}
}
}
}
}
.pop-enter-active, .pop-leave{
transform: scale(1);
opacity: 1;
}
.pop-enter, .pop-leave-active{
transform: scale(.5);
opacity: 0;
}
.mask-leave-active{
opacity: 0;
}
</style>
alert.vue
<template>
<BasePopup v-model="visible" ref="basePop" class="alert-popup" :message="message" :title="title"
:showClose="showClose" :width="width" :height="height" :scroll="scroll" :cover="cover"
:contentWrapperStyle="contentWrapperStyle"
:content="content" :contentProps="contentProps" :contentEvents="contentEvents"
@close="doClose">
<div slot="bottom" class="buttons">
<BaseButton @click="doOK" type="primary" :text="btnText"></BaseButton>
</div>
</BasePopup>
</template>
<script>
import BasePopup from './Popup.vue'
import BaseButton from '../../base/button/index.vue'
export default {
name: 'BaseAlert',
components: {
BasePopup,
BaseButton
},
props: {
// 是否显示弹框
value: {
type: Boolean,
default: false
},
// 标题
title: String,
// 是否显示关闭按钮
showClose: {
type: Boolean,
default: false
},
// 消息内容
message: String,
// 弹窗内容组件
content: Object,
// 弹窗内容组件的props
contentProps: Object,
// 弹窗内容组件的事件
contentEvents: Object,
// 内容包裹器样式
contentWrapperStyle: Object,
// 弹框宽度
width: {
type: Number,
default: 300
},
// 弹框高度
height: {
type: Number,
default: 220
},
// 内容区域是否需要滚动条
scroll: {
type: Boolean,
default: false
},
// 遮罩层覆盖范围(full: 覆盖整个页面, tab: 覆盖当前tab页功能区域)
cover: {
type: String,
default: 'full'
},
// 按钮文字
btnText: {
type: String,
default: '确定'
}
},
data () {
return {
// 是否显示
visible: this.value
}
},
watch: {
value (val) {
this.visible = val
}
},
methods: {
doClose () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('close')
},
doOK () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('ok')
}
}
}
</script>
<style lang="scss" scoped px2rem="false">
$content-padding: 24px;
$bottom-height: 54px;
$bottom-height: 54px;
.alert-popup {
.buttons{
padding: (($bottom-height - 32px) / 2) $content-padding;
}
::v-deep .popup-box {
.popup-pane {
.content-wrap {
bottom: $bottom-height;
}
.bottom {
border-top: solid 1px $border-color-light;
}
}
}
}
</style>
confirm.vue
<template>
<BasePopup v-model="visible" ref="basePop" class="confirm-popup" :message="message" :title="title"
:showClose="showClose" :width="width" :height="height" :scroll="scroll" :cover="cover"
:contentWrapperStyle="contentWrapperStyle"
:content="content" :contentProps="contentProps" :contentEvents="contentEvents"
@close="doClose">
<div slot="bottom" class="buttons">
<BaseButton :class="'cancel-btn'" @click="doCancel" :text="cancelBtnText"></BaseButton>
<BaseButton :class="'ok-btn'" @click="doOK" type="primary" :text="okBtnText"></BaseButton>
</div>
</BasePopup>
</template>
<script>
import BasePopup from './Popup.vue'
import BaseButton from '../../base/button/index.vue'
export default {
name: 'BaseConfirm',
components: {
BasePopup,
BaseButton
},
props: {
// 是否显示弹框
value: {
type: Boolean,
default: false
},
// 标题
title: String,
// 是否显示关闭按钮
showClose: {
type: Boolean,
default: false
},
// 消息内容
message: String,
// 弹窗内容组件
content: Object,
// 弹窗内容组件的props
contentProps: Object,
// 弹窗内容组件的事件
contentEvents: Object,
// 内容包裹器样式
contentWrapperStyle: Object,
// 弹框宽度
width: {
type: Number,
default: 300
},
// 弹框高度
height: {
type: Number,
default: 220
},
// 内容区域是否需要滚动条
scroll: {
type: Boolean,
default: false
},
// 遮罩层覆盖范围(full: 覆盖整个页面, tab: 覆盖当前tab页功能区域)
cover: {
type: String,
default: 'full'
},
// 确定按钮文字
okBtnText: {
type: String,
default: '确定'
},
// 取消按钮文字
cancelBtnText: {
type: String,
default: '取消'
}
},
data () {
return {
// 是否显示
visible: this.value
}
},
watch: {
value (val) {
this.visible = val
}
},
methods: {
doClose () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('close')
},
doOK () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('ok')
},
doCancel () {
this.visible = false
this.$emit('input', this.visible)
this.$emit('cancel')
}
}
}
</script>
<style lang="scss" scoped px2rem="false">
$content-padding: 24px;
$bottom-height: 54px;
$bottom-height: 54px;
.confirm-popup {
.buttons{
padding: (($bottom-height - 32px) / 2) $content-padding;
.cancel-btn {
background-color: #FFF;
border-color: $border-color-light;
}
::v-deep .btn:not(:last-of-type) {
margin-right: 10px;
}
}
::v-deep .popup-box {
.popup-pane {
.content-wrap {
bottom: $bottom-height;
}
.bottom {
border-top: solid 1px $border-color-light;
}
}
}
}
</style>
popup/index.js
/**
* 弹出窗口组件
* @author qinglianshizhe
*/
import Vue from 'vue'
import Popup from './Popup.vue'
import Alert from './Alert.vue'
import Confirm from './Confirm.vue'
// Popup构造函数
const PopupConstructor = Vue.extend({
extends: Popup
})
// Alert构造函数
const AlertConstructor = Vue.extend({
extends: Alert
})
// Confirm构造函数
const ConfirmConstructor = Vue.extend({
extends: Confirm
})
// 获取tab页弹窗的父节点
function getTabPopupParentElement () {
let tabContentElement = window.document.getElementById('tabsViewContent')
if (tabContentElement) {
let tabArray = tabContentElement.childNodes
if (tabArray.length > 0) {
return tabArray[0]
}
}
return document.body
}
function initInstance (instance, options) {
// 显示内容
instance.message = typeof options === 'string' ? options : options.message
// html内容
instance.html = typeof options === 'object' ? options.html : null
// 弹窗内容组件
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.width = typeof options.width === 'number' ? options.width : 300
// 窗口高度
instance.height = typeof options.height === 'number' ? options.height : 220
// 自定义样式名
instance.className = options.className || ''
// 是否显示滚动条
instance.scroll = options.scroll
// 遮罩层覆盖范围(full: 覆盖整个页面, tab: 覆盖当前tab页功能区域,只显示tab页时可用)
instance.cover = options.cover || 'full'
// 父节点
let parentElement = options.parentElement || (options.cover === 'tab' ? getTabPopupParentElement() : 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 popup (options = {}) {
let instance = new PopupConstructor({
el: document.createElement('div'),
store: options.store || (this && this.$store),
router: options.router || (this && this.$router)
})
initInstance(instance, options)
// 弹窗标题
instance.title = options.title || '提示'
// 是否显示关闭按钮
instance.showClose = typeof options.showClose === 'boolean' ? options.showClose : true
return instance
}
// 显示提示框(底部有确定按钮)
export function popupAlert (options = {}) {
let instance = new AlertConstructor({
el: document.createElement('div'),
store: options.store || (this && this.$store),
router: options.router || (this && this.$router)
})
initInstance(instance, options)
// 弹窗标题
instance.title = options.title || ''
// 是否显示关闭按钮
instance.showClose = typeof options.showClose === 'boolean' ? options.showClose : false
// 按钮文字
instance.btnText = options.btnText || '确定'
// 点击确定按钮时调用options.onOK方法
instance.$on('ok', () => {
options.onOK && options.onOK()
})
instance.$on('input', visible => {})
return instance
}
export function popupConfirm (options = {}) {
let instance = new ConfirmConstructor({
el: document.createElement('div'),
store: options.store || (this && this.$store),
router: options.router || (this && this.$router)
})
initInstance(instance, options)
// 弹窗标题
instance.title = options.title || ''
// 是否显示关闭按钮
instance.showClose = typeof options.showClose === 'boolean' ? options.showClose : false
// 确定按钮文字
instance.okBtnText = options.okBtnText || '确定'
// 取消按钮文字
instance.cancelBtnText = options.cancelBtnText || '取消'
// 点击确定按钮时调用options.onOK方法
instance.$on('ok', visible => {
options.onOK && options.onOK()
})
// 点击确定按钮时调用options.onOK方法
instance.$on('cancel', visible => {
options.onCancel && options.onCancel()
})
return instance
}
export default popup
感谢评论区大佬的点拨。