168 lines
3.8 KiB
Vue
168 lines
3.8 KiB
Vue
|
<script setup>
|
||
|
import { ref } from 'vue'
|
||
|
import { useRouter } from 'vue-router'
|
||
|
import { verifySmsApi, sendSmsApi, getTokenApi } from '@/common/apis/ab98'
|
||
|
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||
|
import { showSuccessToast, showFailToast } from 'vant'
|
||
|
|
||
|
const userStore = useAb98UserStore()
|
||
|
const router = useRouter()
|
||
|
|
||
|
// 表单数据
|
||
|
const form = ref({
|
||
|
tel: '',
|
||
|
vcode: ''
|
||
|
})
|
||
|
|
||
|
// 验证规则
|
||
|
const rules = {
|
||
|
tel: [
|
||
|
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||
|
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
|
||
|
],
|
||
|
vcode: [
|
||
|
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||
|
]
|
||
|
}
|
||
|
|
||
|
// 倒计时状态
|
||
|
const countdown = ref(0)
|
||
|
const canSend = ref(true)
|
||
|
|
||
|
// 发送验证码
|
||
|
const handleSendSms = async () => {
|
||
|
if (!form.value.tel) {
|
||
|
showFailToast('请先输入手机号码')
|
||
|
return
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const { data: tokenData } = await getTokenApi('shop-web')
|
||
|
if (!tokenData?.success) {
|
||
|
showFailToast('获取token失败')
|
||
|
return
|
||
|
}
|
||
|
userStore.setToken(tokenData.data.token);
|
||
|
|
||
|
const { data } = await sendSmsApi(tokenData.data.token, form.value.tel)
|
||
|
if (data.success) {
|
||
|
showSuccessToast('验证码已发送')
|
||
|
startCountdown()
|
||
|
} else {
|
||
|
showFailToast(data.message || '发送失败')
|
||
|
}
|
||
|
} catch (error) {
|
||
|
showFailToast('发送验证码失败')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 开始倒计时
|
||
|
const startCountdown = () => {
|
||
|
canSend.value = false
|
||
|
countdown.value = 60
|
||
|
const timer = setInterval(() => {
|
||
|
if (countdown.value <= 0) {
|
||
|
clearInterval(timer)
|
||
|
canSend.value = true
|
||
|
return
|
||
|
}
|
||
|
countdown.value--
|
||
|
}, 1000)
|
||
|
}
|
||
|
|
||
|
// 提交登录
|
||
|
const handleSubmit = async () => {
|
||
|
try {
|
||
|
const params = {
|
||
|
token: userStore.token,
|
||
|
tel: form.value.tel,
|
||
|
vcode: form.value.vcode
|
||
|
}
|
||
|
|
||
|
const { data } = await verifySmsApi(params)
|
||
|
if (data.success) {
|
||
|
userStore.setUserInfo(data)
|
||
|
ElMessage.success('登录成功')
|
||
|
router.push('/')
|
||
|
} else {
|
||
|
ElMessage.error('验证码错误或已过期')
|
||
|
}
|
||
|
} catch (error) {
|
||
|
ElMessage.error('登录失败')
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<div class="login-container">
|
||
|
<el-card class="login-box">
|
||
|
<h2 class="title">手机验证码登录</h2>
|
||
|
|
||
|
<el-form
|
||
|
:model="form"
|
||
|
:rules="rules"
|
||
|
label-width="80px"
|
||
|
label-position="top"
|
||
|
>
|
||
|
<el-form-item label="手机号码" prop="tel">
|
||
|
<el-input v-model="form.tel" placeholder="请输入手机号码" />
|
||
|
</el-form-item>
|
||
|
|
||
|
<el-form-item label="验证码" prop="vcode">
|
||
|
<div class="vcode-input">
|
||
|
<el-input
|
||
|
v-model="form.vcode"
|
||
|
placeholder="请输入验证码"
|
||
|
style="width: 60%"
|
||
|
/>
|
||
|
<el-button
|
||
|
:disabled="!canSend"
|
||
|
@click="handleSendSms"
|
||
|
style="margin-left: 10px; width: 35%"
|
||
|
>
|
||
|
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
|
||
|
</el-button>
|
||
|
</div>
|
||
|
</el-form-item>
|
||
|
|
||
|
<el-form-item>
|
||
|
<el-button
|
||
|
type="primary"
|
||
|
@click="handleSubmit"
|
||
|
style="width: 100%"
|
||
|
>
|
||
|
立即登录
|
||
|
</el-button>
|
||
|
</el-form-item>
|
||
|
</el-form>
|
||
|
</el-card>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<style scoped>
|
||
|
.login-container {
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
min-height: 100vh;
|
||
|
background-color: #f5f7fa;
|
||
|
}
|
||
|
|
||
|
.login-box {
|
||
|
width: 400px;
|
||
|
padding: 30px;
|
||
|
border-radius: 8px;
|
||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||
|
}
|
||
|
|
||
|
.title {
|
||
|
text-align: center;
|
||
|
margin-bottom: 30px;
|
||
|
color: #303133;
|
||
|
}
|
||
|
|
||
|
.vcode-input {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
}
|
||
|
</style>
|