automated_uniapp/components/sms-btn.vue
2025-01-09 16:16:11 +08:00

230 lines
4.0 KiB
Vue

<template>
<view class="sms-btn">
<slot :disabled="isDisabled" :countdown="countdown" :btnText="btnText">
<cl-button
:border="false"
background-color="transparent"
color="#FE6B03"
:height="height"
:font-size="fontSize"
fill
:size="size"
:disabled="isDisabled"
@tap="open"
>
{{ btnText }}
</cl-button>
</slot>
<cl-popup
v-model="captcha.visible"
:ref="setRefs('popup')"
:padding="40"
border-radius="24rpx"
>
<cl-loading-mask :loading="captcha.loading">
<view class="sms-popup">
<view class="head">
<cl-text bold :size="28" value="获取短信验证码"></cl-text>
<cl-icon :size="32" name="close" @tap="close"></cl-icon>
</view>
<view class="row">
<cl-input
type="number"
v-model="form.code"
placeholder="验证码"
:maxlength="4"
:height="70"
:clearable="false"
:focus="refs.popup?.isFocus"
:border="false"
background-color="#f7f7f7"
@confirm="send"
/>
<image :src="captcha.img" mode="aspectFit" @tap="getCaptcha" />
</view>
<cl-button
type="primary"
fill
:disabled="!form.code"
:loading="captcha.sending"
:height="70"
@tap="send"
>
发送短信
</cl-button>
</view>
</cl-loading-mask>
</cl-popup>
</view>
</template>
<script lang="ts" setup>
import { computed, type PropType, reactive, ref } from "vue";
import { useCool } from "../cool";
import { useUi } from "/$/cool-ui";
const props = defineProps({
phone: String,
type: String,
height: Number,
fontSize: Number,
size: String as PropType<"large" | "default" | "small">,
border: {
type: Boolean,
default: true,
},
plain: Boolean,
});
const emit = defineEmits(["success"]);
const { service, refs, setRefs } = useCool();
const ui = useUi();
// 验证码
const captcha = reactive({
visible: false,
loading: false,
sending: false,
img: "",
});
// 倒计时
const countdown = ref(0);
// 是否禁用
const isDisabled = computed(() => countdown.value > 0 || !props.phone);
// 按钮文案
const btnText = computed(() =>
countdown.value > 0 ? `${countdown.value}s后重新获取` : "获取验证码",
);
// 表单
const form = reactive({
code: "",
captchaId: "",
});
// 开始倒计时
function startCountdown() {
countdown.value = 60;
function fn() {
countdown.value--;
if (countdown.value < 1) {
clearInterval(timer);
}
}
const timer = setInterval(fn, 1000);
fn();
}
// 发送短信
async function send() {
if (form.code) {
captcha.sending = true;
await service.user.login
.smsCode({
phone: props.phone,
...form,
})
.then(() => {
ui.showToast("短信已发送,请查收");
startCountdown();
close();
emit("success");
})
.catch((err) => {
ui.showToast(err.message);
getCaptcha();
});
captcha.sending = false;
} else {
ui.showToast("请填写验证码");
}
}
// 获取图片验证码
async function getCaptcha() {
clear();
captcha.loading = true;
await service.user.login
.captcha({ type: "png", color: "#000000", phone: props.phone })
.then((res) => {
form.captchaId = res.captchaId;
captcha.img = res.data;
})
.catch((err) => {
ui.showToast(err.message);
});
captcha.loading = false;
}
// 打开
function open() {
if (props.phone) {
if (/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(props.phone)) {
captcha.visible = true;
getCaptcha();
} else {
ui.showToast("请填写正确的手机号格式");
}
}
}
// 关闭
function close() {
captcha.visible = false;
clear();
}
// 清空
function clear() {
form.code = "";
form.captchaId = "";
}
defineExpose({
open,
send,
getCaptcha,
startCountdown,
});
</script>
<style lang="scss" scoped>
.sms-popup {
width: 400rpx;
.head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
}
.row {
display: flex;
align-items: center;
margin-bottom: 30rpx;
image {
height: 70rpx;
width: 200rpx;
flex-shrink: 0;
}
}
}
</style>