Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
stdj_h5
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Authored by
hookehuyr
2025-11-11 15:53:03 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
2215f09156e8dad77466b40f23d70788e060659a
2215f091
1 parent
ea132ed5
fix(登录页): 修正验证码校验规则为4位数字
修复登录页验证码校验逻辑,将验证码长度从4-6位改为严格4位数字
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
180 additions
and
167 deletions
src/views/Login.vue
src/views/Login.vue
View file @
2215f09
<!--
* @Date: 2025-11-10 18:08:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-11-11 1
4:01:4
0
* @LastEditTime: 2025-11-11 1
5:51:1
0
* @FilePath: /stdj_h5/src/views/Login.vue
* @Description: 登录页
-->
<template>
<div class="login-page">
<!-- 顶部LOGO标题 -->
<div class="logo-title">
<img class="logo-img" src="https://cdn.ipadbiz.cn/stdj/images/logo@2x.png" alt="Logo">
<div class="login-page">
<!-- 顶部LOGO标题 -->
<div class="logo-title">
<img class="logo-img" src="https://cdn.ipadbiz.cn/stdj/images/logo@2x.png" alt="Logo">
</div>
<!-- 戒子身份验证容器 -->
<div class="auth-card">
<div class="card-title">戒子身份验证</div>
<!-- 登录表单:手机号 -->
<div class="form-item">
<div class="input-with-icon phone">
<input class="input" type="tel" placeholder="请输入手机号" v-model="phone" maxlength="11" />
</div>
</div>
<!-- 戒子身份验证容器 -->
<div class="auth-card">
<div class="card-title">戒子身份验证</div>
<!-- 登录表单:手机号 -->
<div class="form-item">
<div class="input-with-icon phone">
<input class="input" type="tel" placeholder="请输入手机号" v-model="phone" maxlength="11" />
</div>
</div>
<!-- 登录表单:验证码 + 获取验证码 -->
<div class="form-item">
<div class="code-row">
<div class="input-with-icon code">
<input class="input" type="tel" placeholder="请输入验证码" v-model="code" maxlength="6" />
</div>
<div class="btn send" :class="{ disabled: send_disabled }" @click="on_click_send_sms">
<span v-if="countdown === 0">获取验证码</span>
<span v-else>{{ countdown }}s后重试</span>
</div>
</div>
</div>
<!-- 立即验证按钮 -->
<div class="form-item">
<div class="btn primary" :class="{ disabled: login_disabled }" @click="on_click_login">立即验证</div>
</div>
<!-- 登录表单:验证码 + 获取验证码 -->
<div class="form-item">
<div class="code-row">
<div class="input-with-icon code">
<input class="input" type="tel" placeholder="请输入验证码" v-model="code" maxlength="6" />
</div>
<div class="btn send" :class="{ disabled: send_disabled }" @click="on_click_send_sms">
<span v-if="countdown === 0">获取验证码</span>
<span v-else>{{ countdown }}s后重试</span>
</div>
</div>
</div>
<!-- 立即验证按钮 -->
<div class="form-item">
<div class="btn primary" :class="{ disabled: login_disabled }" @click="on_click_login">立即验证</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onUnmounted, computed } from 'vue'
import { ref, onUnmounted, computed
, onMounted
} from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import { smsAPI } from '@/api/common.js'
...
...
@@ -71,7 +71,7 @@ const logging = ref(false)
* 说明:倒计时进行中或正在发送或手机号无效时不可操作
*/
const send_disabled = computed(function () {
return countdown.value > 0 || sending.value || !is_valid_phone(phone.value)
return countdown.value > 0 || sending.value || !is_valid_phone(phone.value)
})
/**
...
...
@@ -79,11 +79,11 @@ const send_disabled = computed(function () {
* 说明:登录进行中或手机号/验证码未通过校验时不可操作
*/
const login_disabled = computed(function () {
return (
logging.value ||
!is_valid_phone(phone.value) ||
!/^\d{4,6
}$/.test(String(code.value || '').trim())
)
return (
logging.value ||
!is_valid_phone(phone.value) ||
!/^\d{4
}$/.test(String(code.value || '').trim())
)
})
/**
...
...
@@ -92,8 +92,8 @@ const login_disabled = computed(function () {
* @returns {void}
*/
const on_click_send_sms = function () {
if (send_disabled.value) return
on_send_sms()
if (send_disabled.value) return
on_send_sms()
}
/**
...
...
@@ -102,8 +102,8 @@ const on_click_send_sms = function () {
* @returns {void}
*/
const on_click_login = function () {
if (login_disabled.value) return
on_login()
if (login_disabled.value) return
on_login()
}
/**
...
...
@@ -113,7 +113,7 @@ const on_click_login = function () {
* @returns {boolean} 是否有效
*/
const is_valid_phone = function (v) {
return /^1\d{10}$/.test(String(v || '').trim())
return /^1\d{10}$/.test(String(v || '').trim())
}
/**
...
...
@@ -122,19 +122,19 @@ const is_valid_phone = function (v) {
* @returns {void}
*/
const start_countdown = function () {
countdown.value = 60
if (timer_id.value) {
clearInterval(timer_id.value)
timer_id.value = null
countdown.value = 60
if (timer_id.value) {
clearInterval(timer_id.value)
timer_id.value = null
}
timer_id.value = setInterval(function () {
countdown.value = countdown.value - 1
if (countdown.value <= 0) {
clearInterval(timer_id.value)
timer_id.value = null
countdown.value = 0
}
timer_id.value = setInterval(function () {
countdown.value = countdown.value - 1
if (countdown.value <= 0) {
clearInterval(timer_id.value)
timer_id.value = null
countdown.value = 0
}
}, 1000)
}, 1000)
}
/**
...
...
@@ -143,23 +143,23 @@ const start_countdown = function () {
* @returns {Promise<void>}
*/
const on_send_sms = async function () {
if (sending.value || countdown.value > 0) return
if (!is_valid_phone(phone.value)) {
showFailToast('请输入有效的手机号')
return
}
try {
sending.value = true
const { code } = await smsAPI({ mobile: phone.value })
if (code) {
showToast('验证码已发送')
start_countdown()
}
} catch (e) {
showFailToast('网络异常,请稍后重试')
} finally {
sending.value = false
if (sending.value || countdown.value > 0) return
if (!is_valid_phone(phone.value)) {
showFailToast('请输入有效的手机号')
return
}
try {
sending.value = true
const { code } = await smsAPI({ mobile: phone.value })
if (code) {
showToast('验证码已发送')
start_countdown()
}
} catch (e) {
showFailToast('网络异常,请稍后重试')
} finally {
sending.value = false
}
}
/**
...
...
@@ -167,153 +167,166 @@ const on_send_sms = async function () {
* @returns {Promise<void>}
*/
const on_login = async function () {
if (logging.value) return
if (!is_valid_phone(phone.value)) {
showFailToast('请输入有效的手机号')
return
}
if (!/^\d{4,6}$/.test(String(code.value || '').trim())) {
showFailToast('请输入4~6位数字验证码')
return
}
try {
logging.value = true
const { code, data } = await loginAPI({ mobile: phone.value, code: code.value })
if (code) {
// 登录成功后,将token存储到cookie中
Cookies.set('token-stdj', data.token, { expires: 7 })
showSuccessToast('登录成功')
// 跳转戒子详情页
router.replace({ path: route.query.redirect })
}
} catch (e) {
showFailToast('网络异常,请稍后重试')
} finally {
logging.value = false
if (logging.value) return
if (!is_valid_phone(phone.value)) {
showFailToast('请输入有效的手机号')
return
}
if (!/^\d{4}$/.test(String(code.value || '').trim())) {
showFailToast('请输入4位数字验证码')
return
}
try {
logging.value = true
const { code, data } = await loginAPI({ mobile: phone.value, code: code.value })
if (code) {
// 登录成功后,将token存储到cookie中
Cookies.set('token-stdj', data.token, { expires: 7 })
showSuccessToast('登录成功')
// 跳转戒子详情页
router.replace({ path: route.query.redirect })
}
} catch (e) {
showFailToast('网络异常,请稍后重试')
} finally {
logging.value = false
}
}
onMounted(() => {
})
// 组件卸载时清理计时器
onUnmounted(function () {
if (timer_id.value) {
clearInterval(timer_id.value)
timer_id.value = null
}
if (timer_id.value) {
clearInterval(timer_id.value)
timer_id.value = null
}
})
</script>
<style lang="less" scoped>
// 页面背景与布局
.login-page {
min-height: 100vh;
background-color: #FCF8F1; // 整个页面背景色
display: flex;
flex-direction: column;
align-items: center;
padding: 6rem 1rem;
background-image: url('https://cdn.ipadbiz.cn/stdj/images/bg002@2x.png');
background-size: cover;
background-position: center;
min-height: 100vh;
background-color: #FCF8F1; // 整个页面背景色
display: flex;
flex-direction: column;
align-items: center;
padding: 6rem 1rem;
background-image: url('https://cdn.ipadbiz.cn/stdj/images/bg002@2x.png');
background-size: cover;
background-position: center;
}
// 顶部Logo标题
.logo-title {
margin-bottom: 2rem;
margin-bottom: 2rem;
}
.logo-img {
height: 5.5rem;
object-fit: contain;
margin-top: 1rem;
height: 5.5rem;
object-fit: contain;
margin-top: 1rem;
}
// 验证容器
.auth-card {
max-width: 26rem;
background-color: rgba(174, 155, 99, 0.14); // 容器背景色
border-radius: 0.75rem;
padding: 1rem;
box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.06);
max-width: 26rem;
background-color: rgba(174, 155, 99, 0.14); // 容器背景色
border-radius: 0.75rem;
padding: 1rem;
box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.06);
}
.card-title {
text-align: center;
color: #432C0E;
font-size: 1.125rem;
font-weight: 700;
margin-top: 0.75rem;
margin-bottom: 1.25rem;
text-align: center;
color: #432C0E;
font-size: 1.125rem;
font-weight: 700;
margin-top: 0.75rem;
margin-bottom: 1.25rem;
}
// 表单项布局
.form-item {
margin-top: 1.25rem;
margin-bottom: 0.75rem;
margin-top: 1.25rem;
margin-bottom: 0.75rem;
}
.code-row {
display: flex;
gap: 0.5rem;
align-items: center;
display: flex;
gap: 0.5rem;
align-items: center;
}
// 输入框:带左侧图标
.input-with-icon {
position: relative;
position: relative;
}
.input-with-icon::before {
content: '';
position: absolute;
left: 0.75rem;
top: 50%;
width: 1rem;
height: 1rem;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
transform: translateY(-50%);
content: '';
position: absolute;
left: 0.75rem;
top: 50%;
width: 1rem;
height: 1rem;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
transform: translateY(-50%);
}
.input-with-icon.phone::before {
background-image: url('https://cdn.ipadbiz.cn/stdj/images/%E6%89%8B%E6%9C%BA@2x.png');
background-image: url('https://cdn.ipadbiz.cn/stdj/images/%E6%89%8B%E6%9C%BA@2x.png');
}
.input-with-icon.code::before {
background-image: url('https://cdn.ipadbiz.cn/stdj/images/%E9%AA%8C%E8%AF%81%E7%A0%81@2x-1.png');
background-image: url('https://cdn.ipadbiz.cn/stdj/images/%E9%AA%8C%E8%AF%81%E7%A0%81@2x-1.png');
}
.input {
width: 100%;
height: 2.5rem;
border-radius: 0.5rem;
border: none;
background-color: #FFFFFF;
padding: 0 0.75rem 0 2.25rem; // 为左侧图标预留空间
box-shadow: inset 0 0 0 0.0625rem rgba(0, 0, 0, 0.08);
width: 100%;
height: 2.5rem;
border-radius: 0.5rem;
border: none;
background-color: #FFFFFF;
padding: 0 0.75rem 0 2.25rem; // 为左侧图标预留空间
box-shadow: inset 0 0 0 0.0625rem rgba(0, 0, 0, 0.08);
}
.input::placeholder {
color: #999999;
color: #999999;
}
// 按钮样式
.btn {
height: 2.5rem;
padding: 0 0.75rem;
border: none;
border-radius: 0.5rem;
background-color: #A67939; // 获取验证码与立即验证背景色
color: #FFFFFF;
font-weight: 600;
text-align: center;
line-height: 2.5rem;
height: 2.5rem;
padding: 0 0.75rem;
border: none;
border-radius: 0.5rem;
background-color: #A67939; // 获取验证码与立即验证背景色
color: #FFFFFF;
font-weight: 600;
text-align: center;
line-height: 2.5rem;
}
.btn.send {
white-space: nowrap;
white-space: nowrap;
}
.btn.primary {
width: 100%;
width: 100%;
}
.btn.disabled {
// 不可操作态:明显的灰显与禁止样式
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
filter: grayscale(30%);
box-shadow: none;
// 不可操作态:明显的灰显与禁止样式
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
filter: grayscale(30%);
box-shadow: none;
}
</style>
...
...
Please
register
or
login
to post a comment