feat: 添加强制密码重置功能及相关组件
This commit is contained in:
@@ -37,3 +37,16 @@ export function updateUserPassword(oldPassword: string, newPassword: string) {
|
||||
data: { oldPassword, newPassword },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户强制重置密码
|
||||
* @param password 密码
|
||||
* @returns object
|
||||
*/
|
||||
export function updateUserPasswordForce(password: string) {
|
||||
return request({
|
||||
url: '/system/user/profile/password-force',
|
||||
method: 'PUT',
|
||||
data: { password },
|
||||
});
|
||||
}
|
||||
|
||||
198
src/components/ForcePasswdChange/index.vue
Normal file
198
src/components/ForcePasswdChange/index.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||
import { regExpPasswd, regExpUserName } from '@/utils/regular-utils';
|
||||
import { Form, message, Modal } from 'ant-design-vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { updateUserPasswordForce } from '@/api/profile';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { getConfigKey } from '@/api/system/config';
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**重置密码框是否显示 */
|
||||
open: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: Record<string, any>;
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
/**密码策略 */
|
||||
passwordPolicy: Record<string, any>;
|
||||
/**密码有效期 */
|
||||
passwdExpire: Record<string, any>;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
open: userStore.forcePasswdChange,
|
||||
title: t('components.ForcePasswdChange.title'),
|
||||
from: {
|
||||
userId: userStore.userId,
|
||||
userName: userStore.userName,
|
||||
password: '',
|
||||
},
|
||||
confirmLoading: false,
|
||||
passwordPolicy: { minLength: 8, specialChars: 2, uppercase: 1, lowercase: 1 },
|
||||
passwdExpire: { expHours: 2, alertHours: 1 },
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from,
|
||||
reactive({
|
||||
userName: [
|
||||
{
|
||||
required: true,
|
||||
pattern: regExpUserName,
|
||||
message: t('views.system.user.userNameTip'),
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
pattern: regExpPasswd,
|
||||
message: t('views.system.user.passwdTip'),
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**对话框提交确认 */
|
||||
function fnModalOk() {
|
||||
const { password } = modalState.from;
|
||||
if (!password) {
|
||||
message.error({
|
||||
content: t('views.system.user.passwdTip'),
|
||||
duration: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
updateUserPasswordForce(password).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
userStore.fnLogOut();
|
||||
Modal.success({
|
||||
title: t('common.tipTitle'),
|
||||
content: t('views.account.settings.submitOkTip', {
|
||||
num: modalState.from.userName,
|
||||
}),
|
||||
okText: t('views.account.settings.submitOk'),
|
||||
onOk() {
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.error({
|
||||
content: `${res.msg}`,
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**组件实例挂载之后调用 */
|
||||
onMounted(() => {
|
||||
Promise.all([
|
||||
getConfigKey('sys.user.passwordPolicy'),
|
||||
getConfigKey('sys.user.passwdExpire'),
|
||||
]).then(resArr => {
|
||||
if (resArr[0].code === RESULT_CODE_SUCCESS) {
|
||||
try {
|
||||
modalState.passwordPolicy = JSON.parse(resArr[0].data);
|
||||
} catch (error) {
|
||||
console.error('passwordPolicy', error);
|
||||
}
|
||||
}
|
||||
if (resArr[1].code === RESULT_CODE_SUCCESS) {
|
||||
try {
|
||||
modalState.passwdExpire = JSON.parse(resArr[1].data);
|
||||
} catch (error) {
|
||||
console.error('passwdExpire', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**组件实例被卸载之后调用 */
|
||||
onUnmounted(() => {});
|
||||
</script>
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="modalState.open"
|
||||
get-container="#app"
|
||||
:footer="null"
|
||||
:zIndex="1008"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
:destroyOnClose="true"
|
||||
:mask-closable="false"
|
||||
:title="modalState.title"
|
||||
>
|
||||
<a-form
|
||||
name="modalStateFromByResetPwd"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 6 }"
|
||||
:label-wrap="true"
|
||||
>
|
||||
<a-form-item
|
||||
:label="t('views.system.user.account')"
|
||||
name="userName"
|
||||
v-bind="modalStateFrom.validateInfos.userName"
|
||||
>
|
||||
<a-input :value="modalState.from.userName" disabled :maxlength="30">
|
||||
<template #prefix>
|
||||
<UserOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="t('views.system.user.loginPwd')"
|
||||
name="password"
|
||||
v-bind="modalStateFrom.validateInfos.password"
|
||||
>
|
||||
<a-input-password
|
||||
v-model:value="modalState.from.password"
|
||||
:maxlength="26"
|
||||
>
|
||||
<template #prefix>
|
||||
<LockOutlined />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item name="ok" :wrapper-col="{ offset: 6 }">
|
||||
<a-button type="primary" @click="fnModalOk()">
|
||||
{{ t('common.ok') }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<a-form-item name="info" :label="t('components.ForcePasswdChange.desc')">
|
||||
<div>
|
||||
<p>
|
||||
1. {{ t('components.ForcePasswdChange.passwordPolicy') }}<br />
|
||||
{{
|
||||
t(
|
||||
'components.ForcePasswdChange.passwordPolicyMsg',
|
||||
modalState.passwordPolicy
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p>
|
||||
2. {{ t('components.ForcePasswdChange.passwdExpire') }}<br />
|
||||
{{
|
||||
t('components.ForcePasswdChange.passwdExpireMsg', {
|
||||
expDay: (modalState.passwdExpire.expHours / 24).toFixed(2),
|
||||
alertDay: (modalState.passwdExpire.alertHours / 24).toFixed(2),
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import RightContent from './components/RightContent.vue';
|
||||
import Tabs from './components/Tabs.vue';
|
||||
import GlobalMask from '@/components/GlobalMask/index.vue';
|
||||
import ForcePasswdChange from '@/components/ForcePasswdChange/index.vue';
|
||||
import { scriptUrl } from '@/assets/js/icon_font_8d5l8fzk5b87iudi';
|
||||
import {
|
||||
computed,
|
||||
@@ -362,6 +363,8 @@ onUnmounted(() => {
|
||||
|
||||
<!-- 全局遮罩 -->
|
||||
<GlobalMask />
|
||||
<!-- 强制密码修改 -->
|
||||
<ForcePasswdChange />
|
||||
</a-watermark>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { parseUrlPath } from '@/plugins/file-static-url';
|
||||
|
||||
/**用户信息类型 */
|
||||
type UserInfo = {
|
||||
/**用户ID */
|
||||
forcePasswdChange: boolean;
|
||||
/**用户ID */
|
||||
userId: string;
|
||||
/**登录账号 */
|
||||
@@ -33,6 +35,7 @@ type UserInfo = {
|
||||
|
||||
const useUserStore = defineStore('user', {
|
||||
state: (): UserInfo => ({
|
||||
forcePasswdChange: false,
|
||||
userId: '',
|
||||
userName: '',
|
||||
roles: [],
|
||||
@@ -104,6 +107,9 @@ const useUserStore = defineStore('user', {
|
||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||
const token = res.data[TOKEN_RESPONSE_FIELD];
|
||||
setToken(token);
|
||||
if (res.data?.forcePasswdChange) {
|
||||
this.forcePasswdChange = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@@ -139,6 +145,10 @@ const useUserStore = defineStore('user', {
|
||||
// }
|
||||
// useLayoutStore().changeWaterMark(waterMarkContent);
|
||||
useLayoutStore().changeWaterMark('');
|
||||
// 强制修改密码
|
||||
if (res.data?.forcePasswdChange) {
|
||||
this.forcePasswdChange = true;
|
||||
}
|
||||
}
|
||||
// 网络错误时退出登录状态
|
||||
if (res.code === 0) {
|
||||
|
||||
Reference in New Issue
Block a user