办学质量监测教学评价系统
shenrongliang
2025-06-13 11d86cc6c26bb4f709e407acadf4805c2024e79f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<script setup lang="ts">
import type { PinInputProps } from './types';
 
import { computed, onBeforeUnmount, ref, useId, watch } from 'vue';
 
import { PinInput, PinInputGroup, PinInputInput } from '../../ui';
import { VbenButton } from '../button';
 
defineOptions({
  inheritAttrs: false,
});
 
const {
  codeLength = 6,
  createText = async () => {},
  disabled = false,
  handleSendCode = async () => {},
  loading = false,
  maxTime = 60,
} = defineProps<PinInputProps>();
 
const emit = defineEmits<{
  complete: [];
  sendError: [error: any];
}>();
 
const timer = ref<ReturnType<typeof setTimeout>>();
 
const modelValue = defineModel<string>();
 
const inputValue = ref<string[]>([]);
const countdown = ref(0);
 
const btnText = computed(() => {
  const countdownValue = countdown.value;
  return createText?.(countdownValue);
});
 
const btnLoading = computed(() => {
  return loading || countdown.value > 0;
});
 
watch(
  () => modelValue.value,
  () => {
    inputValue.value = modelValue.value?.split('') ?? [];
  },
);
 
watch(inputValue, (val) => {
  modelValue.value = val.join('');
});
 
function handleComplete(e: string[]) {
  modelValue.value = e.join('');
  emit('complete');
}
 
async function handleSend(e: Event) {
  try {
    e?.preventDefault();
    await handleSendCode();
    countdown.value = maxTime;
    startCountdown();
  } catch (error) {
    console.error('Failed to send code:', error);
    // Consider emitting an error event or showing a notification
    emit('sendError', error);
  }
}
 
function startCountdown() {
  if (countdown.value > 0) {
    timer.value = setTimeout(() => {
      countdown.value--;
      startCountdown();
    }, 1000);
  }
}
 
onBeforeUnmount(() => {
  countdown.value = 0;
  clearTimeout(timer.value);
});
 
const id = useId();
</script>
 
<template>
  <PinInput
    :id="id"
    v-model="inputValue"
    :disabled="disabled"
    class="flex w-full justify-between"
    otp
    placeholder="○"
    type="number"
    @complete="handleComplete"
  >
    <div class="relative flex w-full">
      <PinInputGroup class="mr-2">
        <PinInputInput
          v-for="(item, index) in codeLength"
          :key="item"
          :index="index"
        />
      </PinInputGroup>
      <VbenButton
        :disabled="disabled"
        :loading="btnLoading"
        class="flex-grow"
        size="lg"
        variant="outline"
        @click="handleSend"
      >
        {{ btnText }}
      </VbenButton>
    </div>
  </PinInput>
</template>