feat(user): 新增短信验证码发送功能- 在 user.js 中添加 send_sms 方法- 在 system.js 中添加 verifyUpdatePassword 方法- 新增 send-phone-code.vue 组件,用于发送和验证短信验证码- 修改 login.vue,使用新的 send-phone-code 组件替换原有短信验证逻辑- 更新 ResetPasswordDialog.vue,集成短信验证码验证功能

This commit is contained in:
陈昱达
2025-08-13 15:50:14 +08:00
parent 8ec1ea2d6a
commit 0e625655fa
6 changed files with 345 additions and 176 deletions

View File

@@ -49,6 +49,12 @@ export function sendPhoneCodeLogin(data) {
data data
}) })
} }
export function send_sms() {
return request({
url: getUrl('/sysUserEx/send_sms'),
method: 'get'
})
}
export function verify_login(data) { export function verify_login(data) {
return request({ return request({
url: getUrl('/sysUserEx/verify_login'), url: getUrl('/sysUserEx/verify_login'),

View File

@@ -62,6 +62,14 @@ export function updatePassword(data) {
return request({ return request({
url: getUrl(`/sysUserEx/updatePassword`), url: getUrl(`/sysUserEx/updatePassword`),
method: 'post', method: 'post',
data,
back: true
})
}
export function verifyUpdatePassword(data) {
return request({
url: getUrl(`/sysUserEx/verifyUpdatePassword`),
method: 'post',
data data
}) })
} }

View File

@@ -0,0 +1,197 @@
<template>
<r-dialog
:visible.sync="visible"
title="发送短信验证码"
width="550px"
append-to-body
class="send-chat-phone-dialog"
>
<el-form
label-width="100px"
:model="phoneForm"
:rules="phoneRules"
class="phone-form"
ref="phoneForm"
>
<!-- <el-form-item label="手机号" prop="phone">-->
<!-- <div class="flex">-->
<!-- <el-input-->
<!-- v-model="phoneForm.phone"-->
<!-- placeholder="请输入手机号"-->
<!-- style="color:#000"-->
<!-- ></el-input>-->
<!-- <el-button-->
<!-- class="ml10 render-button pv5 ph10"-->
<!-- type="primary"-->
<!-- size="medium"-->
<!-- @click="sendPhoneCode"-->
<!-- :disabled="!sendCode"-->
<!-- >{{ !sendCode ? minute + '秒后重新发送' : '发送验证码' }}-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </el-form-item>-->
<el-form-item label="短信验证码" prop="code" class="mr30">
<div class="flex">
<el-input
v-model="phoneForm.code"
placeholder="请输入短信验证码"
style="color:#000"
size="medium"
>
<template slot="append">
<el-button
class="ml10"
type="primary"
size="medium"
@click="sendPhoneCode"
:disabled="!sendCode"
>{{ !sendCode ? minute + '秒后重新发送' : '发送短信验证码' }}
</el-button>
</template>
</el-input>
</div>
<!-- <el-input-->
<!-- v-model="phoneForm.code"-->
<!-- placeholder="请输入验证码"-->
<!-- ></el-input>-->
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel" size="medium">取消</el-button>
<el-button type="primary" @click="handleSubmit" size="medium"
>确定</el-button
>
</div>
</r-dialog>
</template>
<script>
import { sendPhoneCodeLogin, send_sms } from '@/api/app/user'
import { setToken } from '@/assets/js/utils/auth'
export default {
name: 'send-phone-code',
data() {
const validatePhone = (rule, value, callback) => {
// 手机号正则
const validPhone = phone => {
return /^1[3456789]\d{9}$/.test(phone)
}
if (!validPhone(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
sendCode: true,
phoneRules: {
phone: [
{ required: true, message: '请输入手机号', trigger: 'change' },
{ validator: validatePhone, trigger: 'change' }
],
code: [{ required: true, message: '请输入验证码', trigger: 'change' }]
},
phoneForm: {
phone: '',
code: ''
},
minute: 120 // 默认120秒 发送间隔
}
},
props: {
visible: {
type: Boolean,
default: false
},
userName: {
type: String,
default: ''
},
resetPassword: {
type: Boolean,
default: false
}
},
watch: {
visible(newVal) {
if (!newVal) {
// 重置表单
this.phoneForm = {
phone: '',
code: ''
}
// this.sendCode = true
// clearInterval(this.timer)
}
}
},
components: {},
filters: {},
methods: {
sendCodeText() {
// 倒计时 60S
this.minute = 120
this.timer = setInterval(() => {
this.minute--
if (this.minute <= 0) {
this.sendCode = true
clearInterval(this.timer)
}
}, 1000)
return this.minute
},
sendPhoneCode() {
let api = this.resetPassword ? send_sms : sendPhoneCodeLogin
api({
userName: !this.resetPassword ? this.userName : undefined
}).then(res => {
if (Number(res.code) === 0) {
this.sendCode = false
this.sendCodeText()
}
})
},
handleSubmit() {
this.$refs.phoneForm.validate(valid => {
if (valid) {
this.$emit('handleSubmit', this.phoneForm.code)
}
})
},
cancel() {
this.$emit('update:visible', false)
}
},
created() {
console.log(this.sendCode, 231)
},
mounted() {},
computed: {}
}
</script>
<style lang="scss">
@import '../assets/sass/renderSass/theme.scss';
.send-chat-phone-dialog {
.phone-form {
& .el-input-group__append {
background: $--color-primary;
color: #fff;
border-color: $--color-primary;
& .el-button {
&.is-disabled {
& ~ .el-input-group__append {
background: $--color-primary-disabled;
}
}
}
}
}
}
</style>

View File

@@ -112,68 +112,11 @@
<!-- <p>京公网安备 ICP许可证号津ICP备14004859号-4</p>--> <!-- <p>京公网安备 ICP许可证号津ICP备14004859号-4</p>-->
</div> </div>
<r-dialog <send-phone-code
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
title="发送短信验证码" :user-name="loginForm.userName"
width="550px" @handleSubmit="handleSubmit"
> ></send-phone-code>
<el-form
label-width="100px"
:model="phoneForm"
:rules="phoneRules"
class="phone-form"
ref="phoneForm"
>
<!-- <el-form-item label="手机号" prop="phone">-->
<!-- <div class="flex">-->
<!-- <el-input-->
<!-- v-model="phoneForm.phone"-->
<!-- placeholder="请输入手机号"-->
<!-- style="color:#000"-->
<!-- ></el-input>-->
<!-- <el-button-->
<!-- class="ml10 render-button pv5 ph10"-->
<!-- type="primary"-->
<!-- size="medium"-->
<!-- @click="sendPhoneCode"-->
<!-- :disabled="!sendCode"-->
<!-- >{{ !sendCode ? minute + '秒后重新发送' : '发送验证码' }}-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </el-form-item>-->
<el-form-item label="短信验证码" prop="code" class="mr30">
<div class="flex">
<el-input
v-model="phoneForm.code"
placeholder="请输入短信验证码"
style="color:#000"
size="medium"
>
<template slot="append">
<el-button
class="ml10"
type="primary"
size="medium"
@click="sendPhoneCode"
:disabled="!sendCode"
>{{ !sendCode ? minute + '秒后重新发送' : '发送短信验证码' }}
</el-button>
</template>
</el-input>
</div>
<!-- <el-input-->
<!-- v-model="phoneForm.code"-->
<!-- placeholder="请输入验证码"-->
<!-- ></el-input>-->
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="medium">取消</el-button>
<el-button type="primary" @click="handleSubmit" size="medium"
>确定</el-button
>
</div>
</r-dialog>
</div> </div>
</template> </template>
@@ -306,21 +249,17 @@ export default {
} }
}) })
}, },
handleSubmit() { handleSubmit(code) {
this.$refs.phoneForm.validate(valid => { verify_login({
if (valid) { smsCode: code,
verify_login({ userName: this.loginForm.userName
smsCode: this.phoneForm.code, }).then(vali => {
userName: this.loginForm.userName if (Number(vali.code) === 0) {
}).then(vali => { this.$store.commit('user/SET_TOKEN', vali.content.content.sid)
if (Number(vali.code) === 0) { setToken(vali.content.content.sid)
this.$store.commit('user/SET_TOKEN', vali.content.content.sid) localStorage.setItem('deviceId', vali.content.content.deviceId)
setToken(vali.content.content.sid) this.$router.push({ path: '/home' })
localStorage.setItem('deviceId', vali.content.content.deviceId) this.loading = false
this.$router.push({ path: '/home' })
this.loading = false
}
})
} }
}) })
}, },
@@ -428,20 +367,7 @@ $cursor: #fff;
font-style: normal; font-style: normal;
} }
} }
.phone-form {
& .el-input-group__append {
background: $--color-primary;
color: #fff;
border-color: $--color-primary;
& .el-button {
&.is-disabled {
& ~ .el-input-group__append {
background: $--color-primary-disabled;
}
}
}
}
}
.login-input { .login-input {
position: relative; position: relative;
border: 1px solid #eaecf5; border: 1px solid #eaecf5;

View File

@@ -238,14 +238,30 @@ export default {
} }
}, },
// 获取规则列表数据 // 获取规则列表数据
getList(queryParams) { getList() {
const queryParams = Object.assign({}, this.queryParams)
this.loading = true this.loading = true
const params = Object.assign( // 如果ruleType是中文转换为编码值
{}, if (queryParams && queryParams.ruleType) {
queryParams ? queryParams : this.queryParams const ruleTypeItem = this.ruleTypeList.find(
) item => item.typeCode === queryParams.ruleType
)
getRulePage(params) if (queryParams && ruleTypeItem) {
queryParams.ruleType = ruleTypeItem.typeName
}
}
// 如果ruleField是中文转换为编码值
if (queryParams && queryParams.ruleField) {
const fieldTypeItem = this.fieldTypeList.find(
item => item.fieldName === queryParams.ruleField
)
if (queryParams && fieldTypeItem) {
queryParams.ruleField = fieldTypeItem.fieldComment
}
}
getRulePage(queryParams)
.then(response => { .then(response => {
// 根据实际API返回结构调整兼容Vue2语法 // 根据实际API返回结构调整兼容Vue2语法
var content = response.content.content || {} var content = response.content.content || {}
@@ -331,25 +347,6 @@ export default {
this.queryParams.page = 1 this.queryParams.page = 1
// 创建查询参数的副本 // 创建查询参数的副本
const queryParams = Object.assign({}, this.queryParams) const queryParams = Object.assign({}, this.queryParams)
// 如果ruleType是中文转换为编码值
if (queryParams.ruleType) {
const ruleTypeItem = this.ruleTypeList.find(
item => item.typeCode === queryParams.ruleType
)
if (ruleTypeItem) {
queryParams.ruleType = ruleTypeItem.typeName
}
}
// 如果ruleField是中文转换为编码值
if (queryParams.ruleField) {
const fieldTypeItem = this.fieldTypeList.find(
item => item.fieldName === queryParams.ruleField
)
if (fieldTypeItem) {
queryParams.ruleField = fieldTypeItem.fieldComment
}
}
// 使用转换后的参数进行查询 // 使用转换后的参数进行查询
// this.queryParams = queryParams // this.queryParams = queryParams

View File

@@ -1,68 +1,78 @@
<template> <template>
<el-dialog <div>
title="修改密码" {{ minute }}
:visible.sync="visible" <r-dialog
width="500px" title="修改密码"
append-to-body :visible.sync="visible"
@close="handleClose" width="500px"
> append-to-body
<el-form @close="handleClose"
ref="form"
:model="form"
:rules="rules"
label-width="120px"
status-icon
label-position="top"
> >
<el-form-item label="旧密码" prop="userPassword"> <el-form
<el-input ref="form"
v-model="form.userPassword" :model="form"
placeholder="请输入旧密码" :rules="rules"
type="password" label-width="120px"
show-password status-icon
clearable label-position="top"
size="small"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="form.newPassword"
placeholder="请输入新密码"
type="password"
show-password
clearable
size="small"
/>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmPassword">
<el-input
v-model="form.confirmPassword"
placeholder="请再次输入新密码"
type="password"
show-password
clearable
size="small"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button
type="primary"
size="small"
:loading="loading"
@click="submitForm"
> </el-button
> >
<el-button size="small" @click="handleClose"> </el-button> <el-form-item label="旧密码" prop="userPassword">
</div> <el-input
</el-dialog> v-model="form.userPassword"
placeholder="请输入旧密码"
type="password"
show-password
clearable
size="small"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="form.newPassword"
placeholder="请输入新密码"
type="password"
show-password
clearable
size="small"
/>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmPassword">
<el-input
v-model="form.confirmPassword"
placeholder="请再次输入新密码"
type="password"
show-password
clearable
size="small"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button
type="primary"
size="medium"
:loading="loading"
@click="submitForm"
> </el-button
>
<el-button size="medium" @click="handleClose"> </el-button>
</div>
</r-dialog>
<send-phone-code
:visible.sync="phoneVisabled"
:resetPassword="true"
@handleSubmit="handleSubmit"
></send-phone-code>
</div>
</template> </template>
<script> <script>
import { updatePassword } from '@/api/generatedApi/system' import { updatePassword, verifyUpdatePassword } from '@/api/generatedApi/system'
import SendPhoneCode from '@/generatedComponents/send-phone-code.vue'
export default { export default {
name: 'ResetPasswordDialog', name: 'ResetPasswordDialog',
components: { SendPhoneCode },
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
@@ -83,6 +93,8 @@ export default {
} }
} }
return { return {
phoneVisabled: false,
minute: 120,
form: { form: {
userPassword: '', userPassword: '',
newPassword: '', newPassword: '',
@@ -105,6 +117,19 @@ export default {
} }
}, },
methods: { methods: {
handleSubmit(code) {
verifyUpdatePassword({
userPassword: this.form.userPassword,
newPassword: this.form.newPassword,
smsCode: code
}).then(async res => {
if (res) {
this.$message.success('密码修改成功')
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
})
},
handleClose() { handleClose() {
this.$refs.form.resetFields() this.$refs.form.resetFields()
this.$emit('update:visible', false) this.$emit('update:visible', false)
@@ -119,13 +144,23 @@ export default {
} }
updatePassword(data) updatePassword(data)
.then(async () => { .then(async res => {
this.$message.success('密码修改成功') let number = Number(res.content.result)
await this.$store.dispatch('user/logout') switch (number) {
this.$router.push(`/login?redirect=${this.$route.fullPath}`) case 0:
this.$message.success('密码修改成功')
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
break
case 51001:
this.phoneVisabled = true
break
default:
this.$message.error(res.content.resultMessage)
}
}) })
.catch(() => { .catch(() => {
this.$message.error('密码修改出错') this.$message.error('系统异常,请联系管理员')
}) })
.finally(() => { .finally(() => {
this.loading = false this.loading = false