猛犸系统-用户功能3
用户功能3
用户信息菜单
下拉菜单参考https://element.eleme.cn/#/zh-CN/component/dropdown
src/plugins/element.js
import Vue from 'vue'
import {
Form, FormItem, Input, Button, Message, Container,
Header, Aside, Main, Menu, MenuItem, Submenu, Breadcrumb,
BreadcrumbItem, Card, Row, Col, Table, TableColumn,
Dialog, Pagination, Switch, MessageBox, Tooltip,
DropdownMenu, DropdownItem, Dropdown
} from 'element-ui'
Vue.use(Dropdown)
Vue.use(DropdownMenu)
Vue.use(DropdownItem)
src/views/HomeView.vue
<template>
<el-container>
<el-header>
<div class="logo">
<img src="../assets/logo.png" alt="logo" />
<div class="title">马哥教育猛犸运维系统管理平台</div>
<div><i :class="isCollapsed ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
@click="toggleMenu"></i></div>
</div>
<div class="info">
<el-button type="info" @click="logout">退出</el-button>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">{{ user.username }}<i class="elicon-arrow-down el-icon--right"></i> </span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="chpwd">修改密码</el-dropdown-item>
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-header>
<el-container>
<el-aside :width="isCollapsed ? '64px' : '200px'">
<el-menu
background-color="#123"
text-color="#fff"
active-text-color="#ffd04b"
:collapse="isCollapsed"
:collapse-transition="true"
:router="true"
>
<el-submenu v-for="item in menulist" :index="item.id + ''" :key="item.id">
<template slot="title">
<i class="el-icon-menu"></i><span slot="title">{{ item.itemName }}</span>
</template>
<el-menu-item v-for="subItem in item.children" :index="subItem.path" :key="subItem.id">
<i class="el-icon-menu"></i><span slot="title">{{ subItem.itemName }}</span>
</el-menu-item>
</el-submenu>
<el-menu-item index="/welcome">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
<el-menu-item index="/t1">
<i class="el-icon-setting"></i>
<span slot="title">t1</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
用户信息
登录成功,返回JSON的token,header部分显示用户信息,比如用户id、用户名等。
src/views/HomeView.vue
<script>
export default {
created() {
this.getMenuList() // vue组件创建时查菜单数据
this.getUserInfo()
},
data() {
return {
menulist: [], // 菜单数据
isCollapsed: false,
user: {}, // 用户信息,
chpwdDialogVisible: false // 修改密码对话框
}
},
methods: {
logout() {
window.localStorage.removeItem('token')
this.$router.push('/login')
},
async getMenuList() {
const { data: response } = await this.$http.get('users/menulist/')
if (response.code) {
return this.$message.error(response.message)
}
this.menulist = response // 菜单项数组
},
handleCommand(command) {
if (command === 'logout') {
this.logout()
} else if (command === 'chpwd') {
this.chpwdDialogVisible = true
}
},
async getUserInfo() {
// 带着token发起请求,后台查询用户数据
const { data: response } = await this.$http.get('users/whoami/')
if (response.code) {
return this.$message.error(response.message)
}
this.user = response.user
},
changePassword() {}
}
}
</script>
后端
user/views.py
from rest_framework.views import Response, Request
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.viewsets import ModelViewSet
from django.contrib.auth import get_user_model
from .models import UserProfile
from .serializers import UserSerializer
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from django.http.response import Http404
class UserViewSet(ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
# permission_classes = [IsAdminUser] # 必须是管理员才能管理用户
filter_backends = [filters.SearchFilter] # 指定filter
# filterset_fields = ['username']
search_fields = ['username']
# 详情页禁止修改username,如果提供用户名,它就要验证用户名唯一性,且尝试修改用户名
def partial_update(self, request, *args, **kwargs):
request.data.pop('username', None) # 剔除不要更新的字段
request.data.pop('id', None)
request.data.pop('password', None)
return super().partial_update(request, *args, **kwargs)
def get_object(self):
if self.request.method.lower() != 'get':
pk = self.kwargs.get('pk')
if pk == 1 or pk == '1':
raise Http404
return super().get_object()
@action(['GET'], detail=False, url_path='whoami')
def whoami(self, request): # detail=False,非详情页
print(request.user)
return Response({
'user':{
'id': request.user.id,
'username': request.user.username
}
})
密码修改
使用对话框,提供当前密码和新密码
src/views/HomeView.vue
<template>
<el-container>
<el-header>
<div class="logo">
<img src="../assets/logo.png" alt="logo" />
<div class="title">马哥教育猛犸运维系统管理平台</div>
<div><i :class="isCollapsed ? 'el-icon-s-unfold' : 'el-icon-sfold'" @click="isCollapsed = !isCollapsed"></i></div>
</div>
<div class="info">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">{{ user.username }}<i class="elicon-arrow-down el-icon--right"></i> </span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="chpwd">修改密码</el-dropdown-item>
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<!-- 修改密码对话框 -->
<el-dialog title="修改密码" :visible.sync="chpwdDialogVisible"
@close="resetForm('chpwd')">
<el-form :model="chpwdForm" :rules="chpwdRules" ref="chpwd" labelwidth="100px">
<el-form-item label="用户名">{{ user.username }}</el-form-item>
<el-form-item label="旧密码" prop="oldPassword">
<el-input type="password" v-model="chpwdForm.oldPassword"></elinput>
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input type="password" v-model="chpwdForm.password"></elinput>
</el-form-item>
<el-form-item label="确认新密码" prop="checkPass">
<el-input type="password" v-model="chpwdForm.checkPass"></elinput>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="chpwdDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="changePassword">确 定</elbutton>
</span>
</el-dialog>
</el-header>
<el-container>
<el-aside :width="isCollapsed ? '64px' : '200px'">
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
created() {
this.getMenulist() // vue组件创建时查菜单数据
this.getUserInfo()
},
data() {
const validatePass = (rule, value, callback) => {
if (value !== this.chpwdForm.password) {
callback(new Error('两次输入密码不一致!'))
} else {
callback()
}
}
return {
menulist: [], // 菜单数据
isCollapsed: false,
user: {}, // 用户信息,
chpwdDialogVisible: false, // 修改密码对话框
chpwdForm: {
oldPassword: '',
password: '',
checkPass: ''
},
chpwdRules: {
oldPassword: [
{ required: true, message: '请输入旧密码', trigger: 'blur' },
{ min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger:
'blur' }
],
password: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 4, max: 16, message: '长度在 4 到 16 个字符', trigger:
'blur' }
],
checkPass: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ validator: validatePass, trigger: 'blur' }
]
}
}
},
methods: {
async getMenulist() {
const { data: response } = await this.$http.get('users/menulist/')
if (response.code) {
return this.$message.error(response.message)
}
this.menulist = response.data
},
logout() {
window.localStorage.removeItem('token')
this.$router.push('/login')
},
handleCommand(command) {
if (command === 'logout') {
this.logout()
} else if (command === 'chpwd') {
this.chpwdDialogVisible = true
}
},
async getUserInfo() {
// 带着token发起请求,后台查询用户数据
const { data: response } = await this.$http.get('users/whoami/')
if (response.code) {
return this.$message.error(response.message)
}
this.user = response.user
},
async changePassword() {
// 后台要验证旧密码
const { data: response } = await
this.$http.post(`users/${this.user.id}/setpwd/`, this.chpwdForm)
if (response.code) {
return this.$message.error(response.message)
}
this.chpwdDialogVisible = false
this.$message('密码修改成功')
},
resetForm(name) {
this.$refs[name].resetFields()
}
}
}
</script>
当前用户修改密码,不要轻信user_id,在后端,一定要使用认证后的request.user。要求不能修改id为1的管理员密码
utils/exceptions.py
class InvalidPassword(MagBaseException):
code = 101
message = '密码验证错误'
user/view.py完整代码
from rest_framework.views import Response, Request
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.viewsets import ModelViewSet
from django.contrib.auth import get_user_model
from .models import UserProfile
from .serializers import UserSerializer
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from django.http.response import Http404
from utils.exceptions import InvalidPassword
class UserViewSet(ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
# permission_classes = [IsAdminUser] # 必须是管理员才能管理用户
filter_backends = [filters.SearchFilter] # 指定filter
# filterset_fields = ['username']
search_fields = ['username']
# 详情页禁止修改username,如果提供用户名,它就要验证用户名唯一性,且尝试修改用户名
def partial_update(self, request, *args, **kwargs):
request.data.pop('username', None) # 剔除不要更新的字段
request.data.pop('id', None)
request.data.pop('password', None)
return super().partial_update(request, *args, **kwargs)
def get_object(self):
if self.request.method.lower() != 'get':
pk = self.kwargs.get('pk')
if pk == 1 or pk == '1':
print('竟敢修改管理员!!!!')
raise Http404
return super().get_object()
@action(['GET'], detail=False, url_path='whoami')
def whoami(self, request): # detail=False,非详情页
print(request.user)
return Response({
'user':{
'id': request.user.id,
'username': request.user.username
}
})
@action(detail=True, methods=['post'], url_path='setpwd')
def set_password(self, request, pk=None):
user = self.get_object() # 能不能使用request.user,为什么
# {'oldPassword': 'adminadmin', 'password': 'admin', 'checkPass': 'admin'}
if user.check_password(request.data['oldPassword']):
user.set_password(request.data['password'])
user.save()
return Response()
else:
raise InvalidPassword
class MenuItem(dict):
def __init__(self, id, name, path=None):
super().__init__() # 你就是字典
self['id'] = id
self['name'] = name
self['path'] = path
# self['children'] = []
def append(self, item):
# self['children'].append(item)
self.setdefault('children', []).append(item)
@api_view(['GET'])
@permission_classes([IsAuthenticated, IsAdminUser]) # 覆盖全局配置
def menulist_view(request:Request):
print(request.user, request.auth) # User
print(dir(request.user)) # is_authenticated is_superuser
menulist = []
if request.user.is_superuser:
m1 = MenuItem(1, '用户管理')
m1.append(MenuItem(101, '用户列表', '/users'))
m1.append(MenuItem(102, '角色列表', '/users/roles'))
m1.append(MenuItem(103, '权限列表', '/users/perms'))
menulist.append(m1)
return Response(menulist)
set_password 中不使用request.user,原因是因为request.user 表示当前登录用户,但如果是当前登录用户重置其他用户密码,换id就可以了。
再一个,由于重写get_object,对id为1的用户不能修改密码,也是一种保护,至少有一个用户不能被修改密码。
提交合并代码
前台代码提交
$ git add .
$ git commit -m "User finished"
$ git push -u origin user
$ git checkout master
$ git merge user
$ git push
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 J.のblog!
评论