Popup 代码
PkMask组件在其他章节中
HTML
<template>
<view>
<PkMask v-if="props.mask"
:visible="props.visible"
:closeOnClickMask="props.closeOnClickMask"
:zIndex="props.zIndex"
:lockScroll="props.lockScroll"
:duration="props.duration"
@click="onClickMask"
/>
<transition :name="transitionName"
@after-enter="onOpened"
@after-leave="onClosed">
<view v-show="props.visible"
:class="classes"
:style="popStyle"
@click="onClick">
<slot v-if="state.showSlot"></slot>
<view v-if="state.closed"
@click="onClickCloseIcon"
class="pk-popup__close-icon"
:class="'pk-popup__close-icon--' + closeIconPosition">
<slot name="close-icon">
<view class="close"></view>
</slot>
</view>
</view>
</transition>
</view>
</template>
script
<script lang="ts" setup>
import PkMask from '@/components/pk-mask/index.vue'
// import type { Props } from './interface'
import { computed, ComputedRef, watchEffect, reactive, watch } from 'vue'
interface Props {
visible: Boolean;
zIndex?: Number;
duration?: Number;
lockScroll?: Boolean;
mask?: Boolean;
closeOnClickMask?: Boolean;
style?: Object;
position?: 'center' | 'top' | 'bottom' | 'left' | 'right';
transition?: String;
closeable?: Boolean;
closeIconPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
destroyOnClose?: Boolean;
round?: Boolean;
safeAreaInsetBottom: Boolean;
}
const props = withDefaults(defineProps<Props>(), {
visible:() => false, // 控制当前组件显示/隐藏
zIndex: () => 99, // 遮罩层级
duration: () => 300, // 组件显示/隐藏的动画时长,单位毫秒
lockScroll: () => true, // 背景是否锁定
mask: () => true, // 是否显示遮罩
closeOnClickMask: () => true, // 是否点击遮罩关闭
position: () => 'center', // 弹出位置
transition: () => '', // 动画名
style:() => '', // 自定义弹框样式
closeable: () => false, // 是否显示关闭按钮
closeIconPosition: () => 'top-right', // 关闭按钮位置
destroyOnClose: () => true, // 弹层关闭后 slot内容会不会清空
round: () => false, // 是否显示圆角
safeAreaInsetBottom:() => false //是否开启 iphone 系列全面屏底部安全区适配,仅当 position 为 bottom 时有效
})
const initIndex = 2000;
let _zIndex = initIndex;
const emits = defineEmits<{
(e:'click-pop', value:any): void;
(e:'click-close-icon', value:any): void;
(e:'open'): void;
(e:'close'): void;
(e:'opend', value:any): void;
(e:'closed', value:any): void;
(e:'click-mask', value:any): void;
(e:'update:visible', value:any): void;
}>()
const state = reactive({
zIndex: props.zIndex,
showSlot: true,
closed: props.closeable
})
const classes = computed(() => {
const prefixCls = 'pk-popup'
return {
[prefixCls]: true,
['round']: props.round,
[`pk-popup--${props.position}`]: true,
[`pk-popup--${props.position}--safebottom`]: props.position === 'bottom' && props.safeAreaInsetBottom
}
})
const popStyle: ComputedRef = computed(() => {
return {
zIndex: state.zIndex,
transitionDuration: `${props.duration}ms`,
...props.style
};
})
const transitionName: ComputedRef = computed(() => {
return props.transition ? props.transition : `pk-popup-slide-${props.position}`;
})
const open = () => {
if (props.zIndex !== initIndex) {
_zIndex = Number(props.zIndex);
}
emits('update:visible', true);
state.zIndex = ++_zIndex;
if (props.destroyOnClose) {
state.showSlot = true;
}
emits('open');
}
const close = () => {
emits('update:visible', false);
emits('close');
if (props.destroyOnClose) {
setTimeout(() => {
state.showSlot = false;
}, +props.duration);
}
}
const onClick = (e: Event) => {
emits('click-pop', e);
};
const onClickCloseIcon = (e: Event) => {
e.stopPropagation();
emits('click-close-icon', e);
emits('update:visible', false);
// close();
};
const onClickMask = (e: Event) => {
if (props.closeOnClickMask) {
emits('click-mask', e);
emits('update:visible', false);
// close();
}
};
const onOpened = (e: Event) => {
emits('opend', e);
};
const onClosed = (e: Event) => {
emits('closed', e);
};
watch(
() => props.visible,
(val) => {
props.visible ? open() : close();
}
);
watchEffect(() => {
// props.visible ? open() : close();
state.closed = props.closeable;
});
</script>
style
<style lang="scss" scoped>
.pk-popup {
position: fixed;
max-height: 100%;
overflow-y: auto;
background: #fff;
&__close-icon {
color: #969799;
}
}
.pk-popup-slide {
&-center-enter-active,
&-center-leave-active {
transition-property: opacity;
transition-timing-function: ease;
}
&-center-enter-from,
&-center-leave-to {
opacity: 0;
}
&-top-enter-from,
&-top-leave-active {
transform: translate(0, -100%);
}
&-right-enter-from,
&-right-leave-active {
transform: translate(100%, 0);
}
&-bottom-enter-from,
&-bottom-leave-active {
transform: translate(0, 100%);
}
&-left-enter-from,
&-left-leave-active {
transform: translate(-100%, 0);
}
}
.pk-popup--center {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&.round {
border-radius: 20rpx 20rpx 0 0;
}
}
.pk-popup--bottom {
bottom: 0;
left: 0;
width: 100%;
&.round {
border-radius: 20rpx 20rpx 0 0;
}
&--safebottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}
.pk-popup--right {
top: 0;
right: 0;
&.round {
border-radius: 20rpx 0 0 20rpx;
}
}
.pk-popup--left {
top: 0;
left: 0;
&.round {
border-radius: 0 20rpx 20rpx 0;
}
}
.pk-popup--top {
top: 0;
left: 0;
width: 100%;
&.round {
border-radius: 0 0 20rpx 20rpx;
}
}
.pk-popup {
position: fixed;
max-height: 100%;
overflow-y: auto;
background-color: #fff;
-webkit-overflow-scrolling: touch;
&__close-icon {
position: absolute !important;
z-index: 1;
color: #969799;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
&:active {
opacity: 0.7;
}
&--top-left {
top: 32rpx;
left: 32rpx;
}
&--top-right {
top: 32rpx;
right: 32rpx;
}
&--bottom-left {
bottom: 32rpx;
left: 32rpx;
}
&--bottom-right {
right: 32rpx;
bottom: 32rpx;
}
}
}
.close {
position: realtive;
}
.close::before,
.close::after {
content: '';
position: absolute;
background-color: #969799;
left: 18rpx;
width: 2rpx;
height: 36rpx;
top: 0rpx;
}
.close::before {
transform: rotate(45deg);
}
.close::after {
transform: rotate(-45deg);
}
</style>
Popup弹出层使用文档
引用
import pkPopup from '@/components/pk-popup/index.vue';
基础用法
<template>
<view @click="visible = true">开关</view>
<pk-popup :style="{ padding: '30px 50px' }" v-model:visible="visible">正文</pk-popup>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const visible = ref(false)
</script>
弹出位置
💡 Tips:通过设置 position 的值来控制弹出位置
<template>
<veiw @click="showTop = true">顶部弹出</veiw>
<pk-popup position="top" :style="{ height: '20%' }" v-model:visible="showTop"></pk-popup>
<veiw @click="showBottom = true">底部弹出</veiw>
<pk-popup position="bottom" :style="{ height: '20%' }" v-model:visible="showBottom"></pk-popup>
<veiw @click="showLeft = true">左侧弹出</veiw>
<pk-popup position="left" :style="{ width: '20%', height: '100%' }" v-model:visible="showLeft"></pk-popup>
<veiw @click="showRight = true">右侧弹出</veiw>
<pk-popup position="right" :style="{ width: '20%', height: '100%' }" v-model:visible="showRight"></pk-popup>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const showTop = ref(false)
const showBottom = ref(false)
const showLeft = ref(false)
const showRight = ref(false)
</script>
图标
💡 Tips:通过 closeable 控制图标是否可关闭,close-icon-position 来设置图标的位置,close-icon 来自定义显示图标
<view @click="showIcon = true">关闭图标</view>
<pk-popup position="bottom" closeable :style="{ height: '20%' }" v-model:visible="showIcon"></pk-popup>
<view @click="showIconPosition = true" >图标位置</view>
<pk-popup position="bottom" closeable close-icon-position="top-left" :style="{ height: '20%' }" v-model:visible="showIconPosition" ></pk-popup>
<view @click="showCloseIcon = true" >自定义图标</view>
<pk-popup position="bottom" closeable close-icon-position="top-left" :style="{ height: '20%' }" v-model:visible="showCloseIcon">
<template #close-icon>
<Heart></Heart>
</template>
</pk-popup>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { Heart } from '@/icon/icons-vue';
const showIcon = ref(false)
const showIconPosition = ref(false)
const showCloseIcon = ref(false)
</script>
圆角弹框
💡 Tips:通过设置 round 来控制是否显示圆角
<template>
<view @click="showRound = true">圆角弹框</view>
<pk-popup position="bottom" closeable round :style="{ height: '30%' }" v-model:visible="showRound"></pk-popup>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const showRound = ref(false)
</script>
API
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| v-model:visible | 控制当前组件显示/隐藏 | boolean | false |
| zIndex | 遮罩层级 | number | 99 |
| duration | 组件显示/隐藏的动画时长,单位毫秒 | number | 300 |
| lockScroll | 背景是否锁定 | boolean | true |
| mask | 是否显示遮罩 | boolean | true |
| closeOnClickMask | 是否点击遮罩关闭 | boolean | true |
| position | 弹出位置(top,bottom,left,right,center) | string | center |
| transition | 动画名 | string | |
| style | 自定义弹框样式 | CSSProperties | |
| closeable | 是否显示关闭按钮 | boolean | false |
| closeIconPosition | 关闭按钮位置(top-left,top-right,bottom-left,bottom-right) | string | true |
| destroyOnClose | 弹层关闭后 slot内容会不会清空 | 弹层关闭后 slot内容会不会清空 | true |
| round | 是否显示圆角 | boolean | false |
| safeAreaInsetBottom | 是否开启 iphone 系列全面屏底部安全区适配,仅当 position 为 bottom 时有效 | boolean | false |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| click-pop | 点击弹出层时触发 | event:Event |
| click-close-icon | 点击关闭图标时触发 | event:Event |
| open | 打开弹框时触发 | |
| close | 关闭弹框时触发 | |
| opend | 遮罩打开动画结束时触发 | event:Event |
| closed | 遮罩关闭动画结束时触发 | event:Event |
| click-mask | 点击遮罩触发 | event:Event |
Slots
| 名称 | 说明 |
|---|---|
| default | 自定义内嵌内容 |
| close-icon | 关闭按钮的自定义图标 |