From b28a3d0b12b060c7e843ae47303f8b578a0c3d59 Mon Sep 17 00:00:00 2001 From: knight Date: Mon, 26 Aug 2024 17:38:06 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=20=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=94=AF=E6=8C=81OTP=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/api/system/user/index.ts | 16 ++++++++++ src/api/system/user/types.ts | 2 ++ src/api/types.ts | 1 + src/views/login.vue | 7 +++++ src/views/system/user/index.vue | 52 +++++++++++++++++++++++++++++++-- 6 files changed, 77 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 48b15717..24dd46b2 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index 25c78848..6c04ee8d 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -72,6 +72,21 @@ export const delUser = (userId: Array | 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, diff --git a/src/api/system/user/types.ts b/src/api/system/user/types.ts index 0787372e..89e6741d 100644 --- a/src/api/system/user/types.ts +++ b/src/api/system/user/types.ts @@ -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[]; diff --git a/src/api/types.ts b/src/api/types.ts index 617286c6..9efff620 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -18,6 +18,7 @@ export interface LoginData { tenantId?: string; username?: string; password?: string; + otpCode?: number; rememberMe?: boolean; socialCode?: string; socialState?: string; diff --git a/src/views/login.vue b/src/views/login.vue index ca2903f0..96f4d2c6 100644 --- a/src/views/login.vue +++ b/src/views/login.vue @@ -18,6 +18,11 @@ + + + + + @@ -76,6 +81,7 @@ const loginForm = ref({ 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: '请输入验证码' }] }; diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue index 46bffd2b..7a8000d6 100644 --- a/src/views/system/user/index.vue +++ b/src/views/system/user/index.vue @@ -91,13 +91,14 @@ - + + + + +
+ +
+
@@ -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([ { 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(); @@ -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( -- Gitee