<script lang="ts" setup>
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
import dayjs from 'dayjs';
|
|
import { Button, message } from 'ant-design-vue';
|
|
import { startWaitCountdown } from '#/api/mes/pro/task';
|
|
defineOptions({ name: 'WaitCountdown' });
|
|
interface Props {
|
taskId?: number;
|
/** 等待时长(小时),>0 才展示倒计时 */
|
waitDuration?: number;
|
/** 倒计时开始时间(yyyy-MM-dd HH:mm:ss) */
|
waitStartTime?: string;
|
/** 只读模式(详情/审批)下不展示开始按钮 */
|
disabled?: boolean;
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
taskId: undefined,
|
waitDuration: undefined,
|
waitStartTime: undefined,
|
disabled: false,
|
});
|
|
const starting = ref(false);
|
const startTime = ref<string | undefined>(props.waitStartTime);
|
const now = ref(Date.now());
|
let timer: ReturnType<typeof setInterval> | undefined;
|
|
const showCountdown = computed(
|
() => !!props.waitDuration && props.waitDuration > 0,
|
);
|
|
const endTime = computed(() => {
|
if (!startTime.value || !showCountdown.value) {
|
return undefined;
|
}
|
return dayjs(startTime.value, 'YYYY-MM-DD HH:mm:ss').valueOf() +
|
props.waitDuration! * 3600 * 1000;
|
});
|
|
const remainingMs = computed(() => {
|
if (!endTime.value) {
|
return 0;
|
}
|
return Math.max(0, endTime.value - now.value);
|
});
|
|
const counting = computed(() => remainingMs.value > 0);
|
|
const remainingText = computed(() => {
|
const totalSeconds = Math.ceil(remainingMs.value / 1000);
|
const hours = Math.floor(totalSeconds / 3600);
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
const seconds = totalSeconds % 60;
|
const pad = (n: number) => String(n).padStart(2, '0');
|
if (hours > 0) {
|
return `剩余 ${hours} 小时 ${pad(minutes)} 分 ${pad(seconds)} 秒`;
|
}
|
return `剩余 ${pad(minutes)} 分 ${pad(seconds)} 秒`;
|
});
|
|
function startTimer() {
|
if (timer) {
|
clearInterval(timer);
|
}
|
timer = setInterval(() => {
|
now.value = Date.now();
|
if (remainingMs.value <= 0) {
|
clearInterval(timer);
|
timer = undefined;
|
}
|
}, 1000);
|
}
|
|
watch(
|
() => props.waitStartTime,
|
(value) => {
|
startTime.value = value;
|
if (value) {
|
now.value = Date.now();
|
startTimer();
|
}
|
},
|
);
|
|
onMounted(() => {
|
if (startTime.value) {
|
startTimer();
|
}
|
});
|
|
onBeforeUnmount(() => {
|
if (timer) {
|
clearInterval(timer);
|
timer = undefined;
|
}
|
});
|
|
async function handleStart() {
|
if (!props.taskId) {
|
message.warning('请先选择任务');
|
return;
|
}
|
starting.value = true;
|
try {
|
const time = await startWaitCountdown(props.taskId);
|
startTime.value = time;
|
now.value = Date.now();
|
startTimer();
|
message.success('倒计时已开始');
|
} finally {
|
starting.value = false;
|
}
|
}
|
</script>
|
|
<template>
|
<div v-if="showCountdown" class="flex items-center gap-2">
|
<Button
|
v-if="!startTime && !disabled"
|
type="primary"
|
:loading="starting"
|
@click="handleStart"
|
>
|
开始倒计时
|
</Button>
|
<span v-if="startTime" class="text-sm">
|
<span v-if="counting" class="text-orange-500">{{ remainingText }}</span>
|
<span v-else class="text-green-600">倒计时已结束,可报工</span>
|
</span>
|
</div>
|
</template>
|