feat: 优化登录页面并添加邮箱验证码登录

This commit is contained in:
caoqianming 2026-03-09 10:59:58 +08:00
parent c0b8ccb37f
commit 389937444a
8 changed files with 272 additions and 106 deletions

View File

@ -6,8 +6,8 @@ VUE_APP_TITLE = ''
VUE_APP_PJ = '' VUE_APP_PJ = ''
# 接口地址 # 接口地址
VUE_APP_API_BASEURL = http://127.0.0.1:226/api VUE_APP_API_BASEURL = http://127.0.0.1:3400/api
VUE_APP_BASEURL = http://127.0.0.1:226 VUE_APP_BASEURL = http://127.0.0.1:3400/
# 本地端口 # 本地端口
VUE_APP_PORT = 2800 VUE_APP_PORT = 2800

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ node_modules
# local env files # local env files
.env.local .env.local
.env.*.local .env.*.local
.env.development
# Log files # Log files
npm-debug.log* npm-debug.log*

View File

@ -44,4 +44,18 @@ export default {
return await http.post(this.url, data); return await http.post(this.url, data);
} }
}, },
register: {
url: `${config.API_URL}/auth/register/`,
name: "用户注册",
req: async function(data={}){
return await http.post(this.url, data);
}
},
email_code: {
url: `${config.API_URL}/auth/email_code/`,
name: "发送邮箱验证码",
req: async function(data={}){
return await http.post(this.url, data);
}
},
} }

View File

@ -0,0 +1,120 @@
<template>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="0" size="large" @keyup.enter="login">
<el-form-item prop="email" style="margin-bottom: 25px;">
<el-input v-model="form.email" prefix-icon="el-icon-message" clearable
placeholder="邮箱号">
</el-input>
</el-form-item>
<el-form-item prop="code" style="margin-bottom: 20px;">
<div class="login-msg-yzm">
<el-input v-model="form.code" prefix-icon="el-icon-unlock" clearable
placeholder="验证码"></el-input>
<el-button @click="getCode" :disabled="disabled">获取验证码<span v-if="disabled">
({{ time }})</span></el-button>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%;" :loading="islogin" @click="login">登录
</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
redirect: undefined,
form: {
email: "",
code: "",
},
rules: {
email: [
{ required: true, message: "请输入邮箱号" },
{
validator: (rule, value, callback) => {
let reg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (reg.test(value)) {
callback();
} else {
callback(new Error('请输入正确的邮箱号'));
}
}
}
],
code: [
{ required: true, message: "请输入验证码" }
]
},
disabled: false,
time: 0,
islogin: false,
}
},
mounted() {
},
watch: {
$route: {
handler: function (route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true,
},
},
methods: {
async getCode() {
let that = this;
var validate = await this.$refs.loginForm.validateField("email").catch(() => { })
if (!validate) { return false }
this.$API.auth.email_code.req({ email: that.form.email }).then(res => {
this.$message.success('验证码已发送')
this.disabled = true;
this.time = 60
var t = setInterval(() => {
this.time -= 1
if (this.time < 1) {
clearInterval(t)
this.disabled = false
this.time = 0
}
}, 1000)
}).catch(err => {
this.disabled = false;
this.$message.warning(err)
})
},
async login() {
let that = this;
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
this.$API.auth.login_email_code.req(that.form).then(res => {
that.$TOOL.data.set("TOKEN", res.access);
that.$TOOL.data.set("TOKEN_REFRESH", res.refresh);
that.$TOOL.data.set("TOKEN_TIME", new Date().getTime());
that.$API.auth.info.get().then(res1 => {
that.$TOOL.data.set("USER_INFO", res1);
that.$TOOL.data.set("PERMISSIONS", Object.keys(res1.perms));
let base_dashboard = this.$TOOL.data.get('BASE_INFO').base.base_dashboard;
if (base_dashboard == null || base_dashboard == undefined || base_dashboard == '') {
base_dashboard = '/dashboard'
}
this.$router.replace({
path: base_dashboard,
});
})
}).catch(err => {
this.disabled = false;
this.$message.warning(err)
})
}
})
}
}
}
</script>
<style></style>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="0" size="large"> <el-form ref="loginForm" :model="form" :rules="rules" label-width="0" size="large">
<el-form-item prop="user"> <el-form-item prop="user">
<el-input v-model="form.user" prefix-icon="el-icon-user" clearable :placeholder="$t('login.userPlaceholder')"> <el-input v-model="form.user" prefix-icon="el-icon-user" clearable placeholder="用户名/邮箱号">
<!-- <template #append> <!-- <template #append>
<el-select v-model="userType" style="width: 130px"> <el-select v-model="userType" style="width: 130px">
<el-option :label="$t('login.admin')" value="admin"></el-option> <el-option :label="$t('login.admin')" value="admin"></el-option>
@ -19,20 +19,17 @@
<!-- <el-link @click="visitors">{{$t('login.fangke')}}</el-link> --> <!-- <el-link @click="visitors">{{$t('login.fangke')}}</el-link> -->
</el-col> </el-col>
<el-col :span="12" class="login-forgot"> <el-col :span="12" class="login-forgot">
<router-link to="/reset_password">{{ $t("login.forgetPassword") }}</router-link> <!-- <router-link to="/reset_password">{{ $t("login.forgetPassword") }}</router-link> -->
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" style="width: 100%" :loading="islogin" @click="login">{{ $t("login.signIn") <el-button type="primary" style="width: 100%" :loading="islogin" @click="login">{{ $t("login.signIn")
}}</el-button> }}</el-button>
</el-form-item> </el-form-item>
<div class="login-reg">
<!-- <div class="login-reg"> 还没有账号
{{ $t("login.noAccount") }} <router-link to="/user_register">立即注册</router-link>
<router-link to="/user_register">{{ </div>
$t("login.createAccount")
}}</router-link>
</div>-->
</el-form> </el-form>
</template> </template>
@ -45,8 +42,8 @@ export default {
visitorsdialog: false, visitorsdialog: false,
visitorform: {}, visitorform: {},
form: { form: {
user: "admin", user: "",
password: "admin", password: "",
autologin: false, autologin: false,
}, },
rules: { rules: {

View File

@ -35,6 +35,7 @@
</el-segmented> </el-segmented>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<password-form v-if="loginType=='password'"></password-form> <password-form v-if="loginType=='password'"></password-form>
<email-form v-if="loginType=='email'"></email-form>
<phone-form v-if="loginType=='phone'"></phone-form> <phone-form v-if="loginType=='phone'"></phone-form>
</div> </div>
<!-- <el-tabs> <!-- <el-tabs>
@ -70,11 +71,13 @@
<script> <script>
import passwordForm from './components/passwordForm' import passwordForm from './components/passwordForm'
import phoneForm from './components/phoneForm' import phoneForm from './components/phoneForm'
import emailForm from './components/emailForm'
export default { export default {
components: { components: {
passwordForm, passwordForm,
phoneForm phoneForm,
emailForm
}, },
data() { data() {
return { return {
@ -99,7 +102,8 @@ export default {
isWechatLoginResult: false, isWechatLoginResult: false,
loginType:'password', loginType:'password',
loginTypeOptions: [ loginTypeOptions: [
{label: "账号登录", value: "password"}, {label: "密码登录", value: "password"},
{label: "验证码登录", value: "email"},
] ]
} }
}, },

View File

@ -1,50 +1,48 @@
<template> <template>
<common-page title="注册新账号"> <common-page title="注册新账号">
<el-steps :active="stepActive" simple finish-status="success"> <el-steps :active="stepActive" simple finish-status="success">
<el-step title="基础信息" /> <el-step title="账号信息" />
<el-step title="详细信息" /> <el-step title="企业信息" />
<el-step title="完成注册" /> <el-step title="完成注册" />
</el-steps> </el-steps>
<el-form v-if="stepActive==0" ref="stepForm_0" :model="form" :rules="rules" :label-width="120"> <el-form v-if="stepActive==0" ref="stepForm_0" :model="form" :rules="rules" :label-width="120">
<el-form-item label="登录账号" prop="user"> <el-form-item label="邮箱" prop="email">
<el-input v-model="form.user" placeholder="请输入登录账号"></el-input> <el-input v-model="form.email" placeholder="请输入邮箱"></el-input>
<div class="el-form-item-msg">登录账号将作为登录时的唯一凭证</div> <div class="el-form-item-msg">邮箱将作为登录时的唯一凭证</div>
</el-form-item> </el-form-item>
<el-form-item label="登录密码" prop="password"> <el-form-item label="验证码" prop="code">
<el-input v-model="form.password" type="password" show-password placeholder="请输入登录密码"></el-input> <el-input v-model="form.code" placeholder="请输入验证码">
<template #append>
<el-button :disabled="codeDisabled" @click="sendCode">{{ codeBtnText }}</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入昵称"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" show-password placeholder="请输入密码"></el-input>
<sc-password-strength v-model="form.password"></sc-password-strength> <sc-password-strength v-model="form.password"></sc-password-strength>
<div class="el-form-item-msg">请输入包含英文数字的8位以上密码</div> <div class="el-form-item-msg">请输入包含英文数字的8位以上密码</div>
</el-form-item> </el-form-item>
<el-form-item label="确认密码" prop="password2"> <el-form-item label="确认密码" prop="password2">
<el-input v-model="form.password2" type="password" show-password placeholder="请再一次输入登录密码"></el-input> <el-input v-model="form.password2" type="password" show-password placeholder="请再次输入密码"></el-input>
</el-form-item>
</el-form>
<el-form v-if="stepActive==1" ref="stepForm_1" :model="form" :rules="rules" :label-width="120">
<el-form-item label="企业名称" prop="enterpriseName">
<el-input v-model="form.enterpriseName" placeholder="请输入企业名称"></el-input>
</el-form-item>
<el-form-item label="统一社会信用代码" prop="creditCode">
<el-input v-model="form.creditCode" placeholder="请输入统一社会信用代码"></el-input>
<div class="el-form-item-msg">请输入18位统一社会信用代码</div>
</el-form-item> </el-form-item>
<el-form-item label="" prop="agree"> <el-form-item label="" prop="agree">
<el-checkbox v-model="form.agree" label="">已阅读并同意</el-checkbox><span class="link" @click="showAgree=true">平台服务协议</span> <el-checkbox v-model="form.agree" label="">已阅读并同意</el-checkbox><span class="link" @click="showAgree=true">平台服务协议</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form v-if="stepActive==1" ref="stepForm_1" :model="form" :rules="rules" :label-width="120">
<el-form-item label="真实姓名" prop="userName">
<el-input v-model="form.userName" placeholder="请输入真实姓名"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱地址"></el-input>
</el-form-item>
<el-form-item label="账号类型" prop="userType">
<el-radio-group v-model="form.userType">
<el-radio-button label="1">企业开发者</el-radio-button>
<el-radio-button label="2">企业开发者</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="开通类别" prop="open">
<el-checkbox-group v-model="form.open">
<el-checkbox label="1">云存储API</el-checkbox>
<el-checkbox label="2">云检索API</el-checkbox>
<el-checkbox label="3">Javescript API</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<div v-if="stepActive==2"> <div v-if="stepActive==2">
<el-result icon="success" title="注册成功" sub-title="可以使用登录账号以及手机号登录系统"> <el-result icon="success" title="注册成功" sub-title="可以使用用户名登录系统">
<template #extra> <template #extra>
<el-button type="primary" @click="goLogin">前去登录</el-button> <el-button type="primary" @click="goLogin">前去登录</el-button>
</template> </template>
@ -78,22 +76,49 @@
return { return {
stepActive: 0, stepActive: 0,
showAgree: false, showAgree: false,
codeDisabled: false,
codeBtnText: '发送验证码',
countdown: 60,
timer: null,
form: { form: {
user: "", email: "",
code: "",
nickname: "",
password: "", password: "",
password2: "", password2: "",
agree: false, enterpriseName: "",
userName: "", creditCode: "",
email: "", agree: false
userType: "1",
open: []
}, },
rules: { rules: {
user: [ email: [
{ required: true, message: '请输入账号名'} { required: true, message: '请输入邮箱'},
{ type: 'email', message: '请输入正确的邮箱格式'}
],
code: [
{ required: true, message: '请输入验证码'}
],
nickname: [
{ required: true, message: '请输入昵称'}
], ],
password: [ password: [
{ required: true, message: '请输入密码'} { required: true, message: '请输入密码'},
{ min: 8, message: '密码长度不能少于8位', trigger: 'blur'},
{
validator: (rule, value, callback) => {
//
const hasLetter = /[a-zA-Z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSpecial = /[!@#$%^&*()_+\-=\[\]{};':"\|,.<>\/?]/.test(value);
if (!hasLetter || !hasNumber || !hasSpecial) {
callback(new Error('密码必须包含字母、数字和特殊字符'));
} else {
callback();
}
},
trigger: 'blur'
}
], ],
password2: [ password2: [
{ required: true, message: '请再次输入密码'}, { required: true, message: '请再次输入密码'},
@ -105,6 +130,13 @@
} }
}} }}
], ],
enterpriseName: [
{ required: true, message: '请输入企业名称'}
],
creditCode: [
{ required: true, message: '请输入统一社会信用代码'},
{ pattern: /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/, message: '请输入正确的18位统一社会信用代码'}
],
agree: [ agree: [
{validator: (rule, value, callback) => { {validator: (rule, value, callback) => {
if (!value) { if (!value) {
@ -113,18 +145,6 @@
callback(); callback();
} }
}} }}
],
userName: [
{ required: true, message: '请输入真实姓名'}
],
email: [
{ required: true, message: '请输入邮箱地址'}
],
userType: [
{ required: true, message: '请选择账户类型'}
],
open: [
{ required: true, message: '请选择开通类别'}
] ]
} }
} }
@ -136,6 +156,34 @@
pre(){ pre(){
this.stepActive -= 1 this.stepActive -= 1
}, },
async sendCode(){
//
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailReg.test(this.form.email)) {
this.$message.error('请输入正确的邮箱格式');
return;
}
try {
await this.$API.auth.email_code.req({ email: this.form.email });
this.$message.success('验证码已发送,请查收');
//
this.codeDisabled = true;
this.countdown = 60;
this.timer = setInterval(() => {
this.countdown--;
this.codeBtnText = `${this.countdown}秒后重试`;
if (this.countdown <= 0) {
clearInterval(this.timer);
this.codeDisabled = false;
this.codeBtnText = '发送验证码';
}
}, 1000);
} catch (error) {
this.$message.error(error.message || '验证码发送失败');
}
},
next(){ next(){
const formName = `stepForm_${this.stepActive}` const formName = `stepForm_${this.stepActive}`
this.$refs[formName].validate((valid) => { this.$refs[formName].validate((valid) => {
@ -146,15 +194,26 @@
} }
}) })
}, },
save(){ async save(){
const formName = `stepForm_${this.stepActive}` const formName = `stepForm_${this.stepActive}`
this.$refs[formName].validate((valid) => { const valid = await this.$refs[formName].validate().catch(() => {});
if (valid) { if (!valid) {
this.stepActive += 1 return false;
}else{ }
return false try {
} const data = {
}) email: this.form.email,
code: this.form.code,
nickname: this.form.nickname,
password: this.form.password,
enterprise_name: this.form.enterpriseName,
credit_code: this.form.creditCode
};
await this.$API.auth.register.req(data);
this.stepActive += 1;
} catch (error) {
this.$message.error(error.message || '注册失败,请重试');
}
}, },
goLogin(){ goLogin(){
this.$router.push({ this.$router.push({

View File

@ -34,20 +34,6 @@
v-auth="'user.create'" v-auth="'user.create'"
@click="add" @click="add"
></el-button> ></el-button>
<el-select
v-model="query.type"
placeholder="账号类型"
@change="handleQuery"
clearable
style="margin-left: 2px"
>
<el-option
v-for="item in userTypeOptions2"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<el-select <el-select
v-model="query.is_deleted" v-model="query.is_deleted"
placeholder="筛选" placeholder="筛选"
@ -90,9 +76,9 @@
width="50" width="50"
></el-table-column> ></el-table-column>
<!-- <el-table-column label="ID" prop="id" width="160"></el-table-column> --> <!-- <el-table-column label="ID" prop="id" width="160"></el-table-column> -->
<el-table-column label="姓名" prop="name"></el-table-column> <el-table-column label="姓名" prop="name" width="120"></el-table-column>
<el-table-column label="登录账号" prop="username" width="140"></el-table-column> <el-table-column label="登录账号" prop="username" width="200"></el-table-column>
<el-table-column label="是否启用" prop="is_active"> <el-table-column label="是否启用" prop="is_active" width="80">
<template #default="scope"> <template #default="scope">
<el-tag type="danger" v-if="scope.row.is_deleted">已删</el-tag> <el-tag type="danger" v-if="scope.row.is_deleted">已删</el-tag>
<el-tag v-else type="success">在用</el-tag> <el-tag v-else type="success">在用</el-tag>
@ -106,11 +92,6 @@
<el-icon v-else color="red"><CircleCloseFilled /></el-icon> <el-icon v-else color="red"><CircleCloseFilled /></el-icon>
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column label="账号类型" prop="type">
<template #default="scope">
<span>{{userTypeOptions[scope.row.type]}}</span>
</template>
</el-table-column>
<el-table-column label="部门" prop="belong_dept_name" :show-overflow-tooltip="true"> <el-table-column label="部门" prop="belong_dept_name" :show-overflow-tooltip="true">
</el-table-column> </el-table-column>
<el-table-column label="岗位" prop="post_name"> <el-table-column label="岗位" prop="post_name">
@ -195,16 +176,6 @@
label-width="100px" label-width="100px"
label-position="left" label-position="left"
> >
<el-form-item label="账户类型:" prop="type">
<el-select v-model="addForm.type" placeholder="账号类型">
<el-option
v-for="item in userTypeOptions2"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="登录账号" prop="username"> <el-form-item label="登录账号" prop="username">
<el-input <el-input
v-model="addForm.username" v-model="addForm.username"