From d5048a33e4760d414115a5e25645d30b41d24198 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 29 八月 2026 18:06:07 +0800
Subject: [PATCH] fix(wls): 修复多个组件中的类型安全和数据验证问题

---
 src/views/mes/pro/feedback/modules/wait-countdown.vue |  137 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 137 insertions(+), 0 deletions(-)

diff --git a/src/views/mes/pro/feedback/modules/wait-countdown.vue b/src/views/mes/pro/feedback/modules/wait-countdown.vue
new file mode 100644
index 0000000..760b8de
--- /dev/null
+++ b/src/views/mes/pro/feedback/modules/wait-countdown.vue
@@ -0,0 +1,137 @@
+<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>

--
Gitblit v1.9.3