提交 576561bc 编写于 作者: Cano1997's avatar Cano1997

update: 新增忘记密码与修改密码功能

上级 58a3fa59
......@@ -90,6 +90,8 @@ import {
CodeList,
TabPageExp,
ViewMessage,
ForgetPassword,
ChangePassword,
} from './components/common';
// 编辑器组件
import {
......@@ -139,6 +141,9 @@ export const AppRegister = {
v.component('ViewBase', ViewBase);
v.component('MdViewBase', MDViewBase);
v.component('ViewShell', ViewShell);
// 注册登录组件
v.component('ChangePassword', ChangePassword);
v.component('ForgetPassword', ForgetPassword);
// 注册视图组件
v.component('IndexView', IndexView);
v.component('GridView', GridView);
......
......@@ -23,6 +23,22 @@ export const AppUser = defineComponent({
ibiz.message.error('登出失败');
}
},
async changePwd() {
const modal = ibiz.overlay.createModal(
'ChangePassword',
{
dismiss: (result: IData) => {
modal.dismiss(result);
},
},
{
width: 'auto',
placement: 'center',
},
);
modal.present();
await modal.onWillDismiss();
},
},
render() {
return (
......@@ -40,6 +56,12 @@ export const AppUser = defineComponent({
/>
<span class={this.ns.be('avatar', 'name')}>{this.srfusername}</span>
</span>
<i-dropdown-menu slot='list'>
<i-dropdown-item>
<i class='ivu-icon ivu-icon-ios-create-outline'></i>
<span on-click={this.changePwd}>修改密码</span>
</i-dropdown-item>
</i-dropdown-menu>
{ibiz.env.disableLogout ? null : (
<i-dropdown-menu slot='list'>
<i-dropdown-item>
......
@include b(change-password) {
@include e(header) {
height: 60px;
padding: 0 20px;
line-height: 60px;
font-size: 18px;
border: 1px solid getCssVar(border, color);
}
@include e(content) {
padding: 20px 40px;
.ivu-form-item:last-child {
margin: 0;
}
}
@include e(verification) {
.ivu-form-item-content {
display: flex;
}
.ivu-btn {
margin-left: 20px;
}
}
@include e(footer) {
padding: 0 40px 20px;
}
}
\ No newline at end of file
/* eslint-disable @typescript-eslint/no-explicit-any */
import { defineComponent, PropType, reactive, ref } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import './change-password.scss';
interface UserData {
oldPwd: string;
newPwd: string;
comfirmPwd: string;
}
export const ChangePassword = defineComponent({
name: 'ChangePassword',
props: {
dismiss: {
type: Function as PropType<(_result: IParams) => void>,
},
},
setup(props) {
const ns = useNamespace('change-password');
const formRef = ref<IData | null>(null);
const userData = reactive<UserData>({
oldPwd: '',
newPwd: '',
comfirmPwd: '',
});
const rules = {
oldPwd: [
{
required: true,
message: '请输入原密码',
trigger: 'blur',
},
],
newPwd: [
{
required: true,
message: '请输入新密码',
trigger: 'blur',
},
{
validator: (rule: IData, value: string, callback: any) => {
// 至少8位字符
if (value && value.length < 8) {
callback(new Error('密码长度不能少于8位'));
}
// 至少1个大写字母
if (!/[A-Z]/.test(value)) {
callback(new Error('至少1个大写字母'));
}
// 至少1个小写字母
if (!/[a-z]/.test(value)) {
callback(new Error('至少1个小写字母'));
}
// 至少1个数字
if (!/[0-9]/.test(value)) {
callback(new Error('至少1个数字'));
}
// 至少1个特殊符号
if (!/[!@#$%^&*]/.test(value)) {
callback(new Error('至少1个特殊符号'));
}
callback();
},
trigger: 'blur',
},
],
comfirmPwd: [
{
required: true,
message: '请再次输入新密码',
trigger: 'blur',
},
{
validator: (rule: IData, value: string, callback: any) => {
const { newPwd, comfirmPwd } = userData;
if (newPwd !== comfirmPwd) {
callback(new Error('两次输入密码不一致'));
}
callback();
},
trigger: 'blur',
},
],
};
const loading = ref(false);
const onClick = () => {
formRef.value!.validate(async (valid: boolean) => {
if (valid) {
try {
loading.value = true;
const { oldPwd, newPwd } = userData;
const res = await ibiz.net.post('/uaa/changepwd', {
oldPwd,
newPwd,
});
if (res.ok) {
ibiz.notification.success({
title: '修改密码成功',
});
} else {
ibiz.notification.error({
title: res.data?.message || '修改密码失败',
});
}
console.log(props);
props.dismiss?.({ ok: true, data: {} });
loading.value = false;
} catch (error) {
ibiz.notification.error({
title: (error as IData).response?.data?.message || '修改密码失败',
});
loading.value = false;
}
}
});
};
return () => (
<div class={ns.b()}>
<div class={ns.e('header')}>修改密码</div>
<div class={ns.e('content')}>
<i-form ref={formRef} props={{ model: userData, rules }}>
<i-form-item prop='oldPwd'>
<i-input
type='password'
value={userData.oldPwd}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.oldPwd = value;
}}
placeholder='请输入原密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item prop='newPwd'>
<i-input
type='password'
value={userData.newPwd}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.newPwd = value;
}}
placeholder='请输入新密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item prop='comfirmPwd'>
<i-input
type='password'
value={userData.comfirmPwd}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.comfirmPwd = value;
}}
placeholder='请再次输入新密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item>
<i-button
type='primary'
shape='circle'
loading={loading.value}
long
on-click={onClick}
>
确认修改
</i-button>
</i-form-item>
</i-form>
</div>
</div>
);
},
});
@include b(forget-password) {
@include e(header) {
height: 60px;
padding: 0 20px;
line-height: 60px;
font-size: 18px;
border: 1px solid getCssVar(border, color);
}
@include e(content) {
padding: 20px 40px;
.ivu-form-item:last-child {
margin: 0;
}
}
@include e(verification) {
.ivu-form-item-content {
display: flex;
}
.ivu-btn {
margin-left: 20px;
}
}
@include e(footer) {
padding: 0 40px 20px;
}
}
\ No newline at end of file
/* eslint-disable @typescript-eslint/no-explicit-any */
import { defineComponent, PropType, reactive, ref } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import './forget-password.scss';
interface UserData {
loginName: string;
verificationCode: string;
newPwd: string;
comfirmPwd: string;
}
export const ForgetPassword = defineComponent({
name: 'ForgetPassword',
props: {
dismiss: {
type: Function as PropType<(_result: IParams) => void>,
},
},
setup(props) {
const ns = useNamespace('forget-password');
const formRef = ref<IData | null>(null);
const userData = reactive<UserData>({
loginName: '',
verificationCode: '',
newPwd: '',
comfirmPwd: '',
});
const rules = {
loginName: [
{
required: true,
message: '请输入账号',
trigger: 'blur',
},
],
verificationCode: [
{
required: true,
message: '请输入验证码',
trigger: 'blur',
},
],
newPwd: [
{
required: true,
message: '请输入新密码',
trigger: 'blur',
},
{
validator: (rule: IData, value: string, callback: any) => {
// 至少8位字符
if (value && value.length < 8) {
callback(new Error('密码长度不能少于8位'));
}
// 至少1个大写字母
if (!/[A-Z]/.test(value)) {
callback(new Error('至少1个大写字母'));
}
// 至少1个小写字母
if (!/[a-z]/.test(value)) {
callback(new Error('至少1个小写字母'));
}
// 至少1个数字
if (!/[0-9]/.test(value)) {
callback(new Error('至少1个数字'));
}
// 至少1个特殊符号
if (!/[!@#$%^&*]/.test(value)) {
callback(new Error('至少1个特殊符号'));
}
callback();
},
trigger: 'blur',
},
],
comfirmPwd: [
{
required: true,
message: '请再次输入新密码',
trigger: 'blur',
},
{
validator: (rule: IData, value: string, callback: any) => {
const { newPwd, comfirmPwd } = userData;
if (newPwd !== comfirmPwd) {
callback(new Error('两次输入密码不一致'));
}
callback();
},
trigger: 'blur',
},
],
};
const loading = ref(false);
const sendVerificationCode = () => {
const { loginName } = userData;
ibiz.net
.post(`/v7/forgetPwd/sendVerificationCode/${loginName}`, {})
.catch((err: IData) => {
ibiz.notification.error({
title: (err as IData).response?.data?.message || '获取验证码失败',
});
});
};
const onClick = () => {
formRef.value!.validate(async (valid: boolean) => {
if (valid) {
try {
loading.value = true;
const { loginName, verificationCode, newPwd, comfirmPwd } =
userData;
const res = await ibiz.net.axios({
url: '/v7/forgetPwd',
method: 'post',
baseURL: ibiz.env.baseUrl,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
Accept: 'application/json',
},
data: {
loginName,
verificationCode,
newPwd,
comfirmPwd,
},
});
if (res.status === 200) {
ibiz.notification.success({
title: '重置密码成功',
});
props.dismiss?.({ ok: true, data: {} });
}
loading.value = false;
} catch (error) {
ibiz.notification.error({
title: (error as IData).response?.data?.message || '重置密码失败',
});
loading.value = false;
}
}
});
};
return () => (
<div class={ns.b()}>
<div class={ns.e('header')}>重置密码</div>
<div class={ns.e('content')}>
<i-form ref={formRef} props={{ model: userData, rules }}>
<i-form-item prop='loginName'>
<i-input
type='text'
value={userData.loginName}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.loginName = value;
}}
placeholder='请输入账号'
size='large'
>
<i-icon type='md-person' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item prop='verificationCode' class={ns.e('verification')}>
<i-input
type='number'
value={userData.verificationCode}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.verificationCode = value;
}}
placeholder='请输入验证码'
></i-input>
<i-button on-click={sendVerificationCode}>获取验证码</i-button>
</i-form-item>
<i-form-item prop='newPwd'>
<i-input
type='password'
value={userData.newPwd}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.newPwd = value;
}}
placeholder='请输入新密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item prop='comfirmPwd'>
<i-input
type='password'
value={userData.comfirmPwd}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
userData.comfirmPwd = value;
}}
placeholder='请再次输入新密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item>
<i-button
type='primary'
shape='circle'
loading={loading.value}
long
on-click={onClick}
>
确认修改
</i-button>
</i-form-item>
</i-form>
</div>
<div class={ns.e('footer')}>
<div>1. 密码长度至少有8位</div>
<div>2. 至少有1个大写字母</div>
<div>3. 至少有1个小写字母</div>
<div>4. 至少有1个数字</div>
<div>5. 至少有1个特殊符号(包括但不限于!@#$%^&*)</div>
<div>
6.
不应以姓名、电话号码、出生日期以及账号名、系统名、公司名等作为口令或者口令的组成部分
</div>
<div>
7.
不可出现3位以上(含3位)的键盘排序密码,以及连续或重复的字母、数字、特殊字符
</div>
<div>8. 不可与近5次的密码相同</div>
</div>
</div>
);
},
});
......@@ -14,6 +14,8 @@ import { ExtendActionTimeLine } from './extend-action-timeline/extend-action-tim
import { ExtendActionGrid } from './extend-action-grid/extend-action-grid';
import { TabPageExp } from './tab-page-exp/tab-page-exp';
import { ViewMessage } from './view-message/view-message';
import { ForgetPassword } from './forget-password/forget-password';
import { ChangePassword } from './change-password/change-password';
export { ImagePreview } from './image-preview/image-preview';
export { DataImport } from './data-import/data-import';
......@@ -38,4 +40,6 @@ export {
ExtendActionGrid,
TabPageExp,
ViewMessage,
ForgetPassword,
ChangePassword,
};
@include b(login-extend) {
display: flex;
justify-content: center;
align-items: flex-start;
width: 100vw;
height: 100vh;
background: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
}
@media screen and (max-width: 720px) {
@include b(login-extend) {
align-items: center;
}
}
@include b(login-extend-box) {
width: 430px;
margin-top: 130px;
background: #fff;
overflow: hidden;
}
@media screen and (max-width: 720px) {
@include b(login-extend-box) {
width: 340px;
margin-top: 0;
}
}
@include b(login-extend-box-header) {
background: #ccccff;
> img {
display: block;
width: 430px;
margin: 0 auto;
user-select: none;
}
}
@include b(login-extend-box-main) {
position: relative;
@include e(avatar) {
display: block;
position: absolute;
height: 100px;
width: 100px;
border-radius: 50%;
border: 4px solid #fff;
top: -50px;
right: 175px;
z-index: 2;
user-select: none;
}
}
@include b(login-extend-box-main-content) {
padding: 100px 40px 40px;
.ivu-input {
outline: none;
border-color: #dcdfe6;
font-size: 14px;
&:hover {
border-color: #c0c4cc;
}
&:focus {
border-color: #409eff;
box-shadow: none;
}
}
.ivu-form-item-error .ivu-input {
border-color: #f56c6c;
}
.ivu-form-item-error-tip {
padding-top: 4px;
font-size: 12px;
color: #f56c6c;
}
.ivu-form-item .ivu-btn {
display: block;
height: 40px;
margin-top: 15px;
color: #fff;
background: #409eff;
border-color: #409eff;
&:focus {
background: #79bbff;
border-color: #79bbff;
box-shadow: none;
}
&:hover {
background: #79bbff;
border-color: #79bbff;
}
&:active {
background: #337ecc;
border-color: #337ecc;
}
}
.ivu-input-wrapper-large {
.ivu-input-icon,
.ivu-input-prefix i,
.ivu-input-suffix i {
font-size: 16px;
}
}
.ivu-form-item {
margin-bottom: 22px;
}
.ivu-form {
position: relative;
&>.ivu-btn {
position: absolute;
right: 0;
bottom: 44px;
color: getCssVar(color, primary);
box-shadow: none;
}
}
}
import { CoreConst } from '@ibiz-template/core';
import {
defineComponent,
getCurrentInstance,
onMounted,
reactive,
ref,
} from 'vue';
import { clearCookie, setCookie } from 'qx-util';
import { useNamespace, useRoute } from '@ibiz-template/vue-util';
import router from '@/router';
import './app-login-extend.scss';
interface LoginData {
username: string;
password: string;
}
const rules = {
username: [
{
required: true,
message: '请输入账号',
trigger: 'blur',
},
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur',
},
{
type: 'string',
min: 6,
message: '密码长度不能少于6位',
trigger: 'blur',
},
],
};
export default defineComponent({
setup() {
const ns = useNamespace('login-extend');
const loginData = reactive<LoginData>({
username: '',
password: '',
});
const formRef = ref<IData | null>(null);
const instance = getCurrentInstance()!;
const route = useRoute(instance.proxy);
const ru = (route.query.ru as string) || '/';
ibiz.appData = undefined;
ibiz.orgData = undefined;
onMounted(() => {
setTimeout(() => {
const el = document.querySelector('.app-loading-x') as HTMLDivElement;
if (el) {
el.style.display = 'none';
}
}, 300);
});
const loading = ref(false);
const onClick = () => {
formRef.value!.validate(async (valid: boolean) => {
if (valid) {
try {
loading.value = true;
clearCookie(CoreConst.TOKEN, true);
let password = loginData.password;
if ((ibiz.env as IData).enableSM3) {
password = ibiz.util.text.sm3(password).toUpperCase();
}
const res = await ibiz.auth.v7login(loginData.username, password);
if (res.ok) {
const { data } = res;
if (data && data.token) {
setCookie(CoreConst.TOKEN, data.token, 0, true);
const expiredDate =
new Date().getTime() + (data.expirein || 7199) * 1000;
setCookie(CoreConst.TOKEN_EXPIRES, `${expiredDate}`, 0, true);
router.push({ path: ru });
return;
}
}
ibiz.notification.error({
title: res.data?.message || '登录失败',
});
loading.value = false;
} catch (error) {
ibiz.notification.error({
title: (error as IData).response?.data?.message || '登录失败',
});
loading.value = false;
}
}
});
};
const onForget = async () => {
const modal = ibiz.overlay.createModal(
'ForgetPassword',
{
dismiss: (result: IData) => {
modal.dismiss(result);
},
},
{
width: 'auto',
placement: 'center',
},
);
modal.present();
await modal.onWillDismiss();
};
return () => (
<div class={ns.b()}>
<div class={ns.b('box')}>
<header class={ns.b('box-header')}>
<img src='./assets/img/login-header.png' />
</header>
<main class={ns.b('box-main')}>
<img
class={ns.be('box-main', 'avatar')}
src='./assets/img/login-avatar.png'
/>
<div class={ns.b('box-main-content')}>
<i-form ref={formRef} props={{ model: loginData, rules }}>
<i-form-item prop='username'>
<i-input
type='text'
value={loginData.username}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
loginData.username = value;
}}
on-on-enter={onClick}
placeholder='请输入账号'
size='large'
>
<i-icon type='md-person' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-form-item prop='password'>
<i-input
type='password'
value={loginData.password}
on-on-change={(evt: InputEvent) => {
const { value } = evt.target as HTMLInputElement;
loginData.password = value;
}}
on-on-enter={onClick}
placeholder='请输入密码'
size='large'
password
>
<i-icon type='ios-unlock' slot='prefix'></i-icon>
</i-input>
</i-form-item>
<i-button type='text' on-click={onForget}>
忘记密码
</i-button>
<i-form-item>
<i-button
shape='circle'
loading={loading.value}
long
on-click={onClick}
>
登录
</i-button>
</i-form-item>
</i-form>
</div>
</main>
</div>
</div>
);
},
});
import { defineComponent } from 'vue';
import AppLoginView from '@/components/login-view/app-login-view/app-login-view';
// import AppLoginView from '@/components/login-view/app-login-view/app-login-view';
import AppLoginExtend from '@/components/login-view/app-login-extend/app-login-extend';
import KqLoginView from '@/components/login-view/kq-login-view/kq-login-view';
export default defineComponent({
......@@ -7,6 +8,6 @@ export default defineComponent({
if (ibiz.env.loginViewName === 'KqLoginView') {
return <KqLoginView></KqLoginView>;
}
return <AppLoginView></AppLoginView>;
return <AppLoginExtend></AppLoginExtend>;
},
});
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册