【私域操盘手】手机短信验证码登录

This commit is contained in:
eison
2025-03-16 19:24:37 +08:00
parent 1d7a87f29f
commit 5ce78d648e
39 changed files with 10304 additions and 13996 deletions

View File

@@ -1,10 +1,34 @@
import { post, get } from '@/utils/request'
// 登录服务接口
export const ServeLogin = data => {
/**
* 用户登录
* @param {Object} data 登录数据
* @param {string} data.username 用户名
* @param {string} data.password 密码
* @param {boolean} data.is_encrypted 密码是否已加密
* @returns {Promise} 登录结果
*/
export function ServeLogin(data) {
return post('/api/auth/login', data)
}
/**
* 手机号验证码登录
* @param {Object} data 登录数据
* @param {string} data.mobile 手机号
* @param {string} data.code 验证码
* @param {boolean} data.is_encrypted 验证码是否已加密
* @returns {Promise} 登录结果
*/
export function ServeMobileLogin(data) {
return post('/api/auth/mobile-login', data)
}
// 发送验证码
export const ServeSendCode = data => {
return post('/api/auth/code', data)
}
// 获取用户信息
export const ServeGetUser = () => {
return get('/api/auth/info')

View File

@@ -12,5 +12,13 @@ export default {
},
component: () => import('@/views/auth/login'),
},
{
path: '/auth/mobile-login',
meta: {
title: '手机号登录',
needLogin: false,
},
component: () => import('@/views/auth/mobile-login'),
},
],
}

View File

@@ -0,0 +1,36 @@
import CryptoJS from 'crypto-js'
/**
* 密码加密工具类
*/
export default {
/**
* MD5加密
* @param {string} text 需要加密的文本
* @returns {string} 加密后的文本
*/
md5(text) {
return CryptoJS.MD5(text).toString()
},
/**
* SHA256加密
* @param {string} text 需要加密的文本
* @returns {string} 加密后的文本
*/
sha256(text) {
return CryptoJS.SHA256(text).toString()
},
/**
* 密码加密
* 使用SHA256加密可以根据需要修改为其他算法
* @param {string} password 原始密码
* @returns {string} 加密后的密码
*/
encryptPassword(password) {
// 可以添加自定义的盐值增加安全性
const salt = 'yishi_salt_2024'
return this.sha256(password + salt)
}
}

View File

@@ -13,6 +13,12 @@ const request = axios.create({
timeout: 20000,
})
// 输出当前环境和API基础URL仅在开发环境
if (process.env.NODE_ENV === 'development') {
console.log('当前环境:', process.env.NODE_ENV)
console.log('API基础URL:', config.BASE_API_URL)
}
/**
* 异常拦截处理器
*

View File

@@ -30,6 +30,9 @@
>立即登录
</el-button>
</el-form-item>
<div class="login-options">
<span @click="toMobileLogin">手机号登录</span>
</div>
</el-form>
</div>
</div>
@@ -37,6 +40,7 @@
<script>
import { setToken, setUserInfo } from '@/utils/auth'
import { ServeLogin } from '@/api/user'
import CryptoUtil from '@/utils/crypto'
export default {
data() {
@@ -76,9 +80,13 @@ export default {
},
login() {
// 对密码进行加密
const encryptedPassword = CryptoUtil.encryptPassword(this.form.password)
ServeLogin({
username: this.form.username,
password: this.form.password,
password: encryptedPassword,
is_encrypted: true // 标记密码已加密
})
.then(res => {
if (res.code == 200) {
@@ -112,6 +120,10 @@ export default {
})
},
toMobileLogin() {
this.$router.push('/auth/mobile-login')
},
toLink(url) {
this.$router.push({
path: url,
@@ -122,4 +134,12 @@ export default {
</script>
<style lang="less" scoped>
@import '~@/assets/css/page/login-auth.less';
.login-options {
text-align: right;
margin-top: 10px;
font-size: 14px;
color: #409EFF;
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div id="login-box">
<div class="header">手机号登录</div>
<div class="main">
<el-form ref="form" :model="form" :rules="rules">
<el-form-item prop="mobile">
<el-input
v-model="form.mobile"
placeholder="手机号"
class="cuborder-radius"
maxlength="11"
@keyup.enter.native="onSubmit('form')"
/>
</el-form-item>
<el-form-item prop="code">
<div class="code-input">
<el-input
v-model="form.code"
placeholder="验证码"
class="cuborder-radius"
maxlength="6"
@keyup.enter.native="onSubmit('form')"
/>
<el-button
type="primary"
class="code-btn"
:disabled="codeBtnDisabled"
@click="sendCode"
>{{ codeBtnText }}</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
class="submit-btn"
:loading="loginLoading"
@click="onSubmit('form')"
>立即登录
</el-button>
</el-form-item>
<div class="login-options">
<span @click="toAccountLogin">账号密码登录</span>
</div>
</el-form>
</div>
</div>
</template>
<script>
import { setToken, setUserInfo } from '@/utils/auth'
import { ServeMobileLogin, ServeSendCode } from '@/api/user'
import CryptoUtil from '@/utils/crypto'
export default {
data() {
// 手机号验证规则
const validateMobile = (rule, value, callback) => {
if (!value) {
return callback(new Error('请输入手机号'))
}
if (!/^1[3-9]\d{9}$/.test(value)) {
return callback(new Error('手机号格式不正确'))
}
callback()
}
return {
loginLoading: false,
form: {
mobile: '',
code: '',
},
rules: {
mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{ validator: validateMobile, trigger: 'blur' }
],
code: [
{ required: true, message: '验证码不能为空', trigger: 'blur' },
{ min: 4, max: 6, message: '验证码长度必须在4-6位之间', trigger: 'blur' }
],
},
codeBtnText: '获取验证码',
codeBtnDisabled: false,
countdown: 60,
timer: null
}
},
beforeDestroy() {
// 组件销毁前清除定时器
if (this.timer) {
clearInterval(this.timer)
}
},
methods: {
onSubmit(formName) {
if (this.loginLoading) return false
this.$refs[formName].validate(valid => {
if (!valid) return false
this.loginLoading = true
this.login()
})
},
login() {
// 不再对验证码进行加密,直接使用明文
ServeMobileLogin({
mobile: this.form.mobile,
code: this.form.code,
is_encrypted: false // 标记验证码未加密
})
.then(res => {
if (res.code == 200) {
let result = res.data
// 保存授权信息到本地缓存
setToken(result.token, result.token_expired - Math.floor(Date.now() / 1000))
// 保存用户信息到本地缓存
setUserInfo(result.member)
this.$store.commit('UPDATE_USER_INFO', result.member)
this.$store.commit('UPDATE_LOGIN_STATUS', true)
this.$notify.success({
title: '成功',
message: '登录成功',
})
// 跳转到首页
this.toLink('/')
} else {
this.$notify.error({
title: '错误',
message: res.msg,
})
}
})
.finally(() => {
this.loginLoading = false
})
},
// 发送验证码
sendCode() {
if (this.codeBtnDisabled) return
// 验证手机号
this.$refs.form.validateField('mobile', (errorMsg) => {
if (errorMsg) return
this.codeBtnDisabled = true
// 发送验证码请求
ServeSendCode({
mobile: this.form.mobile,
type: 'login'
}).then(res => {
if (res.code === 200) {
this.$notify.success({
title: '成功',
message: '验证码发送成功',
})
// 开始倒计时
this.startCountdown()
// 测试环境下,自动填充验证码
if (res.data && res.data.code) {
this.form.code = res.data.code
}
} else {
this.$notify.error({
title: '错误',
message: res.msg,
})
this.codeBtnDisabled = false
}
}).catch(() => {
this.codeBtnDisabled = false
})
})
},
// 开始倒计时
startCountdown() {
this.countdown = 60
this.codeBtnText = `${this.countdown}秒后重新获取`
this.timer = setInterval(() => {
if (this.countdown > 1) {
this.countdown--
this.codeBtnText = `${this.countdown}秒后重新获取`
} else {
clearInterval(this.timer)
this.codeBtnDisabled = false
this.codeBtnText = '获取验证码'
}
}, 1000)
},
// 跳转到账号密码登录
toAccountLogin() {
this.$router.push('/auth/login')
},
toLink(url) {
this.$router.push({
path: url,
})
},
},
}
</script>
<style lang="less" scoped>
@import '~@/assets/css/page/login-auth.less';
.code-input {
display: flex;
align-items: center;
.el-input {
flex: 1;
}
.code-btn {
margin-left: 10px;
width: 120px;
}
}
.login-options {
text-align: right;
margin-top: 10px;
font-size: 14px;
color: #409EFF;
cursor: pointer;
}
</style>