验证码

This commit is contained in:
2021-06-08 10:08:58 +08:00
parent a15512892c
commit bbd9fec7d9
10 changed files with 1341 additions and 62 deletions

View File

@@ -73,4 +73,16 @@ export default {
* 更新基本信息
*/
sysUserUpdateInfo: ['/sysUser/updateInfo', 'post'],
/**
* 发送验证码
*/
SendCode: ['/sysUser/SendCode', 'post'],
/**
* 绑定/验证
*/
CheckBindcode: ['/sysUser/CheckBindcode', 'post'],
}

View File

@@ -1,9 +1,10 @@
const SESSION_KEY = '__SESSION'
const SETTING_KEY = '__SETTINGS'
const GLOBAL_INFO_KEY = '__GLOBAL_INFO'
const COUNT_DWON_KEY = '__COUNT_DWON'
export {
SESSION_KEY,
SETTING_KEY,
GLOBAL_INFO_KEY,
COUNT_DWON_KEY
}

View File

@@ -46,6 +46,9 @@
>
<password />
</yo-modal-form>
<phone @ok="showSafety" ref="phone" />
<mail @ok="showSafety" ref="mail" />
</container>
</template>
<style lang="less" scoped>
@@ -57,16 +60,19 @@
background-color: @white;
}
}
</style>
<script>
import { doLogout } from '@/common/login';
import Password from './password';
import phone from './phone';
import mail from './mail';
export default {
components: {
Password,
phone,
mail,
},
data() {
@@ -102,70 +108,77 @@ export default {
},
created() {
const info = this.$root.global.info;
// 登录密码
this.data.push({
title: '登录密码',
description:
'安全性高的密码可以使帐号更安全。建议您定期更换密码设置一个包含字母符号或数字中至少两项且长度超过6位的密码。',
extra: (
<div>
当前密码强度
{
[
<span class="text-error"></span>,
<span class="text-warning"></span>,
<span class="text-success"></span>,
][info.securityLevel - 1]
}
</div>
),
done: true,
action: () => {
this.$refs['password-form'].onOpen({});
},
});
// 手机绑定
this.data.push({
title: '手机绑定(发送验证码到手机,未实现)',
description: (
<div>
手机号可以直接用于登录找回密码等
{info.phone && (
<span>
您已绑定了手机<b>{info.phone}</b>
</span>
)}
</div>
),
done: !!info.phone,
action: () => {},
});
// 邮箱绑定
this.data.push({
title: '安全邮箱(发送验证码到邮箱,未实现)',
description: (
<div>
安全邮箱可以直接用于登录找回密码等
{info.email && (
<span>
您已绑定了邮箱<b>{info.email}</b>
</span>
)}
</div>
),
done: !!info.email,
action: () => {},
});
this.showSafety();
},
methods: {
onSetPasswordSuccess() {
doLogout();
},
showSafety() {
const info = this.$root.global.info;
this.data = [];
// 登录密码
this.data.push({
title: '登录密码',
description:
'安全性高的密码可以使帐号更安全。建议您定期更换密码设置一个包含字母符号或数字中至少两项且长度超过6位的密码。',
extra: (
<div>
当前密码强度
{
[
<span class="text-error"></span>,
<span class="text-warning"></span>,
<span class="text-success"></span>,
][info.securityLevel - 1]
}
</div>
),
done: true,
action: () => {
this.$refs['password-form'].onOpen({});
},
});
// 手机绑定
this.data.push({
title: '手机绑定(发送验证码到手机,未实现)',
description: (
<div>
手机号可以直接用于登录找回密码等
{info.phone && (
<span>
您已绑定了手机<b>{info.phone}</b>
</span>
)}
</div>
),
done: !!info.phone,
action: () => {
this.$refs.phone.OnOpen();
},
});
// 邮箱绑定
this.data.push({
title: '安全邮箱(发送验证码到邮箱,未实现)',
description: (
<div>
安全邮箱可以直接用于登录找回密码等
{info.email && (
<span>
您已绑定了邮箱<b>{info.email}</b>
</span>
)}
</div>
),
done: !!info.email,
action: () => {
this.$refs.mail.OnOpen();
},
});
},
},
};
</script>

View File

@@ -0,0 +1,360 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-modal
:footer="false"
:visible="visible"
@cancel="onResetFields"
class="yo-modal-form"
title="绑定邮箱"
>
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<div class="yo-form">
<div class="yo-form-group" v-if="this.type.length!==0">
<!-- 表单控件 -->
<!-- ... -->
<div style="padding: 20px 100px">
<a-steps :current="current">
<a-step :key="item.title" :title="item.title" v-for="item in steps" />
</a-steps>
</div>
<a-form-model :model="form" ref="form">
<!-- 发送给原邮箱号码验证码 -->
<div v-if="current===0">
<a-form-model-item label="选择验证方式">
<a-select style="width:300px" v-model="form.type">
<a-select-option
:key="item.Title"
:value="item.Value"
v-for="item in type"
>{{item.Title}}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入六位验证码"
style="width: 185px; margin-right: 15px"
v-model="form.orgcode"
></a-input>
<a-button :loading="codeLoading" @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</div>
<!-- 发送给绑定新邮箱号码-->
<div v-if="current===1">
<a-form-model-item label="新邮箱号码">
<a-input placeholder="请输入邮箱账号" style="width:300px" v-model="form.target"></a-input>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入六位验证码"
style="width: 185px; margin-right: 15px"
v-model="form.code"
></a-input>
<a-button :loading="codeLoading" @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</div>
</a-form-model>
<br />
<div class="steps-action" style="text-align:center">
<a-button :disabled="checkfirst" @click="next" type="primary" v-if="current == 0">下一步</a-button>
<a-button
:disabled="checkform"
:loading="completeLoading"
@click="complete"
type="primary"
v-if="current == 1"
>完成</a-button>
<a-button @click="prev" style="margin-left: 8px" v-if="current > 0">前一步</a-button>
</div>
<br />
</div>
<div class="yo-form-group" v-else>
<!-- 表单控件 -->
<!-- ... -->
<a-form-model :model="form" ref="form">
<a-form-model-item label="请输入邮箱">
<a-input placeholder="请输入邮箱号码" style="width:250px;" v-model="form.target"></a-input>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入验证码"
style="width:140px;margin-right:15px"
v-model="form.code"
></a-input>
<a-button @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</a-form-model>
<br />
<div style="text-align:center">
<a-button :disabled="checkform" @click="complete" type="primary">绑定</a-button>
</div>
<br />
</div>
</div>
</a-spin>
</a-modal>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
target: '',
code: '',
type: null,
orgcode: '',
/* ... */
};
import app from '@/main';
import { COUNT_DWON_KEY } from '@/common/storage';
export default {
data() {
return {
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
/* ... */
},
current: 0,
steps: [
{
title: '验证',
},
{
title: '绑定',
},
],
/** 加载异步数据状态 */
loading: false,
countdown: 0,
type: [],
sendOrNo: true,
codeLoading: false,
completeLoading: false,
visible: false,
checkform: true,
checkfirst: true,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
//...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
if (this.type.length != 0) {
this.form.type = this.type[0].Value;
}
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
// onGetData() {
// return new Promise((reslove, reject) => {
// this.$refs.form.validate((valid) => {
// if (valid) {
// const record = this.$_.cloneDeep(this.form);
// /** 验证通过后可以对数据进行转换得到想要提交的格式 */
// /* ... */
// reslove(record);
// } else {
// reject();
// }
// });
// });
// },
/**
* 必要的方法
* 在外部窗口进行保存时调用表单验证
*/
// onValidate(callback) {
// this.$refs.form.validate(callback);
// },
/**
* 必要的方法
* 在外部窗口关闭或重置时对表单验证进行初始化
*/
onResetFields() {
setTimeout(() => {
this.$refs.form.resetFields();
this.current = 0;
this.visible = false;
window.localStorage.removeItem(COUNT_DWON_KEY);
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
this.type = [];
/** 可以在这里await获取一些异步数据 */
const info = this.$root.global.info;
info.phone &&
this.type.push({
Title: '使用手机号' + info.phone + '进行验证',
Value: 1,
});
info.email &&
this.type.push({
Title: '使用邮箱' + info.email + '进行验证',
Value: 2,
});
this.onFillData();
this.showcountdown();
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
SendOrgSms() {
this.$api.SendOrgSms().then((res) => {
if (res) {
this.addTime();
this.showcountdown();
}
});
},
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now();
var date = now + 60 * 1000 + 500;
window.localStorage.setItem(COUNT_DWON_KEY, date);
},
/**
* 下一步
*/
next() {
this.loading = true;
this.$api
.CheckBindcode(this.form)
.then((res) => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY);
this.form.type = null;
this.current++;
}
})
.finally(() => {
this.loading = false;
});
},
/**
* 完成
*/
complete() {
this.completeLoading = true;
this.$api
.CheckBindcode(this.form)
.then((res) => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY);
var num = this.form.target;
num = num.replace(/(\w?)(\w+)(\w)(@\w+\.[a-z]+(\.[a-z]+)?)/, '$1****$4');
app.$set(app.global.info, 'email', num);
this.$message.success('改绑完成');
this.onResetFields();
this.$emit('ok', 100, 100);
}
})
.finally(() => {
this.completeLoading = false;
});
},
/**
* 发送验证码
*/
sendcode() {
this.codeLoading = true;
var reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
if (!reg.test(this.form.target) && this.form.type != '1' && this.form.type != '2') {
this.$message.warning('请输入正确的邮箱');
this.codeLoading = false;
return;
}
this.$api
.SendCode(this.form)
.then((res) => {
if (res.success) {
this.addTime();
this.showcountdown();
}
})
.finally(() => {
this.codeLoading = false;
});
},
//打开弹窗
OnOpen() {
this.onInit();
this.visible = true;
},
/**
* 向前异步
*/
prev() {
window.localStorage.removeItem(COUNT_DWON_KEY);
this.form = {
target: '',
code: '',
type: this.type[0].Value,
orgcode: '',
};
this.current--;
},
/**
* 显示倒计时
*/
showcountdown() {
let _this = this;
var Furdate = window.localStorage.getItem(COUNT_DWON_KEY);
var nowdate = new Date().getTime();
if (Furdate >= nowdate) {
this.sendOrNo = false;
this.countdown = parseInt((Furdate - nowdate) / 1000);
setTimeout(() => {
_this.showcountdown();
}, 1000);
} else {
this.sendOrNo = true;
}
},
},
watch: {
form: {
deep: true,
handler: function (newVal, oldVal) {
this.checkform = !(newVal.target != '' && newVal.code != '');
this.checkfirst = !(newVal.orgcode != '');
},
},
},
};
</script>

View File

@@ -0,0 +1,360 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-modal
:footer="false"
:visible="visible"
@cancel="onResetFields"
class="yo-modal-form"
title="绑定邮箱"
>
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<div class="yo-form">
<div class="yo-form-group" v-if="this.type.length!==0">
<!-- 表单控件 -->
<!-- ... -->
<div style="padding: 20px 100px">
<a-steps :current="current">
<a-step :key="item.title" :title="item.title" v-for="item in steps" />
</a-steps>
</div>
<a-form-model :model="form" ref="form">
<!-- 发送给原邮箱号码验证码 -->
<div v-if="current===0">
<a-form-model-item label="选择验证方式">
<a-select style="width:300px" v-model="form.type">
<a-select-option
:key="item.Title"
:value="item.Value"
v-for="item in type"
>{{item.Title}}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入六位验证码"
style="width: 185px; margin-right: 15px"
v-model="form.orgcode"
></a-input>
<a-button :loading="codeLoading" @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</div>
<!-- 发送给绑定新邮箱号码-->
<div v-if="current===1">
<a-form-model-item label="新手机号码">
<a-input placeholder="请输入手机号码" style="width:300px" v-model="form.target"></a-input>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入六位验证码"
style="width: 185px; margin-right: 15px"
v-model="form.code"
></a-input>
<a-button :loading="codeLoading" @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</div>
</a-form-model>
<br />
<div class="steps-action" style="text-align:center">
<a-button :disabled="checkfirst" @click="next" type="primary" v-if="current == 0">下一步</a-button>
<a-button
:disabled="checkform"
:loading="completeLoading"
@click="complete"
type="primary"
v-if="current == 1"
>完成</a-button>
<a-button @click="prev" style="margin-left: 8px" v-if="current > 0">前一步</a-button>
</div>
<br />
</div>
<div class="yo-form-group" v-else>
<!-- 表单控件 -->
<!-- ... -->
<a-form-model :model="form" ref="form">
<a-form-model-item label="请输入手机号码">
<a-input placeholder="请输入手机号码" style="width:250px;" v-model="form.target"></a-input>
</a-form-model-item>
<a-form-model-item label="验证码">
<a-input
placeholder="请输入验证码"
style="width:140px;margin-right:15px"
v-model="form.code"
></a-input>
<a-button @click="sendcode()" v-if="sendOrNo">发送验证码</a-button>
<a-button disabled v-else>{{ countdown }}秒后重送</a-button>
</a-form-model-item>
</a-form-model>
<br />
<div style="text-align:center">
<a-button :disabled="checkform" @click="complete" type="primary">绑定</a-button>
</div>
<br />
</div>
</div>
</a-spin>
</a-modal>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
target: '',
code: '',
type: null,
orgcode: '',
/* ... */
};
import app from '@/main';
import { COUNT_DWON_KEY } from '@/common/storage';
export default {
data() {
return {
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
/* ... */
},
current: 0,
steps: [
{
title: '验证',
},
{
title: '绑定',
},
],
/** 加载异步数据状态 */
loading: false,
countdown: 0,
type: [],
sendOrNo: true,
codeLoading: false,
completeLoading: false,
visible: false,
checkform: true,
checkfirst: true,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
//...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
if (this.type.length != 0) {
this.form.type = this.type[0].Value;
}
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
// onGetData() {
// return new Promise((reslove, reject) => {
// this.$refs.form.validate((valid) => {
// if (valid) {
// const record = this.$_.cloneDeep(this.form);
// /** 验证通过后可以对数据进行转换得到想要提交的格式 */
// /* ... */
// reslove(record);
// } else {
// reject();
// }
// });
// });
// },
/**
* 必要的方法
* 在外部窗口进行保存时调用表单验证
*/
// onValidate(callback) {
// this.$refs.form.validate(callback);
// },
/**
* 必要的方法
* 在外部窗口关闭或重置时对表单验证进行初始化
*/
onResetFields() {
setTimeout(() => {
this.$refs.form.resetFields();
this.current = 0;
this.visible = false;
window.localStorage.removeItem(COUNT_DWON_KEY);
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
this.type = [];
/** 可以在这里await获取一些异步数据 */
const info = this.$root.global.info;
info.phone &&
this.type.push({
Title: '使用手机号' + info.phone + '进行验证',
Value: 1,
});
info.email &&
this.type.push({
Title: '使用邮箱' + info.email + '进行验证',
Value: 2,
});
this.onFillData();
this.showcountdown();
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
SendOrgSms() {
this.$api.SendOrgSms().then((res) => {
if (res) {
this.addTime();
this.showcountdown();
}
});
},
/**
* 将倒计时添加入到本地
*/
addTime() {
const now = Date.now();
var date = now + 60 * 1000 + 500;
window.localStorage.setItem(COUNT_DWON_KEY, date);
},
/**
* 下一步
*/
next() {
this.loading = true;
this.$api
.CheckBindcode(this.form)
.then((res) => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY);
this.form.type = null;
this.current++;
}
})
.finally(() => {
this.loading = false;
});
},
/**
* 完成
*/
complete() {
this.completeLoading = true;
this.$api
.CheckBindcode(this.form)
.then((res) => {
if (res.data) {
window.localStorage.removeItem(COUNT_DWON_KEY);
var num = this.form.target;
num = num.replace(/^(\d{3})\d{3}(\d+)/, '$1****$2');
app.$set(app.global.info, 'phone', num);
this.$message.success('改绑完成');
this.onResetFields();
this.$emit('ok', 100, 100);
}
})
.finally(() => {
this.completeLoading = false;
});
},
/**
* 发送验证码
*/
sendcode() {
this.codeLoading = true;
var reg = /^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/;
if (!reg.test(this.form.target) && this.form.type != '1' && this.form.type != '2') {
this.$message.warning('请输入正确的手机号');
this.codeLoading = false;
return;
}
this.$api
.SendCode(this.form)
.then((res) => {
if (res.success) {
this.addTime();
this.showcountdown();
}
})
.finally(() => {
this.codeLoading = false;
});
},
//打开弹窗
OnOpen() {
this.onInit();
this.visible = true;
},
/**
* 向前异步
*/
prev() {
window.localStorage.removeItem(COUNT_DWON_KEY);
this.form = {
target: '',
code: '',
type: this.type[0].Value,
orgcode: '',
};
this.current--;
},
/**
* 显示倒计时
*/
showcountdown() {
let _this = this;
var Furdate = window.localStorage.getItem(COUNT_DWON_KEY);
var nowdate = new Date().getTime();
if (Furdate >= nowdate) {
this.sendOrNo = false;
this.countdown = parseInt((Furdate - nowdate) / 1000);
setTimeout(() => {
_this.showcountdown();
}, 1000);
} else {
this.sendOrNo = true;
}
},
},
watch: {
form: {
deep: true,
handler: function (newVal, oldVal) {
this.checkform = !(newVal.target != '' && newVal.code != '');
this.checkfirst = !(newVal.orgcode != '');
},
},
},
};
</script>