新功能: 密码重置功能
parent
48f18d7f1c
commit
7706d6db84
|
@ -41,6 +41,7 @@ urlpatterns = [
|
|||
path('menu/web_router/', MenuViewSet.as_view({'get': 'web_router'})),
|
||||
path('user/user_info/', UserViewSet.as_view({'get': 'user_info', 'put': 'update_user_info'})),
|
||||
path('user/change_password/<int:pk>/', UserViewSet.as_view({'put': 'change_password'})),
|
||||
path('user/reset_password/<int:pk>/', UserViewSet.as_view({'put': 'reset_password'})),
|
||||
path('user/export/', UserViewSet.as_view({'post': 'export_data', })),
|
||||
path('user/import/',UserViewSet.as_view({'get': 'import_data', 'post': 'import_data'})),
|
||||
path('system_config/save_content/', SystemConfigViewSet.as_view({'put': 'save_content'})),
|
||||
|
|
|
@ -11,6 +11,7 @@ import hashlib
|
|||
from django.contrib.auth.hashers import make_password
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from dvadmin.system.models import Users
|
||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
||||
|
@ -147,7 +148,7 @@ class UserViewSet(CustomModelViewSet):
|
|||
'gender': '用户性别(男/女/未知)',
|
||||
'is_active': '帐号状态(启用/禁用)', 'password': '登录密码', 'dept': '部门ID', 'role': '角色ID'}
|
||||
|
||||
@action(methods=['GET'], detail=True, permission_classes=[])
|
||||
@action(methods=['GET'], detail=True, permission_classes=[IsAuthenticated])
|
||||
def user_info(self, request):
|
||||
"""获取当前用户信息"""
|
||||
user = request.user
|
||||
|
@ -159,14 +160,14 @@ class UserViewSet(CustomModelViewSet):
|
|||
}
|
||||
return DetailResponse(data=result, msg="获取成功")
|
||||
|
||||
@action(methods=['PUT'], detail=True, permission_classes=[])
|
||||
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated])
|
||||
def update_user_info(self, request):
|
||||
"""修改当前用户信息"""
|
||||
user = request.user
|
||||
Users.objects.filter(id=user.id).update(**request.data)
|
||||
return DetailResponse(data=None, msg="修改成功")
|
||||
|
||||
@action(methods=['PUT'], detail=True, permission_classes=[])
|
||||
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated])
|
||||
def change_password(self, request, *args, **kwargs):
|
||||
"""密码修改"""
|
||||
instance = Users.objects.filter(id=kwargs.get('pk')).first()
|
||||
|
@ -185,3 +186,22 @@ class UserViewSet(CustomModelViewSet):
|
|||
return ErrorResponse(msg="旧密码不正确")
|
||||
else:
|
||||
return ErrorResponse(msg="未获取到用户")
|
||||
|
||||
@action(methods=['PUT'], detail=True)
|
||||
def reset_password(self, request, pk):
|
||||
"""
|
||||
密码重置
|
||||
"""
|
||||
instance = Users.objects.filter(id=pk).first()
|
||||
data = request.data
|
||||
new_pwd = data.get('newPassword')
|
||||
new_pwd2 = data.get('newPassword2')
|
||||
if instance:
|
||||
if new_pwd != new_pwd2:
|
||||
return ErrorResponse(msg="两次密码不匹配")
|
||||
else:
|
||||
instance.password = make_password(new_pwd)
|
||||
instance.save()
|
||||
return DetailResponse(data=None, msg="修改成功")
|
||||
else:
|
||||
return ErrorResponse(msg="未获取到用户")
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 联系Qq:1638245306
|
||||
* @文件介绍: 用户接口
|
||||
*/
|
||||
import { request, downloadFile } from '@/api/service'
|
||||
import { request } from '@/api/service'
|
||||
|
||||
export const urlPrefix = '/api/system/user/'
|
||||
|
||||
|
@ -43,13 +43,15 @@ export function DelObj (id) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
* @param params
|
||||
* 重置密码
|
||||
* @param id
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
export function exportData (params) {
|
||||
return downloadFile({
|
||||
url: urlPrefix + 'export/',
|
||||
params: params,
|
||||
method: 'post'
|
||||
export function ResetPwd (obj) {
|
||||
return request({
|
||||
url: urlPrefix + 'reset_password/' + obj.id + '/',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import { request } from '@/api/service'
|
|||
import { BUTTON_STATUS_BOOL } from '@/config/button'
|
||||
import { urlPrefix as deptPrefix } from '../dept/api'
|
||||
import util from '@/libs/util'
|
||||
import XEUtils from 'xe-utils'
|
||||
|
||||
const uploadUrl = util.baseURL() + 'api/system/img/'
|
||||
export const crudOptions = (vm) => {
|
||||
|
@ -14,8 +13,7 @@ export const crudOptions = (vm) => {
|
|||
height: '100%'
|
||||
},
|
||||
rowHandle: {
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
width: 180,
|
||||
view: {
|
||||
thin: true,
|
||||
text: '',
|
||||
|
@ -36,7 +34,21 @@ export const crudOptions = (vm) => {
|
|||
disabled () {
|
||||
return !vm.hasPermissions('Delete')
|
||||
}
|
||||
}
|
||||
},
|
||||
custom: [
|
||||
{
|
||||
thin: true,
|
||||
text: '',
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
icon: 'el-icon-refresh-left',
|
||||
show () {
|
||||
return vm.hasPermissions('ResetPwd')
|
||||
},
|
||||
emit: 'resetPwd'
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
viewOptions: {
|
||||
componentType: 'form'
|
||||
|
@ -47,7 +59,7 @@ export const crudOptions = (vm) => {
|
|||
indexRow: { // 或者直接传true,不显示title,不居中
|
||||
title: '序号',
|
||||
align: 'center',
|
||||
width: 80
|
||||
width: 100
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
|
@ -87,10 +99,7 @@ export const crudOptions = (vm) => {
|
|||
type: 'input',
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '账号必填项'
|
||||
}
|
||||
{ required: true, message: '账号必填项' }
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入账号'
|
||||
|
@ -110,16 +119,12 @@ export const crudOptions = (vm) => {
|
|||
title: '姓名',
|
||||
key: 'name',
|
||||
search: {
|
||||
key: 'name__icontains',
|
||||
disabled: false
|
||||
},
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '姓名必填项'
|
||||
}
|
||||
{ required: true, message: '姓名必填项' }
|
||||
],
|
||||
component: {
|
||||
span: 12,
|
||||
|
@ -132,7 +137,6 @@ export const crudOptions = (vm) => {
|
|||
},
|
||||
{
|
||||
title: '部门',
|
||||
width: 160,
|
||||
key: 'dept',
|
||||
search: {
|
||||
disabled: true
|
||||
|
@ -143,29 +147,18 @@ export const crudOptions = (vm) => {
|
|||
url: deptPrefix,
|
||||
value: 'id', // 数据字典中value字段的属性名
|
||||
label: 'name', // 数据字典中label字段的属性名
|
||||
isTree: true,
|
||||
getData: (url, dict, {
|
||||
_,
|
||||
component
|
||||
}) => {
|
||||
return request({
|
||||
url: url,
|
||||
params: {
|
||||
page: 1,
|
||||
limit: 999,
|
||||
status: 1
|
||||
}
|
||||
}).then(ret => {
|
||||
getData: (url, dict, { form, component }) => {
|
||||
return request({ url: url, params: { page: 1, limit: 10, status: 1 } }).then(ret => {
|
||||
component._elProps.page = ret.data.page
|
||||
component._elProps.limit = ret.data.limit
|
||||
component._elProps.total = ret.data.total
|
||||
return ret.data.data
|
||||
})
|
||||
}
|
||||
},
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
{ required: true, message: '必填项' }
|
||||
],
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
|
@ -174,20 +167,14 @@ export const crudOptions = (vm) => {
|
|||
span: 12,
|
||||
props: { multiple: false },
|
||||
elProps: {
|
||||
treeConfig: {
|
||||
transform: true,
|
||||
rowField: 'id',
|
||||
parentField: 'parent',
|
||||
expandAll: true
|
||||
},
|
||||
pagination: true,
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '部门名称',
|
||||
treeNode: true
|
||||
title: '部门名称'
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
field: 'status_label',
|
||||
title: '状态'
|
||||
},
|
||||
{
|
||||
|
@ -199,90 +186,21 @@ export const crudOptions = (vm) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key: 'role',
|
||||
width: 160,
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
type: 'table-selector',
|
||||
dict: {
|
||||
cache: false,
|
||||
url: '/api/system/role/',
|
||||
value: 'id', // 数据字典中value字段的属性名
|
||||
label: 'name', // 数据字典中label字段的属性名
|
||||
getData: (url, dict, {
|
||||
form,
|
||||
component
|
||||
}) => {
|
||||
return request({
|
||||
url: url,
|
||||
params: {
|
||||
page: 1,
|
||||
limit: 10
|
||||
}
|
||||
}).then(ret => {
|
||||
component._elProps.page = ret.data.page
|
||||
component._elProps.limit = ret.data.limit
|
||||
component._elProps.total = ret.data.total
|
||||
return ret.data.data
|
||||
})
|
||||
}
|
||||
},
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{
|
||||
required: true,
|
||||
message: '必填项'
|
||||
}
|
||||
],
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
},
|
||||
component: {
|
||||
span: 12,
|
||||
props: { multiple: true },
|
||||
elProps: {
|
||||
pagination: true,
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '角色名称'
|
||||
},
|
||||
{
|
||||
field: 'key',
|
||||
title: '权限标识'
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
key: 'mobile',
|
||||
width: 120,
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
type: 'input',
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
max: 20,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
pattern: /^1[3|4|5|7|8]\d{9}$/,
|
||||
message: '请输入正确的手机号码'
|
||||
}
|
||||
{ max: 20, message: '请输入正确的手机号码', trigger: 'blur' },
|
||||
{ pattern: /^1[3|4|5|7|8]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
],
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
},
|
||||
component: {
|
||||
placeholder: '请输入手机号码'
|
||||
}
|
||||
|
@ -290,14 +208,9 @@ export const crudOptions = (vm) => {
|
|||
}, {
|
||||
title: '邮箱',
|
||||
key: 'email',
|
||||
width: 120,
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
|
||||
],
|
||||
component: {
|
||||
placeholder: '请输入邮箱'
|
||||
|
@ -307,57 +220,18 @@ export const crudOptions = (vm) => {
|
|||
{
|
||||
title: '性别',
|
||||
key: 'gender',
|
||||
type: 'select',
|
||||
type: 'radio',
|
||||
dict: {
|
||||
data: [{
|
||||
label: '男',
|
||||
value: 1
|
||||
}, {
|
||||
label: '女',
|
||||
value: 0
|
||||
}]
|
||||
data: [{ label: '男', value: 1 }, { label: '女', value: 0 }]
|
||||
},
|
||||
form: {
|
||||
value: 1,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '性别必填项'
|
||||
}
|
||||
],
|
||||
component: {
|
||||
span: 12
|
||||
},
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
}
|
||||
},
|
||||
component: { props: { color: 'auto' } } // 自动染色
|
||||
},
|
||||
{
|
||||
title: '用户类型',
|
||||
key: 'user_type',
|
||||
type: 'select',
|
||||
width: 120,
|
||||
search: {
|
||||
key: 'user_type',
|
||||
value: 0,
|
||||
disabled: false
|
||||
},
|
||||
dict: {
|
||||
data: [{
|
||||
label: '前台用户',
|
||||
value: 1
|
||||
}, {
|
||||
label: '后台用户',
|
||||
value: 0
|
||||
}]
|
||||
},
|
||||
form: {
|
||||
disabled: true
|
||||
},
|
||||
component: { props: { color: 'auto' } } // 自动染色
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'is_active',
|
||||
|
@ -380,7 +254,7 @@ export const crudOptions = (vm) => {
|
|||
title: '头像',
|
||||
key: 'avatar',
|
||||
type: 'avatar-uploader',
|
||||
width: 80,
|
||||
width: 100,
|
||||
align: 'left',
|
||||
form: {
|
||||
component: {
|
||||
|
@ -393,13 +267,10 @@ export const crudOptions = (vm) => {
|
|||
},
|
||||
type: 'form',
|
||||
successHandle (ret, option) {
|
||||
if (ret.data == null || ret.data === '') {
|
||||
if (ret.data === null || ret.data === '') {
|
||||
throw new Error('上传失败')
|
||||
}
|
||||
return {
|
||||
url: ret.data.data.url,
|
||||
key: option.data.key
|
||||
}
|
||||
return { url: ret.data.data.url, key: option.data.key }
|
||||
}
|
||||
},
|
||||
elProps: { // 与el-uploader 配置一致
|
||||
|
@ -432,7 +303,58 @@ export const crudOptions = (vm) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key: 'role',
|
||||
search: {
|
||||
disabled: true
|
||||
},
|
||||
type: 'table-selector',
|
||||
dict: {
|
||||
cache: false,
|
||||
url: '/api/system/role/',
|
||||
value: 'id', // 数据字典中value字段的属性名
|
||||
label: 'name', // 数据字典中label字段的属性名
|
||||
getData: (url, dict, { form, component }) => {
|
||||
return request({ url: url, params: { page: 1, limit: 10 } }).then(ret => {
|
||||
component._elProps.page = ret.data.page
|
||||
component._elProps.limit = ret.data.limit
|
||||
component._elProps.total = ret.data.total
|
||||
return ret.data.data
|
||||
})
|
||||
}
|
||||
},
|
||||
form: {
|
||||
rules: [ // 表单校验规则
|
||||
{ required: true, message: '必填项' }
|
||||
],
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
},
|
||||
component: {
|
||||
span: 12,
|
||||
props: { multiple: true },
|
||||
elProps: {
|
||||
pagination: true,
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '角色名称'
|
||||
},
|
||||
{
|
||||
field: 'key',
|
||||
title: '权限标识'
|
||||
},
|
||||
{
|
||||
field: 'status_label',
|
||||
title: '状态'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
].concat(vm.commonEndColumns({ update_datetime: { showTable: false } }))
|
||||
].concat(vm.commonEndColumns({ show_create_datetime: false }))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
<!--
|
||||
* @创建文件时间: 2021-06-01 22:41:21
|
||||
* @Auther: 猿小天
|
||||
* @最后修改人: 猿小天
|
||||
* @最后修改时间: 2021-07-29 19:27:10
|
||||
* 联系Qq:1638245306
|
||||
* @文件介绍: 用户管理
|
||||
-->
|
||||
<template>
|
||||
<d2-container :class="{ 'page-compact': crud.pageOptions.compact }">
|
||||
<d2-crud-x
|
||||
|
@ -13,6 +5,7 @@
|
|||
v-bind="_crudProps"
|
||||
v-on="_crudListeners"
|
||||
crud.options.tableType="vxe-table"
|
||||
@resetPwd="resetPwd"
|
||||
>
|
||||
<div slot="header">
|
||||
<crud-search
|
||||
|
@ -26,20 +19,8 @@
|
|||
v-permission="'Create'"
|
||||
type="primary"
|
||||
@click="addRow"
|
||||
><i class="el-icon-plus"/> 新增
|
||||
</el-button
|
||||
><i class="el-icon-plus" /> 新增</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="onExport"
|
||||
v-permission="'Export'"
|
||||
><i class="el-icon-download"/> 导出
|
||||
</el-button>
|
||||
<importExcel
|
||||
importApi="/api/system/user/import/"
|
||||
v-permission="'Import'">导入
|
||||
</importExcel>
|
||||
</el-button-group>
|
||||
<crud-toolbar
|
||||
:search.sync="crud.searchOptions.show"
|
||||
|
@ -50,6 +31,20 @@
|
|||
/>
|
||||
</div>
|
||||
</d2-crud-x>
|
||||
<el-dialog title="密码重置" :visible.sync="dialogFormVisible" :close-on-click-modal="false">
|
||||
<el-form :model="resetPwdForm" ref="resetPwdForm" :rules="passwordRules">
|
||||
<el-form-item label="密码" prop="pwd">
|
||||
<el-input v-model="resetPwdForm.pwd" type="password" show-password clearable autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="再次输入密码" prop="pwd2">
|
||||
<el-input v-model="resetPwdForm.pwd2" type="password" show-password clearable autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="resetPwdSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</d2-container>
|
||||
</template>
|
||||
|
||||
|
@ -60,10 +55,42 @@ import { d2CrudPlus } from 'd2-crud-plus'
|
|||
|
||||
export default {
|
||||
name: 'user',
|
||||
|
||||
mixins: [d2CrudPlus.crud],
|
||||
data () {
|
||||
return {}
|
||||
var validatePass = (rule, value, callback) => {
|
||||
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}')
|
||||
if (value === '') {
|
||||
callback(new Error('请输入密码'))
|
||||
} else if (!pwdRegex.test(value)) {
|
||||
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)'))
|
||||
} else {
|
||||
if (this.resetPwdForm.pwd2 !== '') {
|
||||
this.$refs.resetPwdForm.validateField('pwd2')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
var validatePass2 = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== this.resetPwdForm.pwd) {
|
||||
callback(new Error('两次输入密码不一致!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
resetPwdForm: {
|
||||
id: null,
|
||||
pwd: null,
|
||||
pwd2: null
|
||||
},
|
||||
passwordRules: {
|
||||
pwd: [{ required: true, message: '必填项' }, { validator: validatePass, trigger: 'blur' }],
|
||||
pwd2: [{ required: true, message: '必填项' }, { validator: validatePass2, trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCrudOptions () {
|
||||
|
@ -76,19 +103,38 @@ export default {
|
|||
return api.AddObj(row)
|
||||
},
|
||||
updateRequest (row) {
|
||||
console.log('----', row)
|
||||
return api.UpdateObj(row)
|
||||
},
|
||||
delRequest (row) {
|
||||
return api.DelObj(row.id)
|
||||
},
|
||||
onExport () {
|
||||
this.$confirm('是否确认导出所有数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(function () {
|
||||
return api.exportData()
|
||||
// 重置密码弹框
|
||||
resetPwd ({ row }) {
|
||||
this.dialogFormVisible = true
|
||||
this.resetPwdForm.id = row.id
|
||||
},
|
||||
// 重置密码确认
|
||||
resetPwdSubmit () {
|
||||
const that = this
|
||||
that.$refs.resetPwdForm.validate((valid) => {
|
||||
if (valid) {
|
||||
const params = {
|
||||
id: that.resetPwdForm.id,
|
||||
newPassword: that.$md5(that.resetPwdForm.pwd),
|
||||
newPassword2: that.$md5(that.resetPwdForm.pwd2)
|
||||
}
|
||||
api.ResetPwd(params).then(res => {
|
||||
that.dialogFormVisible = false
|
||||
that.resetPwdForm = {
|
||||
id: null,
|
||||
pwd: null,
|
||||
pwd2: null
|
||||
}
|
||||
that.$message.success('修改成功')
|
||||
})
|
||||
} else {
|
||||
that.$message.error('表单校验失败,请检查')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue