| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- <template>
- <l-overlay
- :visible="innerValue"
- :zIndex="overlayZIndex"
- :appear="true"
- :preventScrollThrough="preventScrollThrough"
- :l-style="overlayStyle"
- @click="handleOverlayClick"
- v-if="destroyOnClose ? display && overlay : overlay">
- </l-overlay>
- <view class="l-popup"
- ref="popupRef"
- v-if="destroyOnClose ? display : inited"
- :class="rootClass"
- :style="[styles, lStyle]"
- @transitionend="finished">
- <slot></slot>
- <view class="l-popup__close" v-if="closeable" @click="handleClose">
- <slot name="close-btn">
- <l-icon class="l-popup__close-icon" :name="closeIcon" size="27px" :color="iconColor" />
- </slot>
- </view>
- </view>
- </template>
- <script lang="uts" setup>
- /**
- * Popup 弹出层组件
- * @description 提供多种位置的弹窗展示能力,支持自定义内容和动画效果
- * <br>插件类型:LPopupComponentPublicInstance
- * @tutorial https://ext.dcloud.net.cn/plugin?name=lime-popup
- *
- * @property {boolean} closeable 显示关闭按钮(默认:false)
- * @property {boolean} closeOnClickOverlay 点击遮罩关闭(默认:true)
- * @property {boolean} destroyOnClose 关闭时销毁内容(默认:false)
- * @property {string} overlayStyle 遮罩层样式(支持CSS字符串)
- * @property {'top' | 'left' | 'right' | 'bottom' | 'center' | ''} position 弹出位置
- * @value top 从顶部滑入
- * @value bottom 从底部滑入
- * @value left 从左侧滑入
- * @value right 从右侧滑入
- * @value center 居中显示(默认)
- * @property {boolean} preventScrollThrough 阻止滚动穿透(默认:true)
- * @property {boolean} overlay 显示遮罩层(默认:true)
- * @property {string} transitionName 自定义动画名称(配合transition使用)
- * @property {string|number|Array} radius 圆角 可以是字符,数值,数组
- * @property {boolean} visible 控制显示/隐藏(支持v-model)
- * @property {number} zIndex 组件层级(默认:999)
- * @property {number} duration 动画时长(单位ms,默认:300)
- * @property {string} bgColor 内容区域背景色(默认:#ffffff)
- * @property {string} closeIcon 关闭图标名称/路径(默认:'close')
- * @property {string} iconColor 关闭图标颜色(默认:#333)
- * @property {string} lStyle 自定义内容区样式(支持CSS字符串)
- * @property {boolean} safeAreaInsetBottom 适配底部安全区域(默认:true)
- * @property {boolean} safeAreaInsetTop 适配顶部安全区域(默认:true)
- * @event {Function} close 关闭时触发(返回触发来源:'close-btn' | 'overlay')
- * @event {Function} change 切换时触发
- * @event {Function} click-overlay 点击遮罩触发
- * @event {Function} click-close 点击关闭触发
- * @event {Function} open 打开触发
- * @event {Function} opened 打开完成触发
- * @event {Function} closed 关闭完成触发
- * @event {Function} before-enter
- * @event {Function} enter
- * @event {Function} after-enter
- * @event {Function} before-leave
- * @event {Function} leave
- * @event {Function} after-leave
- */
- import { useTransition, type UseTransitionOptions, type TransitionEmitStatus } from '@/uni_modules/lime-transition';
- import { convertRadius } from './utils'
- import { PopupProps } from './type';
-
- const emit = defineEmits(['change', 'click-overlay', 'click-close', 'open', 'opened','close','closed', 'before-enter', 'enter', 'after-enter', 'before-leave', 'leave', 'after-leave'])
- const props = withDefaults(defineProps<PopupProps>(),{
- closeable: false,
- overlay: true,
- closeOnClickOverlay: true,
- preventScrollThrough: true,
- destroyOnClose: false,
- safeAreaInsetBottom: true,
- safeAreaInsetTop: false,
- position: 'center',
- zIndex: 999,
- duration: 300,
- closeIcon: 'close'
- })
-
- const modelValue = defineModel({type: Boolean});
- const innerValue = computed({
- set(value: boolean) {
- modelValue.value = value;
- emit('change', value)
- },
- get():boolean {
- // #ifdef APP-ANDROID
- return (props.visible ?? false) || modelValue.value// ?? false
- // #endif
- // #ifndef APP-ANDROID
- return props.visible || modelValue.value
- // #endif
- }
- } as WritableComputedOptions<boolean>)
- // const hosKey = ref(0)
- const status = ref<TransitionEmitStatus>('before-enter')
- const {inited, display, classes, finished} = useTransition({
- defaultName: props.transitionName ?? 'popup-fade',
- appear: innerValue.value,
- emits: (name:TransitionEmitStatus) => {
- status.value = name
- if(name == 'before-enter') {
- emit('open')
-
- } else if(name == 'after-enter') {
- emit('opened')
- // setTimeout(()=>{
- // if(hosKey.value > 0) return
- // hosKey.value++
- // },5)
- } else if(name == 'before-leave') {
- emit('close')
- } else if(name == 'after-leave') {
- emit('closed')
- }
- emit(name)
- },
- visible: (): boolean => innerValue.value,
- duration: props.duration,
- } as UseTransitionOptions)
-
- const overlayZIndex = computed(():number => props.zIndex > 0 ? props.zIndex - 1: 998);
-
- const rootClass = computed(():string=>{
- const safe = props.safeAreaInsetTop && props.position == 'top'
- ? 'l-popup--safe-top'
- : props.safeAreaInsetBottom && props.position == 'bottom'
- ? 'l-popup--safe-bottom'
- : ''
-
- return `l-popup--${props.position} ${safe} ${classes.value}`
- })
-
- const {safeAreaInsets} = uni.getWindowInfo()
-
- const styles = computed<Map<string,any>>(():Map<string,any> => {
- const style = new Map<string,any>();
- // #ifdef UNI-APP-X && APP
- style.set('transition-duration', (['after-leave', 'before-enter'].includes(status.value) ? 0 : props.duration) + 'ms')
- // #endif
- // #ifndef UNI-APP-X && APP
- style.set('transition-duration', props.duration + 'ms')
- // #endif
- if (props.bgColor != null) {
- style.set("background", props.bgColor!)
- }
- if (props.zIndex > 0) {
- style.set("z-index", props.zIndex)
- }
-
- // if(props.safeAreaInsetBottom && props.position == 'bottom') {
- // style.set('padding-bottom', safeAreaInsets.bottom + 'px')
- // }
- // if(props.safeAreaInsetTop && props.position == 'top') {
- // style.set('padding-top', safeAreaInsets.top + 'px')
- // }
-
- if(props.radius != null) {
- const values = convertRadius(props.radius!)
- style.set('border-top-left-radius', values[0])
- style.set('border-top-right-radius', values[1])
- style.set('border-bottom-right-radius', values[2])
- style.set('border-bottom-left-radius', values[3])
- }
-
- // #ifndef APP
- if (!display.value) {
- // 会导致picker触发pick
- // style.set("display", "none")
- style.set("pointer-events", "none")
- }
- // #endif
-
- return style
- })
- const handleOverlayClick = () => {
- if(props.closeOnClickOverlay) {
- innerValue.value = false;
- emit('click-overlay')
- }
- }
- const handleClose = () => {
- innerValue.value = false;
- emit('click-close')
- }
-
- // #ifdef APP
- const popupRef = ref<UniElement|null>(null)
-
- watchEffect(()=>{
- // 会导致picker触发pick
- if(!display.value) {
- // popupRef.value?.style.setProperty('display', 'none')
- popupRef.value?.style.setProperty('pointer-events', 'none')
- popupRef.value?.style.setProperty('z-index', -10000)
- } else {
- // popupRef.value?.style.setProperty('display', 'flex')
- popupRef.value?.style.setProperty('pointer-events', 'auto')
- popupRef.value?.style.setProperty('z-index', props.zIndex)
- }
- })
-
- // #endif
- </script>
- <style lang="scss">
- @import "./index-u";
- </style>
|