230 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			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>
 | 
