CNCEC_APP/uni_modules/uview-pro/components/u-loading-popup/u-loading-popup.vue

254 lines
5.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view @touchmove.stop.prevent>
<view v-if="popupValue" class="u-loading-init" :class="[direction]">
<u-loading :mode="mode" :color="color" :size="size" />
<view v-if="currentText" class="u-loading-tips">
{{ currentText }}
</view>
</view>
<view class="u-loading-mask" :class="[popupValue ? 'u-mask-show' : '']" @click="onMaskClick" />
</view>
</template>
<script lang="ts">
export default {
name: 'u-loading-popup',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script lang="ts" setup>
import { computed, onUnmounted, ref, watch } from 'vue';
import { LoadingPopupProps } from './types';
// 组件props类型
const props = defineProps(LoadingPopupProps);
const emit = defineEmits(['update:modelValue', 'cancel']);
// 自动关闭的持续时间定时器
let durationTimer: ReturnType<typeof setTimeout> | null = null;
// 关闭按钮倒计时定时器
let cancelTimer: ReturnType<typeof setTimeout> | null = null;
// 记录弹窗显示的时间戳
const now = ref(0);
// 点击遮罩层是否可关闭(超时后)
const canClose = ref(false);
// 当前显示的text优先级loading参数 > props.text
const currentText = ref(props.text);
const popupValue = computed({
get: () => props.modelValue,
set: (val: boolean) => emit('update:modelValue', val)
});
watch(
() => popupValue.value,
val => {
if (val) {
doOpen(currentText.value);
} else {
doClose();
}
}
);
// 响应 props 变更,自动刷新当前 text
watch(
() => props.text,
val => {
currentText.value = val;
}
);
// 响应 cancelTime/duration 变化,重启定时器
watch(
() => [props.cancelTime, props.duration],
() => {
if (popupValue.value) {
clearDurationTimer();
startCancelTime();
startDurationTime();
}
}
);
/**
* 启动超时关闭按钮计时
*/
function startCancelTime() {
clearCancelTimer();
canClose.value = false;
if (props.cancelTime > 0) {
cancelTimer = setTimeout(() => {
canClose.value = true;
}, props.cancelTime);
}
}
/**
* 启动持续时间计时
*/
function startDurationTime() {
clearDurationTimer();
if (props.duration) {
durationTimer = setTimeout(() => {
close();
}, props.duration);
}
}
/**
* 内部显示逻辑,初始化所有状态
*/
function doOpen(text?: string) {
canClose.value = false;
clearDurationTimer();
clearCancelTimer();
now.value = Date.now();
if (typeof text === 'string' && text !== '') {
currentText.value = text;
} else {
currentText.value = props.text;
}
startCancelTime();
startDurationTime();
}
/**
* 内部关闭逻辑,重置所有状态
*/
function doClose() {
canClose.value = false;
currentText.value = props.text;
clearDurationTimer();
clearCancelTimer();
}
/**
* 显示弹窗
* @param text 可选,优先显示的文案
*/
function open(text?: string) {
currentText.value = text || props.text;
popupValue.value = true;
}
/**
* 隐藏弹窗
*/
function close() {
popupValue.value = false;
}
// 清理定时器
function clearDurationTimer() {
if (durationTimer) {
clearTimeout(durationTimer);
durationTimer = null;
}
}
function clearCancelTimer() {
if (cancelTimer) {
clearTimeout(cancelTimer);
cancelTimer = null;
}
}
// 遮罩点击事件
function onMaskClick() {
// 只有显示关闭按钮时才允许关闭
if (canClose.value) {
emit('cancel');
close();
}
}
onUnmounted(() => {
clearDurationTimer();
clearCancelTimer();
});
defineExpose({
open,
close
});
</script>
<style lang="scss">
.u-loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0);
z-index: 9999996;
transition: all 0.3s ease-in-out;
opacity: 0;
visibility: hidden;
}
.u-mask-show {
visibility: visible;
opacity: 1;
}
.u-loading-init {
position: relative;
min-width: 200rpx;
min-height: 200rpx;
max-width: 500rpx;
padding: 15rpx 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
font-size: 30rpx;
color: var(--u-white-color);
background: rgba(0, 0, 0, 0.7);
border-radius: 7px;
.u-icon-close {
position: absolute;
top: 4rpx;
right: 2rpx;
color: var(--u-white-color);
opacity: 0.8;
}
&.horizontal {
flex-direction: row;
align-items: center;
justify-content: left;
width: 600rpx;
max-width: 600rpx;
min-height: 150rpx;
padding-left: 40rpx;
.u-loading-tips {
margin-top: 0;
margin-left: 20rpx;
font-size: 32rpx;
}
}
}
.u-loading-tips {
text-align: center;
padding: 0 20rpx;
box-sizing: border-box;
margin-top: 36rpx;
}
</style>