274 lines
4.5 KiB
Vue
274 lines
4.5 KiB
Vue
<template>
|
||
<view
|
||
class="cl-countdown"
|
||
:style="{
|
||
'font-size': parseRpx(fontSize),
|
||
}"
|
||
v-if="visible"
|
||
>
|
||
<template v-for="(item, index) in list" :key="index">
|
||
<view class="cl-countdown__item" :style="item.ch ? textStyle : numberStyle">
|
||
{{ item.value }}
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts">
|
||
import { defineComponent, ref, watch, nextTick, onBeforeUnmount, onBeforeMount } from "vue";
|
||
import { parseRpx } from "/@/cool/utils";
|
||
|
||
export default defineComponent({
|
||
name: "cl-countdown",
|
||
|
||
props: {
|
||
// 格式化
|
||
format: {
|
||
type: String,
|
||
default: "{h}:{m}:{s}",
|
||
},
|
||
// 是否精简:为00时自动隐藏
|
||
simple: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
// 还有多少天
|
||
day: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
// 还有多少小时
|
||
hour: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
// 还有多少分钟
|
||
minute: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
// 还有多少秒
|
||
second: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
// 结束时间
|
||
datetime: [Date, String],
|
||
// 数字样式
|
||
numberStyle: Object,
|
||
// 分隔符样式
|
||
textStyle: Object,
|
||
// 字体大小
|
||
fontSize: {
|
||
type: Number,
|
||
default: 26,
|
||
},
|
||
},
|
||
|
||
emits: ["stop", "done", "change"],
|
||
|
||
setup(props, { emit }) {
|
||
let timer: any = null;
|
||
|
||
// 秒
|
||
const seconds = ref(0);
|
||
|
||
// 状态
|
||
let status = false;
|
||
|
||
// 是否显示
|
||
const visible = ref(true);
|
||
|
||
// 列表
|
||
const list = ref<any[]>([]);
|
||
|
||
// 转成秒
|
||
function toSeconds({ day = 0, hour = 0, minute = 0, second = 0, datetime }: any) {
|
||
if (datetime) {
|
||
return (
|
||
(new Date(datetime.replace(/-/g, "/")).getTime() - new Date().getTime()) / 1000
|
||
);
|
||
} else {
|
||
return day * 60 * 60 * 24 + hour * 60 * 60 + minute * 60 + second;
|
||
}
|
||
}
|
||
|
||
// 开始倒计时
|
||
function start(options?: any) {
|
||
visible.value = true;
|
||
|
||
nextTick(() => {
|
||
let { day, hour, minute, second, datetime } = options || {};
|
||
|
||
if (!day) {
|
||
day = props.day;
|
||
}
|
||
|
||
if (!hour) {
|
||
hour = props.hour;
|
||
}
|
||
|
||
if (!minute) {
|
||
minute = props.minute;
|
||
}
|
||
|
||
if (!second) {
|
||
second = props.second;
|
||
}
|
||
|
||
if (!datetime) {
|
||
datetime = props.datetime;
|
||
}
|
||
|
||
seconds.value = toSeconds({
|
||
day,
|
||
hour,
|
||
minute,
|
||
second,
|
||
datetime,
|
||
});
|
||
|
||
next();
|
||
});
|
||
}
|
||
|
||
// 继续倒计时
|
||
function next() {
|
||
if (seconds.value <= 0) {
|
||
return;
|
||
}
|
||
|
||
if (status) {
|
||
return;
|
||
}
|
||
|
||
status = true;
|
||
|
||
// 开始
|
||
function next() {
|
||
countDown();
|
||
|
||
if (seconds.value <= 0) {
|
||
done();
|
||
return;
|
||
} else {
|
||
seconds.value--;
|
||
timer = setTimeout(next, 1000);
|
||
}
|
||
}
|
||
|
||
next();
|
||
}
|
||
|
||
// 停止
|
||
function stop() {
|
||
clear();
|
||
emit("stop");
|
||
}
|
||
|
||
// 结束
|
||
function done() {
|
||
visible.value = false;
|
||
clear();
|
||
emit("done");
|
||
}
|
||
|
||
// 清除定时器
|
||
function clear() {
|
||
clearTimeout(timer);
|
||
|
||
timer = null;
|
||
status = false;
|
||
}
|
||
|
||
// 倒计时执行
|
||
function countDown() {
|
||
let [day, hour, minute, second]: any = [0, 0, 0, 0];
|
||
|
||
day = Math.floor(seconds.value / (60 * 60 * 24));
|
||
hour = Math.floor(seconds.value / (60 * 60)) - day * 24;
|
||
minute = Math.floor(seconds.value / 60) - day * 24 * 60 - hour * 60;
|
||
second = Math.floor(seconds.value) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
|
||
|
||
if (day < 10) {
|
||
day = "0" + day;
|
||
}
|
||
if (hour < 10) {
|
||
hour = "0" + hour;
|
||
}
|
||
if (minute < 10) {
|
||
minute = "0" + minute;
|
||
}
|
||
if (second < 10) {
|
||
second = "0" + second;
|
||
}
|
||
|
||
const d: any = {
|
||
d: day,
|
||
h: hour,
|
||
m: minute,
|
||
s: second,
|
||
};
|
||
|
||
let arr: any[] = props.format
|
||
.split(/[{,}]/)
|
||
.filter(Boolean)
|
||
.map((e) => {
|
||
return {
|
||
value: d[e] ? d[e] : e,
|
||
ch: d[e] === undefined,
|
||
};
|
||
});
|
||
|
||
let f = true;
|
||
let n = -1;
|
||
|
||
list.value = arr
|
||
.map((e, i) => {
|
||
if (props.simple) {
|
||
if (!e.ch) {
|
||
if (f) {
|
||
if (e.value == "00") {
|
||
n = i;
|
||
f = true;
|
||
} else {
|
||
f = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return e;
|
||
})
|
||
.filter((_, i) => {
|
||
return n == -1 ? true : n + 1 < i;
|
||
});
|
||
|
||
emit(
|
||
"change",
|
||
list.value.map((e) => e.value),
|
||
);
|
||
}
|
||
|
||
onBeforeUnmount(clear);
|
||
onBeforeMount(start);
|
||
|
||
watch(() => props.day, start);
|
||
watch(() => props.hour, start);
|
||
watch(() => props.minute, start);
|
||
watch(() => props.second, start);
|
||
watch(() => props.datetime, start);
|
||
|
||
return {
|
||
visible,
|
||
seconds,
|
||
list,
|
||
start,
|
||
stop,
|
||
toSeconds,
|
||
parseRpx,
|
||
};
|
||
},
|
||
});
|
||
</script>
|