Merge remote-tracking branch 'origin/v2.x' into v2.x

pull/82/head
猿小天 2022-12-03 21:13:48 +08:00
commit 256b13bc02
13 changed files with 135 additions and 106 deletions

View File

@ -5,7 +5,7 @@ from django_restql.fields import DynamicSerializerMethodField
from rest_framework import serializers from rest_framework import serializers
from rest_framework.decorators import action, permission_classes from rest_framework.decorators import action, permission_classes
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from django.db import connection
from application import dispatch from application import dispatch
from dvadmin.system.models import Users, Role, Dept from dvadmin.system.models import Users, Role, Dept
from dvadmin.system.views.role import RoleSerializer from dvadmin.system.views.role import RoleSerializer
@ -15,17 +15,18 @@ from dvadmin.utils.validator import CustomUniqueValidator
from dvadmin.utils.viewset import CustomModelViewSet from dvadmin.utils.viewset import CustomModelViewSet
def recursion(instance,parent,result): def recursion(instance, parent, result):
new_instance = getattr(instance,parent,None) new_instance = getattr(instance, parent, None)
res = [] res = []
data = getattr(instance, result, None) data = getattr(instance, result, None)
if data: if data:
res.append(data) res.append(data)
if new_instance: if new_instance:
array = recursion(new_instance,parent,result) array = recursion(new_instance, parent, result)
res+=(array) res += (array)
return res return res
class UserSerializer(CustomModelSerializer): class UserSerializer(CustomModelSerializer):
""" """
用户管理-序列化器 用户管理-序列化器
@ -63,12 +64,13 @@ class UsersInitSerializer(CustomModelSerializer):
""" """
初始化获取数信息(用于生成初始化json文件) 初始化获取数信息(用于生成初始化json文件)
""" """
def save(self, **kwargs): def save(self, **kwargs):
instance = super().save(**kwargs) instance = super().save(**kwargs)
role_key = self.initial_data.get('role_key',[]) role_key = self.initial_data.get('role_key', [])
role_ids = Role.objects.filter(key__in=role_key).values_list('id',flat=True) role_ids = Role.objects.filter(key__in=role_key).values_list('id', flat=True)
instance.role.set(role_ids) instance.role.set(role_ids)
dept_key = self.initial_data.get('dept_key',None) dept_key = self.initial_data.get('dept_key', None)
dept_id = Dept.objects.filter(key=dept_key).first() dept_id = Dept.objects.filter(key=dept_key).first()
instance.dept = dept_id instance.dept = dept_id
instance.save() instance.save()
@ -162,6 +164,29 @@ class UserUpdateSerializer(CustomModelSerializer):
} }
class UserInfoUpdateSerializer(CustomModelSerializer):
"""
用户修改-序列化器
"""
mobile = serializers.CharField(
max_length=50,
validators=[
CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
],
allow_blank=True
)
def update(self, instance, validated_data):
return super().update(instance, validated_data)
class Meta:
model = Users
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
extra_kwargs = {
"post": {"required": False, "read_only": True},
}
class ExportUserProfileSerializer(CustomModelSerializer): class ExportUserProfileSerializer(CustomModelSerializer):
""" """
用户导出 序列化器 用户导出 序列化器
@ -242,15 +267,15 @@ class UserViewSet(CustomModelViewSet):
search_fields = ["username", "name", "gender", "dept__name", "role__name"] search_fields = ["username", "name", "gender", "dept__name", "role__name"]
# 导出 # 导出
export_field_label = { export_field_label = {
"username":"用户账号", "username": "用户账号",
"name":"用户名称", "name": "用户名称",
"email":"用户邮箱", "email": "用户邮箱",
"mobile":"手机号码", "mobile": "手机号码",
"gender":"用户性别", "gender": "用户性别",
"is_active":"帐号状态", "is_active": "帐号状态",
"last_login":"最后登录时间", "last_login": "最后登录时间",
"dept_name":"部门名称", "dept_name": "部门名称",
"dept_owner":"部门负责人", "dept_owner": "部门负责人",
} }
export_serializer_class = ExportUserProfileSerializer export_serializer_class = ExportUserProfileSerializer
# 导入 # 导入
@ -283,6 +308,7 @@ class UserViewSet(CustomModelViewSet):
user = request.user user = request.user
result = { result = {
"id": user.id, "id": user.id,
"username": user.username,
"name": user.name, "name": user.name,
"mobile": user.mobile, "mobile": user.mobile,
"user_type": user.user_type, "user_type": user.user_type,
@ -293,6 +319,9 @@ class UserViewSet(CustomModelViewSet):
"is_superuser": user.is_superuser, "is_superuser": user.is_superuser,
"role": user.role.values_list('id', flat=True), "role": user.role.values_list('id', flat=True),
} }
if hasattr(connection, 'tenant'):
result['tenant_id'] = connection.tenant and connection.tenant.id
result['tenant_name'] = connection.tenant and connection.tenant.name
dept = getattr(user, 'dept', None) dept = getattr(user, 'dept', None)
if dept: if dept:
result['dept_info'] = { result['dept_info'] = {
@ -307,8 +336,9 @@ class UserViewSet(CustomModelViewSet):
@action(methods=["PUT"], detail=False, permission_classes=[IsAuthenticated]) @action(methods=["PUT"], detail=False, permission_classes=[IsAuthenticated])
def update_user_info(self, request): def update_user_info(self, request):
"""修改当前用户信息""" """修改当前用户信息"""
user = request.user serializer = UserInfoUpdateSerializer(request.user, data=request.data, request=request)
Users.objects.filter(id=user.id).update(**request.data) serializer.is_valid(raise_exception=True)
serializer.save()
return DetailResponse(data=None, msg="修改成功") return DetailResponse(data=None, msg="修改成功")
@action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated]) @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated])

View File

@ -14,29 +14,20 @@ server {
} }
location /api/ { location /api/ {
proxy_http_version 1.1;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
rewrite ^/api/(.*)$ /$1 break; #重写
proxy_pass http://177.8.0.12:8000/; # 设置代理服务器的协议和地址
}
location /api/ws {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Nginx-Proxy true; proxy_set_header X-Nginx-Proxy true;
set_real_ip_from 0.0.0.0/0; set_real_ip_from 0.0.0.0/0;
proxy_redirect off;
client_max_body_size 10m;
proxy_pass http://177.8.0.12:8000;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_connect_timeout 600s; proxy_connect_timeout 600s;
proxy_read_timeout 600s; proxy_read_timeout 600s;
proxy_send_timeout 600s; proxy_send_timeout 600s;
real_ip_header X-Forwarded-For;
rewrite ^/api/(.*)$ /$1 break; #重写
proxy_pass http://177.8.0.12:8000/; # 设置代理服务器的协议和地址
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "django-vue-admin", "name": "django-vue-admin",
"version": "2.0.6", "version": "2.0.7",
"scripts": { "scripts": {
"serve": "vue-cli-service serve --open", "serve": "vue-cli-service serve --open",
"start": "npm run serve", "start": "npm run serve",
@ -13,7 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@great-dream/template": "^1.0.2", "@great-dream/template": "^1.0.2",
"@vue/composition-api": "^1.0.4", "@vue/composition-api": "1.0.4",
"axios": "^0.19.0", "axios": "^0.19.0",
"axios-mock-adapter": "^1.18.1", "axios-mock-adapter": "^1.18.1",
"better-scroll": "^1.15.2", "better-scroll": "^1.15.2",
@ -41,7 +41,7 @@
"screenfull": "^5.0.2", "screenfull": "^5.0.2",
"sortablejs": "^1.10.1", "sortablejs": "^1.10.1",
"ua-parser-js": "^0.7.20", "ua-parser-js": "^0.7.20",
"vue": "^2.6.11", "vue": "2.7.14",
"vue-i18n": "^8.15.1", "vue-i18n": "^8.15.1",
"vue-infinite-scroll": "^2.0.2", "vue-infinite-scroll": "^2.0.2",
"vue-router": "^3.6.5", "vue-router": "^3.6.5",

BIN
web/public/image/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@ -1,10 +1,12 @@
<template> <template>
<el-tooltip effect="dark" :content="'颜色'" placement="bottom">
<el-color-picker <el-color-picker
class="btn-text can-hover" class="can-hover"
:value="value" :value="value"
:predefine="predefine" :predefine="predefine"
size="mini" size="mini"
@change="set"/> @change="set"/>
</el-tooltip>
</template> </template>
<script> <script>

View File

@ -16,7 +16,7 @@
</template> </template>
<script> <script>
import {mapActions} from 'vuex' import { mapActions } from 'vuex'
export default { export default {
name: 'msgList', name: 'msgList',
props: { props: {
@ -32,7 +32,7 @@ export default {
toPage () { toPage () {
// this.open({name:'messageCenter'}) // this.open({name:'messageCenter'})
this.$router.push({ this.$router.push({
name:'messageCenter' name: 'messageCenter'
}) })
} }
} }

View File

@ -8,9 +8,22 @@
--> -->
<template> <template>
<el-dropdown size="small" class="d2-mr"> <el-dropdown size="small" class="d2-mr">
<el-link
type="primary"
:underline="false"
style="margin-bottom: 2px;margin-left: 10px"
v-if="isTenants"
>
<span>
当前租户{{info.tenant_name}}
</span>
<span class="btn-text">{{ <span class="btn-text">{{
info.name ? `你好 ${info.name}` : "未登录" info.name ? `你好 ${info.name}` : "未登录"
}}</span> }}</span>
</el-link>
<span class="btn-text" v-else>{{
info.name ? `你好 ${info.name}` : "未登录"
}}</span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="userInfo"> <el-dropdown-item @click.native="userInfo">
<d2-icon name="cog" class="d2-mr-5" />个人信息 <d2-icon name="cog" class="d2-mr-5" />个人信息
@ -20,7 +33,7 @@
注销 注销
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
<el-image v-if="info.avatar" :src="info.avatar" :preview-src-list="[info.avatar]" style="width: 20px;height: 20px" alt="头像"></el-image> <el-image v-if="info.avatar" :src="info.avatar" :preview-src-list="[info.avatar]" style="width: 20px;height: 20px;border-radius: 20%;top: 5px;" alt="头像"></el-image>
</el-dropdown> </el-dropdown>
</template> </template>
@ -30,6 +43,11 @@ export default {
computed: { computed: {
...mapState('d2admin/user', ['info']) ...mapState('d2admin/user', ['info'])
}, },
data () {
return {
isTenants: window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenants-web') !== -1
}
},
methods: { methods: {
...mapActions('d2admin/account', ['logout']), ...mapActions('d2admin/account', ['logout']),
/** /**

View File

@ -1,11 +1,3 @@
<!--
* @创建文件时间: 2021-07-26 23:08:16
* @Auther: 猿小天
* @最后修改人: 猿小天
* @最后修改时间: 2021-08-12 11:32:30
* 联系Qq:1638245306
* @文件介绍: 用户信息
-->
<template> <template>
<d2-container class="page"> <d2-container class="page">
<el-tabs v-model="activeName" @tab-click="handleClick"> <el-tabs v-model="activeName" @tab-click="handleClick">
@ -14,42 +6,20 @@
<el-col :span="10" :offset="6"> <el-col :span="10" :offset="6">
<el-form <el-form
ref="userInfoForm" ref="userInfoForm"
label-width="100px"
:model="userInfo" :model="userInfo"
required-asterisk required-asterisk
:rules="userInforules" :rules="userInforules"
:label-position="position" :label-position="position"
center center
> >
<el-form-item prop="avatar" required label="头像"> <el-form-item prop="avatar" label="头像">
<el-upload <d2p-cropper-uploader :value="userInfo.avatar || '/image/avatar.png'" @input="handleAvatarSuccess"/>
class="avatar-uploader"
list-type="picture-card"
:file-list="fileList"
:action="action"
:headers="headers"
:limit="1"
:disabled="fileList.length === 1"
:on-success="handleAvatarSuccess"
>
<!-- <el-image
v-if="userInfo.avatar"
:src="userInfo.avatar"
:preview-src-list="[userInfo.avatar]"
style="width: 100px; height: 100px"
alt="头像"
></el-image>
<i
v-else
class="el-icon-plus avatar-uploader-icon"
style="width: 100px; height: 100px"
></i> -->
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item> </el-form-item>
<el-form-item prop="name" required label="昵称"> <el-form-item prop="name" required label="昵称">
<el-input v-model="userInfo.name" clearable></el-input> <el-input v-model="userInfo.name" clearable></el-input>
</el-form-item> </el-form-item>
<el-form-item label="电话号码" prop="mobile"> <el-form-item label="电话号码" required prop="mobile">
<el-input v-model="userInfo.mobile" clearable></el-input> <el-input v-model="userInfo.mobile" clearable></el-input>
</el-form-item> </el-form-item>
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
@ -62,6 +32,22 @@
<el-radio :label="-1">未知</el-radio> <el-radio :label="-1">未知</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="用户名" prop="dept">
<el-input :value="userInfo.username" clearable disabled></el-input>
</el-form-item>
<el-form-item label="所属部门" prop="dept">
<el-input :value="userInfo.dept_info && userInfo.dept_info.dept_name" clearable disabled></el-input>
</el-form-item>
<el-form-item label="当前角色" prop="role">
<el-select :value="userInfo.role" multiple placeholder="请选择" disabled style="width: 100%;">
<el-option
v-for="item in userInfo.role_info"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button @click="updateInfo" type="primary"> <el-button @click="updateInfo" type="primary">
<i class="fa fa-check"></i> <i class="fa fa-check"></i>
@ -83,6 +69,7 @@
ref="userPasswordForm" ref="userPasswordForm"
:model="userPasswordInfo" :model="userPasswordInfo"
required-asterisk required-asterisk
label-width="100px"
:label-position="position" :label-position="position"
:rules="passwordRules" :rules="passwordRules"
center center
@ -90,6 +77,7 @@
<el-form-item label="原密码" required prop="oldPassword"> <el-form-item label="原密码" required prop="oldPassword">
<el-input <el-input
v-model="userPasswordInfo.oldPassword" v-model="userPasswordInfo.oldPassword"
placeholder="请输入原始密码"
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -97,6 +85,7 @@
<el-input <el-input
type="password" type="password"
v-model="userPasswordInfo.newPassword" v-model="userPasswordInfo.newPassword"
placeholder="请输入新密码"
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -104,6 +93,7 @@
<el-input <el-input
type="password" type="password"
v-model="userPasswordInfo.newPassword2" v-model="userPasswordInfo.newPassword2"
placeholder="请再次输入新密码"
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -206,7 +196,6 @@ export default {
params: {} params: {}
}).then((res) => { }).then((res) => {
_self.userInfo = res.data _self.userInfo = res.data
_self.fileList = [{ name: 'avatar.png', url: res.data.avatar }]
}) })
}, },
/** /**
@ -217,10 +206,12 @@ export default {
_self.$refs.userInfoForm.validate((valid) => { _self.$refs.userInfoForm.validate((valid) => {
if (valid) { if (valid) {
const userInfo = _self.userInfo
delete userInfo.role
request({ request({
url: '/api/system/user/update_user_info/', url: '/api/system/user/update_user_info/',
method: 'put', method: 'put',
data: _self.userInfo data: userInfo
}).then((res) => { }).then((res) => {
_self.$message.success('修改成功') _self.$message.success('修改成功')
_self.getCurrentUserInfo() _self.getCurrentUserInfo()
@ -289,8 +280,7 @@ export default {
*/ */
handleAvatarSuccess (res, file) { handleAvatarSuccess (res, file) {
console.log(11, res) console.log(11, res)
this.fileList = [{ url: util.baseURL() + res.data.url, name: file.name }] this.userInfo.avatar = res
this.userInfo.avatar = util.baseURL() + res.data.url
} }
} }
} }

View File

@ -39,7 +39,7 @@ util.open = function (url) {
util.baseURL = function () { util.baseURL = function () {
var baseURL = process.env.VUE_APP_API var baseURL = process.env.VUE_APP_API
var param = baseURL.split('/')[3] || '' var param = baseURL.split('/')[3] || ''
if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenant-web') !== -1 && (!param || baseURL.startsWith('/'))) { if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
// 1.把127.0.0.1 替换成和前端一样域名 // 1.把127.0.0.1 替换成和前端一样域名
// 2. ip 地址替换成和前端一样域名 // 2. ip 地址替换成和前端一样域名
// 3. /api 或其他类似的替换成和前端一样域名 // 3. /api 或其他类似的替换成和前端一样域名
@ -66,7 +66,7 @@ util.baseURL = function () {
util.wsBaseURL = function () { util.wsBaseURL = function () {
var baseURL = process.env.VUE_APP_API var baseURL = process.env.VUE_APP_API
var param = baseURL.split('/')[3] || '' var param = baseURL.split('/')[3] || ''
if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenant-web') !== -1 && (!param || baseURL.startsWith('/'))) { if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
// 1.把127.0.0.1 替换成和前端一样域名 // 1.把127.0.0.1 替换成和前端一样域名
// 2. ip 地址替换成和前端一样域名 // 2. ip 地址替换成和前端一样域名
// 3. /api 或其他类似的替换成和前端一样域名 // 3. /api 或其他类似的替换成和前端一样域名

View File

@ -62,14 +62,7 @@ router.beforeEach(async (to, from, next) => {
method: 'get', method: 'get',
params: {} params: {}
}) })
await store.dispatch('d2admin/user/set', { await store.dispatch('d2admin/user/set', res.data, { root: true })
name: res.data.name,
user_id: res.data.id,
avatar: res.data.avatar,
role_info: res.data.role_info,
dept_info: res.data.dept_info,
is_superuser: res.data.is_superuser
}, { root: true })
await store.dispatch('d2admin/account/load') await store.dispatch('d2admin/account/load')
store.dispatch('d2admin/dept/load') store.dispatch('d2admin/dept/load')
store.dispatch('d2admin/settings/init') store.dispatch('d2admin/settings/init')

View File

@ -11,6 +11,7 @@ import util from '@/libs/util.js'
import router from '@/router' import router from '@/router'
import store from '@/store/index' import store from '@/store/index'
import { SYS_USER_LOGIN, SYS_USER_LOGOUT } from '@/views/system/login/api' import { SYS_USER_LOGIN, SYS_USER_LOGOUT } from '@/views/system/login/api'
import { request } from '@/api/service'
export default { export default {
namespaced: true, namespaced: true,
@ -44,13 +45,19 @@ export default {
util.cookies.set('token', res.access) util.cookies.set('token', res.access)
util.cookies.set('refresh', res.refresh) util.cookies.set('refresh', res.refresh)
// 设置 vuex 用户信息 // 设置 vuex 用户信息
await dispatch('d2admin/user/set', { // await dispatch('d2admin/user/set', {
name: res.name, // name: res.name,
user_id: res.userId, // user_id: res.userId,
avatar: res.avatar, // avatar: res.avatar,
role_info: res.role_info, // role_info: res.role_info,
dept_info: res.dept_info // dept_info: res.dept_info
}, { root: true }) // }, { root: true })
var userInfoRes = await request({
url: '/api/system/user/user_info/',
method: 'get',
params: {}
})
await store.dispatch('d2admin/user/set', userInfoRes.data, { root: true })
// 用户登录后从持久化数据加载一系列的设置 // 用户登录后从持久化数据加载一系列的设置
await dispatch('load') await dispatch('load')
}, },

View File

@ -18,8 +18,8 @@ export default {
* @param {String} param type {String} 类型 * @param {String} param type {String} 类型
* @param {Object} payload meta {Object} 附带的信息 * @param {Object} payload meta {Object} 附带的信息
*/ */
async setUnread ({ state,commit },number) { async setUnread ({ state, commit }, number) {
commit('set',number) commit('set', number)
} }
}, },
mutations: { mutations: {

View File

@ -52,5 +52,3 @@ export function DelObj (id) {
data: { id } data: { id }
}) })
} }