Skip to content

Commit 7f5c1d8

Browse files
committed
feat: 添加用户密码重置功能并优化普通用户登录后跳转页面
1 parent f56e87b commit 7f5c1d8

File tree

3 files changed

+163
-34
lines changed

3 files changed

+163
-34
lines changed

src/api/system/user.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export function createUser(data) {
5252

5353
// 更新用户(已完成)
5454
export function updateUserById(data) {
55-
5655
return request({
5756
url: '/api/user/update',
5857
method: 'post',
@@ -68,3 +67,12 @@ export function batchDeleteUserByIds(data) {
6867
})
6968
}
7069

70+
// 重置用户密码(已完成)
71+
export function resetPassword(data) {
72+
return request({
73+
url: '/api/user/resetPassword',
74+
method: 'post',
75+
data
76+
})
77+
}
78+

src/permission.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import getPageTitle from '@/utils/get-page-title'
99
NProgress.configure({ showSpinner: false }) // NProgress Configuration
1010

1111
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist 没有重定向白名单
12-
//路由守卫
12+
// 路由守卫
1313
router.beforeEach(async(to, from, next) => {
1414
// start progress bar
1515
NProgress.start()
@@ -34,11 +34,21 @@ router.beforeEach(async(to, from, next) => {
3434
try {
3535
// get user info
3636
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
37-
const { ID, roles } = await store.dispatch('user/getInfo')
38-
const userinfo = { id: ID, roles: roles }
39-
// generate accessible routes map based on roles
37+
const userInfo = await store.dispatch('user/getInfo')
38+
const { ID, roles } = userInfo
39+
const userinfoForRoutes = { id: ID, roles: roles }
40+
41+
// 检查是否为管理员(角色ID为1)
42+
const isAdmin = userInfo.roles && userInfo.roles.some(role => role.ID === 1)
4043

41-
const accessRoutes = await store.dispatch('permission/generateRoutes', userinfo)
44+
// 如果是普通用户且要访问的不是个人主页,则重定向到个人主页
45+
if (!isAdmin && to.path !== '/profile/index') {
46+
next('/profile/index')
47+
return
48+
}
49+
50+
// generate accessible routes map based on roles
51+
const accessRoutes = await store.dispatch('permission/generateRoutes', userinfoForRoutes)
4252

4353
accessRoutes.push({ path: '*', redirect: '/404', hidden: true })
4454
// dynamically add accessible routes
@@ -61,12 +71,12 @@ router.beforeEach(async(to, from, next) => {
6171
/* has no token*/
6272

6373
if (whiteList.indexOf(to.path) !== -1) {
64-
//在免费登录白名单,直接去
74+
// 在免费登录白名单,直接去
6575
next()
66-
}else if(to.path === '/changePass'){
67-
next({replace: true} )
76+
} else if (to.path === '/changePass') {
77+
next({ replace: true })
6878
// NProgress.done()
69-
}else {
79+
} else {
7080
// other pages that do not have permission to access are redirected to the login page.
7181
next(`/login?redirect=${to.path}`)
7282
NProgress.done()

src/views/personnel/user/index.vue

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
<el-card class="container-card" shadow="always">
44
<el-form size="mini" :inline="true" :model="params" class="demo-form-inline">
55
<el-form-item label="用户名">
6-
<el-input style="width: 100px;" v-model.trim="params.username" clearable placeholder="用户名" @keyup.enter.native="search" @clear="search" />
6+
<el-input v-model.trim="params.username" style="width: 100px;" clearable placeholder="用户名" @keyup.enter.native="search" @clear="search" />
77
</el-form-item>
88
<el-form-item label="昵称">
9-
<el-input style="width: 100px;" v-model.trim="params.nickname" clearable placeholder="昵称" @keyup.enter.native="search" @clear="search" />
9+
<el-input v-model.trim="params.nickname" style="width: 100px;" clearable placeholder="昵称" @keyup.enter.native="search" @clear="search" />
1010
</el-form-item>
1111
<el-form-item label="状态">
12-
<el-select style="width: 100px;" v-model.trim="params.status" clearable placeholder="状态" @change="search" @clear="search">
12+
<el-select v-model.trim="params.status" style="width: 100px;" clearable placeholder="状态" @change="search" @clear="search">
1313
<el-option label="正常" value="1" />
1414
<el-option label="禁用" value="2" />
1515
</el-select>
1616
</el-form-item>
17-
<el-form-item label="同步状态">
18-
<el-select style="width: 100px;" v-model.trim="params.syncState" clearable placeholder="同步状态" @change="search" @clear="search">
17+
<el-form-item label="同步状态">
18+
<el-select v-model.trim="params.syncState" style="width: 100px;" clearable placeholder="同步状态" @change="search" @clear="search">
1919
<el-option label="已同步" value="1" />
2020
<el-option label="未同步" value="2" />
2121
</el-select>
@@ -30,7 +30,7 @@
3030
<el-button :disabled="multipleSelection.length === 0" :loading="loading" icon="el-icon-delete" type="danger" @click="batchDelete">批量删除</el-button>
3131
</el-form-item>
3232
<el-form-item>
33-
<el-button :disabled="multipleSelection.length === 0" :loading="loading" icon="el-icon-upload2" type="success" @click="batchSync">批量同步</el-button>
33+
<el-button :disabled="multipleSelection.length === 0" :loading="loading" icon="el-icon-upload2" type="success" @click="batchSync">批量同步</el-button>
3434
</el-form-item>
3535
<br>
3636
<el-form-item>
@@ -73,11 +73,16 @@
7373
<el-table-column show-overflow-tooltip sortable prop="userDn" label="DN" />
7474
<el-table-column show-overflow-tooltip sortable prop="CreatedAt" label="创建时间" />
7575
<el-table-column show-overflow-tooltip sortable prop="UpdatedAt" label="更新时间" />
76-
<el-table-column fixed="right" label="操作" align="center" width="150">
76+
<el-table-column fixed="right" label="操作" align="center" width="190">
7777
<template slot-scope="scope">
7878
<el-tooltip content="编辑" effect="dark" placement="top">
7979
<el-button size="mini" icon="el-icon-edit" circle type="primary" @click="update(scope.row)" />
8080
</el-tooltip>
81+
<el-tooltip class="delete-popover" content="重置密码" effect="dark" placement="top">
82+
<el-popconfirm title="确定重置该用户密码吗?" @onConfirm="resetUserPassword(scope.row.username)">
83+
<el-button slot="reference" size="mini" icon="el-icon-key" circle type="warning" />
84+
</el-popconfirm>
85+
</el-tooltip>
8186
<el-tooltip class="delete-popover" content="删除" effect="dark" placement="top">
8287
<el-popconfirm title="确定删除吗?" @onConfirm="singleDelete(scope.row.ID)">
8388
<el-button slot="reference" size="mini" icon="el-icon-delete" circle type="danger" />
@@ -129,7 +134,7 @@
129134
</el-col>
130135
<el-col :span="12">
131136
<!-- 修改用户时,不显示密码字段 -->
132-
<el-form-item :label="dialogType === 'create' ? '新密码':'重置密码'" prop="password" v-if="dialogType === 'create'">
137+
<el-form-item v-if="dialogType === 'create'" :label="dialogType === 'create' ? '新密码':'重置密码'" prop="password">
133138
<el-input v-model.trim="dialogFormData.password" autocomplete="off" :type="passwordType" :placeholder="dialogType === 'create' ? '新密码':'重置密码'" />
134139
<span class="show-pwd" @click="showPwd">
135140
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
@@ -205,6 +210,50 @@
205210
</div>
206211
</el-dialog>
207212

213+
<!-- 重置密码结果对话框 -->
214+
<el-dialog
215+
title="密码重置成功"
216+
:visible.sync="resetPasswordDialogVisible"
217+
width="400px"
218+
:close-on-click-modal="false"
219+
:close-on-press-escape="false"
220+
@close="closeResetPasswordDialog"
221+
>
222+
<div style="text-align: center;">
223+
<el-alert
224+
title="请保存新密码"
225+
type="warning"
226+
:closable="false"
227+
show-icon
228+
style="margin-bottom: 20px;"
229+
/>
230+
<p style="margin-bottom: 10px; font-weight: bold;">用户:{{ resetUsername }}</p>
231+
<p style="margin-bottom: 20px; color: #606266;">新密码:</p>
232+
<el-input
233+
v-model="newPassword"
234+
readonly
235+
style="margin-bottom: 20px;"
236+
>
237+
<el-button
238+
slot="append"
239+
icon="el-icon-document-copy"
240+
@click="copyPassword"
241+
>
242+
复制
243+
</el-button>
244+
</el-input>
245+
<el-alert
246+
title="请立即保存密码,关闭对话框后将无法再次查看"
247+
type="info"
248+
:closable="false"
249+
show-icon
250+
/>
251+
</div>
252+
<div slot="footer" class="dialog-footer">
253+
<el-button type="primary" @click="closeResetPasswordDialog">我已保存</el-button>
254+
</div>
255+
</el-dialog>
256+
208257
</el-card>
209258
</div>
210259
</template>
@@ -214,6 +263,7 @@ import JSEncrypt from 'jsencrypt'
214263
import Treeselect from '@riophae/vue-treeselect'
215264
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
216265
import { getUsers, createUser, updateUserById, batchDeleteUserByIds, changeUserStatus, syncDingTalkUsersApi, syncWeComUsersApi, syncFeiShuUsersApi, syncOpenLdapUsersApi, syncSqlUsers } from '@/api/personnel/user'
266+
import { resetPassword } from '@/api/system/user'
217267
import { getRoles } from '@/api/system/role'
218268
import { getGroupTree } from '@/api/personnel/group'
219269
import { Message } from 'element-ui'
@@ -342,7 +392,12 @@ export default {
342392
changeUserStatusFormData: {
343393
id: '',
344394
status: ''
345-
}
395+
},
396+
397+
// 重置密码结果对话框
398+
resetPasswordDialogVisible: false,
399+
newPassword: '',
400+
resetUsername: ''
346401
}
347402
},
348403
created() {
@@ -457,14 +512,14 @@ export default {
457512
},
458513
459514
// 判断结果
460-
judgeResult(res){
461-
if (res.code==0){
462-
Message({
463-
showClose: true,
464-
message: "操作成功",
465-
type: 'success'
466-
})
467-
}
515+
judgeResult(res) {
516+
if (res.code === 0) {
517+
Message({
518+
showClose: true,
519+
message: '操作成功',
520+
type: 'success'
521+
})
522+
}
468523
},
469524
470525
// 提交表单
@@ -542,11 +597,11 @@ export default {
542597
}
543598
try {
544599
if (this.dialogType === 'create') {
545-
await createUser(this.dialogFormDataCopy).then(res =>{
600+
await createUser(this.dialogFormDataCopy).then(res => {
546601
this.judgeResult(res)
547602
})
548603
} else {
549-
await updateUserById(this.dialogFormDataCopy).then(res =>{
604+
await updateUserById(this.dialogFormDataCopy).then(res => {
550605
this.judgeResult(res)
551606
})
552607
}
@@ -602,7 +657,7 @@ export default {
602657
userIds.push(x.ID)
603658
})
604659
try {
605-
await batchDeleteUserByIds({ userIds: userIds }).then(res =>{
660+
await batchDeleteUserByIds({ userIds: userIds }).then(res => {
606661
this.judgeResult(res)
607662
})
608663
} finally {
@@ -630,7 +685,7 @@ export default {
630685
userIds.push(x.ID)
631686
})
632687
try {
633-
await syncSqlUsers({ userIds: userIds }).then(res =>{
688+
await syncSqlUsers({ userIds: userIds }).then(res => {
634689
this.judgeResult(res)
635690
})
636691
} finally {
@@ -652,7 +707,6 @@ export default {
652707
this.changeUserStatusFormData.status = userInfo.status
653708
const { code } = await changeUserStatus(this.changeUserStatusFormData)
654709
if (code !== 0) {
655-
userInfo.status = !userInfo.status
656710
return Message.error('更新用户状态失败')
657711
}
658712
Message.success('更新用户状态成功')
@@ -667,7 +721,7 @@ export default {
667721
async singleDelete(Id) {
668722
this.loading = true
669723
try {
670-
await batchDeleteUserByIds({ userIds: [Id] }).then(res =>{
724+
await batchDeleteUserByIds({ userIds: [Id] }).then(res => {
671725
this.judgeResult(res)
672726
})
673727
} finally {
@@ -679,7 +733,7 @@ export default {
679733
async singleSync(Id) {
680734
this.loading = true
681735
try {
682-
await syncSqlUsers({ userIds: [Id] }).then(res =>{
736+
await syncSqlUsers({ userIds: [Id] }).then(res => {
683737
this.judgeResult(res)
684738
})
685739
} finally {
@@ -748,6 +802,63 @@ export default {
748802
this.loading = false
749803
this.getTableData()
750804
})
805+
},
806+
807+
// 重置用户密码
808+
async resetUserPassword(username) {
809+
this.loading = true
810+
try {
811+
const res = await resetPassword({ username: username })
812+
if (res.code === 0) {
813+
this.newPassword = res.data.newPassword
814+
this.resetUsername = username
815+
this.resetPasswordDialogVisible = true
816+
Message({
817+
showClose: true,
818+
message: '密码重置成功',
819+
type: 'success'
820+
})
821+
} else {
822+
Message({
823+
showClose: true,
824+
message: res.msg || '密码重置失败',
825+
type: 'error'
826+
})
827+
}
828+
} finally {
829+
this.loading = false
830+
}
831+
this.getTableData()
832+
},
833+
834+
// 复制密码到剪贴板
835+
copyPassword() {
836+
const textArea = document.createElement('textarea')
837+
textArea.value = this.newPassword
838+
document.body.appendChild(textArea)
839+
textArea.select()
840+
try {
841+
document.execCommand('copy')
842+
Message({
843+
showClose: true,
844+
message: '密码已复制到剪贴板',
845+
type: 'success'
846+
})
847+
} catch (err) {
848+
Message({
849+
showClose: true,
850+
message: '复制失败,请手动复制',
851+
type: 'error'
852+
})
853+
}
854+
document.body.removeChild(textArea)
855+
},
856+
857+
// 关闭重置密码对话框
858+
closeResetPasswordDialog() {
859+
this.resetPasswordDialogVisible = false
860+
this.newPassword = ''
861+
this.resetUsername = ''
751862
}
752863
}
753864
}

0 commit comments

Comments
 (0)