add 新增 密码登录支持OTP验证
This commit is contained in:
parent
3c2d9100b5
commit
b28a3d0b12
@ -39,6 +39,7 @@
|
||||
"jsencrypt": "3.3.2",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"qrcode.vue": "3.4.1",
|
||||
"screenfull": "6.0.2",
|
||||
"vue": "3.4.34",
|
||||
"vue-cropper": "1.1.1",
|
||||
|
@ -72,6 +72,21 @@ export const delUser = (userId: Array<string | number> | string | number) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户OTP秘钥重置
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
export const resetUserOptSecret = (userId: string | number) => {
|
||||
return request({
|
||||
url: '/system/user/resetOtpSecret/' + userId,
|
||||
method: 'put',
|
||||
headers: {
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户密码重置
|
||||
* @param userId 用户ID
|
||||
@ -217,6 +232,7 @@ export default {
|
||||
updateUser,
|
||||
delUser,
|
||||
resetUserPwd,
|
||||
resetUserOptSecret,
|
||||
changeUserStatus,
|
||||
getUserProfile,
|
||||
updateUserProfile,
|
||||
|
@ -39,6 +39,8 @@ export interface UserVO extends BaseEntity {
|
||||
delFlag: string;
|
||||
loginIp: string;
|
||||
loginDate: string;
|
||||
otpSecret: string;
|
||||
otpUrl: string;
|
||||
remark: string;
|
||||
deptName: string;
|
||||
roles: RoleVO[];
|
||||
|
@ -18,6 +18,7 @@ export interface LoginData {
|
||||
tenantId?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
otpCode?: number;
|
||||
rememberMe?: boolean;
|
||||
socialCode?: string;
|
||||
socialState?: string;
|
||||
|
@ -18,6 +18,11 @@
|
||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="otpCode">
|
||||
<el-input v-model="loginForm.otpCode" type="number" size="large" auto-complete="off" placeholder="OTP验证码" @keyup.enter="handleLogin">
|
||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="captchaEnabled" prop="code">
|
||||
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
|
||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
||||
@ -76,6 +81,7 @@ const loginForm = ref<LoginData>({
|
||||
tenantId: '000000',
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
otpCode: NaN,
|
||||
rememberMe: false,
|
||||
code: '',
|
||||
uuid: ''
|
||||
@ -85,6 +91,7 @@ const loginRules: ElFormRules = {
|
||||
tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
|
||||
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
|
||||
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
|
||||
otpCode: [{ required: true, trigger: 'blur', message: '请输入您的OTP验证码' }],
|
||||
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
|
||||
};
|
||||
|
||||
|
@ -91,13 +91,14 @@
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange" @cell-click="cellClickFunc">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
|
||||
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
|
||||
<el-table-column v-if="columns[7].visible" key="otpSecret" label="OTP秘钥" align="center" prop="otpSecret" width="120" />
|
||||
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
@ -123,6 +124,16 @@
|
||||
<el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="重置OTP秘钥" placement="top">
|
||||
<el-button
|
||||
v-hasPermi="['system:user:resetPwd']"
|
||||
link
|
||||
type="primary"
|
||||
icon="Lock"
|
||||
@click="handleResetOtpSecret(scope.row)"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
|
||||
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
@ -281,6 +292,12 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="optSecretVisible" title="OTP秘钥绑定" width="500" append-to-body>
|
||||
<div style="display: flex; justify-content: center">
|
||||
<qrcode-vue :value="otpUrl" :size="150" level="H" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -294,6 +311,7 @@ import { treeselect } from '@/api/system/dept';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import { to } from 'await-to-js';
|
||||
import { optionselect } from '@/api/system/post';
|
||||
import QrcodeVue from 'qrcode.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@ -334,7 +352,8 @@ const columns = ref<FieldOption[]>([
|
||||
{ key: 3, label: `部门`, visible: true, children: [] },
|
||||
{ key: 4, label: `手机号码`, visible: true, children: [] },
|
||||
{ key: 5, label: `状态`, visible: true, children: [] },
|
||||
{ key: 6, label: `创建时间`, visible: true, children: [] }
|
||||
{ key: 6, label: `创建时间`, visible: true, children: [] },
|
||||
{ key: 7, label: `OTP秘钥`, visible: true, children: [] }
|
||||
]);
|
||||
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
@ -431,6 +450,18 @@ watchEffect(
|
||||
}
|
||||
);
|
||||
|
||||
const optSecretVisible = ref(false);
|
||||
const otpUrl = ref(undefined);
|
||||
|
||||
const cellClickFunc = (row, column) => {
|
||||
if (column?.rawColumnKey === 'otpSecret') {
|
||||
otpUrl.value = row.otpUrl;
|
||||
if (otpUrl.value) {
|
||||
optSecretVisible.value = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 查询部门下拉树结构 */
|
||||
const getTreeSelect = async () => {
|
||||
const res = await api.deptTreeSelect();
|
||||
@ -495,6 +526,23 @@ const handleAuthRole = (row: UserVO) => {
|
||||
router.push('/system/user-auth/role/' + userId);
|
||||
};
|
||||
|
||||
/** 重置谷歌密钥按钮操作 */
|
||||
const handleResetOtpSecret = async (row: UserVO) => {
|
||||
const [err, res] = await to(
|
||||
ElMessageBox.confirm('请确认是否重置OTP密钥?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
closeOnClickModal: false
|
||||
})
|
||||
);
|
||||
if (!err && res) {
|
||||
await api.resetUserOptSecret(row.userId);
|
||||
proxy?.$modal.msgSuccess('密钥重置成功');
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
|
||||
/** 重置密码按钮操作 */
|
||||
const handleResetPwd = async (row: UserVO) => {
|
||||
const [err, res] = await to(
|
||||
|
Loading…
x
Reference in New Issue
Block a user