2026-09-21 15:04:59 +08:00
|
|
|
<template>
|
|
|
|
|
<view class="code-field"><input class="code-input" :value="modelValue" :disabled="disabled" type="number" maxlength="6" placeholder="请输入 6 位验证码" @input="emit('update:modelValue', $event.detail.value)" /><button class="code-button" :disabled="disabled || sending || countdown > 0" @click="send">{{ sending ? '发送中…' : (countdown > 0 ? countdown + 's 后重试' : '获取验证码') }}</button></view>
|
|
|
|
|
</template>
|
|
|
|
|
<script setup lang="uts">
|
|
|
|
|
import { requestSecurityCode } from '@/packages/account/services/account.uts'
|
2026-10-07 10:01:05 +08:00
|
|
|
import { getSession } from '@/services/api.uts'
|
|
|
|
|
const props = defineProps({ modelValue: { type: String, default: '' }, purpose: { type: String, default: 'password' }, phone: { type: String, default: '' }, disabled: { type: Boolean, default: false }, active: { type: Boolean, default: true }, ownerToken: { type: String, default: '' } })
|
2026-09-21 15:04:59 +08:00
|
|
|
const emit = defineEmits(['update:modelValue', 'error', 'sent'])
|
|
|
|
|
const sending = ref(false); const countdown = ref(0)
|
|
|
|
|
let timer = 0; let version = 0; let expiresAt = 0
|
|
|
|
|
const reset = () => { version++; clearInterval(timer); countdown.value = 0; sending.value = false }
|
|
|
|
|
const targetPhone = computed((): string => props.phone)
|
|
|
|
|
watch(targetPhone, () => { reset(); emit('update:modelValue', '') })
|
2026-10-07 10:01:05 +08:00
|
|
|
watch((): boolean => props.active, () => reset())
|
|
|
|
|
watch((): string => props.ownerToken, () => { reset(); emit('update:modelValue', '') })
|
2026-09-21 15:04:59 +08:00
|
|
|
const send = async () => {
|
2026-10-07 10:01:05 +08:00
|
|
|
if (!props.active || props.disabled || sending.value || countdown.value > 0) return
|
|
|
|
|
const token = props.ownerToken
|
|
|
|
|
if (token.length == 0 || getSession()?.getString('token') != token) { emit('error', '登录状态已变化,请重新进入页面'); return }
|
2026-09-21 15:04:59 +08:00
|
|
|
const current = ++version; sending.value = true
|
2026-10-07 10:01:05 +08:00
|
|
|
const valid = (): boolean => current == version && props.active && props.ownerToken == token && getSession()?.getString('token') == token
|
2026-09-21 15:04:59 +08:00
|
|
|
try {
|
|
|
|
|
await requestSecurityCode(props.purpose, props.phone)
|
2026-10-07 10:01:05 +08:00
|
|
|
if (!valid()) return
|
2026-09-21 15:04:59 +08:00
|
|
|
expiresAt = Date.now() + 60000; countdown.value = 60
|
|
|
|
|
timer = setInterval(() => { countdown.value = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1000)); if (countdown.value == 0) clearInterval(timer) }, 1000)
|
|
|
|
|
emit('sent'); uni.showToast({ title: '验证码已发送', icon: 'none' })
|
2026-10-07 10:01:05 +08:00
|
|
|
} catch (error) { if (valid()) emit('error', error instanceof Error ? error.message : '发送失败,请稍后重试') }
|
2026-09-21 15:04:59 +08:00
|
|
|
finally { if (current == version) sending.value = false }
|
|
|
|
|
}
|
|
|
|
|
onUnmounted(() => reset())
|
|
|
|
|
</script>
|
|
|
|
|
<style>
|
|
|
|
|
.code-field { flex-direction: row; align-items: center; height: 96rpx; border-bottom: 1rpx solid #edf1f6; }
|
|
|
|
|
.code-input { flex: 1; min-width: 0; font-size: 27rpx; color: #23344e; }
|
|
|
|
|
.code-button { flex-shrink: 0; padding: 0 12rpx; margin: 0 0 0 12rpx; height: 72rpx; line-height: 72rpx; font-size: 24rpx; color: #0868f7; background-color: transparent; }
|
|
|
|
|
</style>
|