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

pull/79/head
李强 2022-11-13 10:15:05 +08:00
commit 69de8fff0b
8 changed files with 321 additions and 96 deletions

View File

@ -417,15 +417,24 @@ class MessageCenter(CoreModel):
title = models.CharField(max_length=100,verbose_name="标题",help_text="标题") title = models.CharField(max_length=100,verbose_name="标题",help_text="标题")
content = models.TextField(verbose_name="内容",help_text="内容") content = models.TextField(verbose_name="内容",help_text="内容")
target_type=models.IntegerField(default=0,verbose_name="目标类型",help_text="目标类型") target_type=models.IntegerField(default=0,verbose_name="目标类型",help_text="目标类型")
target_user = models.ManyToManyField(to=Users,related_name="target_user",blank=True,db_constraint=False,verbose_name="目标用户",help_text="目标用户") target_user = models.ManyToManyField(to=Users,related_name='user',through='MessageCenterTargetUser', through_fields=('messagecenter','users'),blank=True,verbose_name="目标用户",help_text="目标用户")
target_dept = models.ManyToManyField(to=Dept, blank=True, db_constraint=False, target_dept = models.ManyToManyField(to=Dept, blank=True, db_constraint=False,
verbose_name="目标部门", help_text="目标部门") verbose_name="目标部门", help_text="目标部门")
target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False, target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False,
verbose_name="目标角色", help_text="目标角色") verbose_name="目标角色", help_text="目标角色")
is_read=models.BooleanField(default=False,blank=True,verbose_name="是否已读",help_text="是否已读")
class Meta: class Meta:
db_table = table_prefix + "message_center" db_table = table_prefix + "message_center"
verbose_name = "消息中心" verbose_name = "消息中心"
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
ordering = ("-create_datetime",) ordering = ("-create_datetime",)
class MessageCenterTargetUser(CoreModel):
users = models.ForeignKey(Users,related_name="target_user", on_delete=models.CASCADE,db_constraint=False,verbose_name="关联用户表",help_text="关联用户表")
messagecenter = models.ForeignKey(MessageCenter, on_delete=models.CASCADE,db_constraint=False,verbose_name="关联消息中心表",help_text="关联消息中心表")
is_read = models.BooleanField(default=False,blank=True,null=True,verbose_name="是否已读",help_text="是否已读")
class Meta:
db_table = table_prefix + "message_center_target_user"
verbose_name = "消息中心目标用户表"
verbose_name_plural = verbose_name

View File

@ -2,13 +2,15 @@
from itertools import chain from itertools import chain
from django_restql.fields import DynamicSerializerMethodField from django_restql.fields import DynamicSerializerMethodField
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, AllowAny from rest_framework.permissions import IsAuthenticated, AllowAny
from dvadmin.system.models import MessageCenter, Users from dvadmin.system.models import MessageCenter, Users, MessageCenterTargetUser
from dvadmin.system.views.dept import DeptSerializer
from dvadmin.system.views.role import RoleSerializer from dvadmin.system.views.role import RoleSerializer
from dvadmin.system.views.user import UserSerializer from dvadmin.system.views.user import UserSerializer
from dvadmin.utils.json_response import SuccessResponse from dvadmin.utils.json_response import SuccessResponse, DetailResponse
from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.viewset import CustomModelViewSet from dvadmin.utils.viewset import CustomModelViewSet
@ -19,6 +21,8 @@ class MessageCenterSerializer(CustomModelSerializer):
""" """
role_info = DynamicSerializerMethodField() role_info = DynamicSerializerMethodField()
user_info = DynamicSerializerMethodField() user_info = DynamicSerializerMethodField()
dept_info = DynamicSerializerMethodField()
is_read = serializers.BooleanField(read_only=True,source='target_user__is_read')
def get_role_info(self, instance, parsed_query): def get_role_info(self, instance, parsed_query):
roles =instance.target_role.all() roles =instance.target_role.all()
# You can do what ever you want in here # You can do what ever you want in here
@ -41,12 +45,49 @@ class MessageCenterSerializer(CustomModelSerializer):
) )
return serializer.data return serializer.data
def get_dept_info(self, instance, parsed_query):
dept = instance.target_dept.all()
# You can do what ever you want in here
# `parsed_query` param is passed to BookSerializer to allow further querying
serializer = DeptSerializer(
dept,
many=True,
parsed_query=parsed_query
)
return serializer.data
class Meta: class Meta:
model = MessageCenter model = MessageCenter
fields = "__all__" fields = "__all__"
read_only_fields = ["id"] read_only_fields = ["id"]
class MessageCenterTargetUserSerializer(CustomModelSerializer):
"""
目标用户序列化器-序列化器
"""
class Meta:
model = MessageCenterTargetUser
fields = "__all__"
read_only_fields = ["id"]
class MessageCenterTargetUserListSerializer(CustomModelSerializer):
"""
目标用户序列化器-序列化器
"""
class Meta:
model = MessageCenterTargetUser
fields = "__all__"
read_only_fields = ["id"]
def to_representation(self, instance):
data = super().to_representation(instance)
data['title'] = instance.messagecenter.title
data['content'] = instance.messagecenter.content
data['target_type'] = instance.messagecenter.target_type
data['id'] = instance.messagecenter.id
return data
class MessageCenterCreateSerializer(CustomModelSerializer): class MessageCenterCreateSerializer(CustomModelSerializer):
""" """
消息中心-新增-序列化器 消息中心-新增-序列化器
@ -58,14 +99,21 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
target_type = initial_data.get('target_type') target_type = initial_data.get('target_type')
# 在保存之前,根据目标类型,把目标用户查询出来并保存 # 在保存之前,根据目标类型,把目标用户查询出来并保存
users = initial_data.get('target_user',[]) users = initial_data.get('target_user',[])
if target_type in [1]: if target_type in [1]: #按角色
target_role = initial_data.get('target_role') target_role = initial_data.get('target_role')
users = Users.objects.exclude(is_deleted=True).filter(role__id__in=target_role).values_list('id',flat=True) users = Users.objects.exclude(is_deleted=True).filter(role__id__in=target_role).values_list('id',flat=True)
if target_type in [2]: if target_type in [2]: #按部门
target_dept = initial_data.get('target_dept') target_dept = initial_data.get('target_dept')
users = Users.objects.exclude(is_deleted=True).filter(dept__id__in=target_dept).values_list('id',flat=True) users = Users.objects.exclude(is_deleted=True).filter(dept__id__in=target_dept).values_list('id',flat=True)
data.save() if target_type in [3]: #系统通知
data.target_user.set(users) users = Users.objects.exclude(is_deleted=True).values_list('id',flat=True)
targetuser_data = [{
"messagecenter": data.id,
"users": user
} for user in users]
targetuser_instance = MessageCenterTargetUserSerializer(data=targetuser_data, many=True, request=self.request)
targetuser_instance.is_valid(raise_exception=True)
targetuser_instance.save()
return data return data
class Meta: class Meta:
@ -90,16 +138,44 @@ class MessageCenterViewSet(CustomModelViewSet):
create_serializer_class = MessageCenterCreateSerializer create_serializer_class = MessageCenterCreateSerializer
extra_filter_backends = [] extra_filter_backends = []
def get_queryset(self):
if self.action=='list':
return MessageCenter.objects.filter(creator=self.request.user.id).all()
return MessageCenter.objects.all()
def retrieve(self, request, *args, **kwargs):
pk = kwargs.get('pk')
user_id = self.request.user.id
queryset = MessageCenterTargetUser.objects.filter(users__id=user_id,messagecenter__id=pk).first()
if queryset:
queryset.is_read = True
queryset.save()
instance = self.get_object()
serializer = self.get_serializer(instance)
return DetailResponse(data=serializer.data, msg="获取成功")
@action(methods=['get'],detail=True,permission_classes=[IsAuthenticated])
def receive_view(self, request, pk):
"""
我的接收-查看
"""
instance = MessageCenterTargetUser.objects.filter(id=pk).first()
instance.is_read = True
instance.save()
serializer = MessageCenterTargetUserListSerializer(instance)
return DetailResponse(data=serializer.data, msg="获取成功")
@action(methods=['GET'],detail=False,permission_classes=[IsAuthenticated]) @action(methods=['GET'],detail=False,permission_classes=[IsAuthenticated])
def get_self_receive(self,request): def get_self_receive(self,request):
""" """
获取接收到的消息 获取接收到的消息
""" """
self_user_id = self.request.user.id self_user_id = self.request.user.id
queryset = MessageCenter.objects.filter(target_user__id=self_user_id) queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).exclude(messagecenter__is_deleted=True)
queryset = self.filter_queryset(queryset)
page = self.paginate_queryset(queryset) page = self.paginate_queryset(queryset)
if page is not None: if page is not None:
serializer = self.get_serializer(page, many=True, request=request) serializer = MessageCenterTargetUserListSerializer(page, many=True, request=request)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True, request=request) serializer = MessageCenterTargetUserListSerializer(queryset, many=True, request=request)
return SuccessResponse(data=serializer.data, msg="获取成功") return SuccessResponse(data=serializer.data, msg="获取成功")

View File

@ -252,6 +252,10 @@ Vue.prototype.commonEndColumns = function (param = {}) {
showForm: (param.update_datetime && param.update_datetime.showForm) !== undefined ? param.update_datetime.showForm : false, showForm: (param.update_datetime && param.update_datetime.showForm) !== undefined ? param.update_datetime.showForm : false,
showTable: (param.update_datetime && param.update_datetime.showTable) !== undefined ? param.update_datetime.showTable : true showTable: (param.update_datetime && param.update_datetime.showTable) !== undefined ? param.update_datetime.showTable : true
}, },
creator_name: {
showForm: (param.creator_name && param.creator_name.showForm) !== undefined ? param.creator_name.showForm : false,
showTable: (param.creator_name && param.creator_name.showTable) !== undefined ? param.creator_name.showTable : false
},
create_datetime: { create_datetime: {
showForm: (param.create_datetime && param.create_datetime.showForm) !== undefined ? param.create_datetime.showForm : false, showForm: (param.create_datetime && param.create_datetime.showForm) !== undefined ? param.create_datetime.showForm : false,
showTable: (param.create_datetime && param.create_datetime.showTable) !== undefined ? param.create_datetime.showTable : true showTable: (param.create_datetime && param.create_datetime.showTable) !== undefined ? param.create_datetime.showTable : true

View File

@ -52,3 +52,16 @@ export function DelObj (id) {
data: { id } data: { id }
}) })
} }
/**
* 我的接收-查看
* @param obj
* @returns {*}
*/
export function receiveView (obj) {
return request({
url: urlPrefix + obj.id + '/receive_view/',
method: 'get',
params: {}
})
}

View File

@ -3,13 +3,52 @@ import { request } from '@/api/service'
export const crudOptions = (vm) => { export const crudOptions = (vm) => {
return { return {
indexRow: { // 或者直接传true,不显示title不居中 indexRow: { // 或者直接传true,不显示title不居中
width:60,
title: '序号', title: '序号',
align: 'center' align: 'center'
}, },
options: { options: {
tableType: 'vxe-table',
rowKey: true, // 必须设置true or false
height: '100%' // 表格高度100%, 使用toolbar必须设置 height: '100%' // 表格高度100%, 使用toolbar必须设置
}, },
viewOptions: { rowHandle: {
width: 160,
fixed: 'right',
view: false,
edit: {
thin: true,
text: '',
show () {
return vm.tabActivted !== 'receive'
},
disabled () {
return !vm.hasPermissions('Update')
}
},
remove: {
thin: true,
text: '',
show () {
return vm.tabActivted !== 'receive'
},
disabled () {
return !vm.hasPermissions('Delete')
}
},
custom: [
{
thin: true,
text: null,
icon: 'el-icon-view',
size: 'small',
disabled () {
return !vm.hasPermissions('Retrieve')
},
order: 1,
emit: 'onView'
}
]
}, },
columns: [ columns: [
{ {
@ -24,7 +63,7 @@ export const crudOptions = (vm) => {
search: { search: {
disabled: false disabled: false
}, },
width: 400, width: 200,
form: { form: {
rules: [ // 表单校验规则 rules: [ // 表单校验规则
{ {
@ -35,12 +74,40 @@ export const crudOptions = (vm) => {
component: { span: 24, placeholder: '请输入标题' } component: { span: 24, placeholder: '请输入标题' }
} }
}, },
{
title: '是否已读',
key: 'is_read',
type: 'select',
width: 100,
show () {
return vm.tabActivted === 'receive'
},
dict: {
data: [
{ label: '已读', value: true, color: 'success' },
{ label: '未读', value: false, color: 'danger' }
]
},
form: {
disabled: true
}
},
{ {
title: '目标类型', title: '目标类型',
key: 'target_type', key: 'target_type',
type: 'radio', type: 'radio',
dict: { data: [{ value: 0, label: '按用户' }, { value: 1, label: '按角色' }, { value: 2, label: '按部门' }] }, width: 120,
show () {
return vm.tabActivted === 'send'
},
dict: { data: [{ value: 0, label: '按用户' }, { value: 1, label: '按角色' }, { value: 2, label: '按部门' }, { value: 3, label: '通知公告' }] },
form: { form: {
component: {
span: 24,
props: {
type: 'el-radio-button'
}
},
rules: [ rules: [
{ {
required: true, required: true,
@ -56,8 +123,11 @@ export const crudOptions = (vm) => {
search: { search: {
disabled: true disabled: true
}, },
minWidth: 130, width: 130,
type: 'table-selector', type: 'table-selector',
show () {
return vm.tabActivted === 'send'
},
dict: { dict: {
cache: false, cache: false,
url: '/api/system/user/', url: '/api/system/user/',
@ -124,8 +194,10 @@ export const crudOptions = (vm) => {
search: { search: {
disabled: true disabled: true
}, },
show () {
minWidth: 130, return vm.tabActivted === 'send'
},
width: 130,
type: 'table-selector', type: 'table-selector',
dict: { dict: {
cache: false, cache: false,
@ -193,11 +265,14 @@ export const crudOptions = (vm) => {
search: { search: {
disabled: true disabled: true
}, },
minWidth: 130, width: 130,
show () {
return vm.tabActivted === 'send'
},
type: 'table-selector', type: 'table-selector',
dict: { dict: {
cache: false, cache: false,
url: '/api/system/dept/', url: '/api/system/dept/all_dept/',
isTree: true, isTree: true,
value: 'id', // 数据字典中value字段的属性名 value: 'id', // 数据字典中value字段的属性名
label: 'name', // 数据字典中label字段的属性名 label: 'name', // 数据字典中label字段的属性名
@ -207,11 +282,7 @@ export const crudOptions = (vm) => {
component component
}) => { }) => {
return request({ return request({
url: url, url: url
params: {
page: 1,
limit: 999
}
}).then(ret => { }).then(ret => {
return ret.data.data return ret.data.data
}) })
@ -269,7 +340,7 @@ export const crudOptions = (vm) => {
{ {
title: '内容', title: '内容',
key: 'content', key: 'content',
width: 300, minWidth: 300,
type: 'editor-quill', // 富文本图片上传依赖file-uploader请先配置好file-uploader type: 'editor-quill', // 富文本图片上传依赖file-uploader请先配置好file-uploader
form: { form: {
rules: [ // 表单校验规则 rules: [ // 表单校验规则
@ -295,6 +366,9 @@ export const crudOptions = (vm) => {
} }
} }
} }
] ].concat(vm.commonEndColumns({
create_datetime: { showTable: true },
update_datetime: { showTable: false }
}))
} }
} }

View File

@ -5,7 +5,7 @@
ref="d2Crud" ref="d2Crud"
v-bind="_crudProps" v-bind="_crudProps"
v-on="_crudListeners" v-on="_crudListeners"
@form-component-ready="handleFormComponentReady" @onView="onView"
> >
<div slot="header"> <div slot="header">
<crud-search ref="search" :options="crud.searchOptions" @submit="handleSearch" /> <crud-search ref="search" :options="crud.searchOptions" @submit="handleSearch" />
@ -26,9 +26,10 @@
</template> </template>
<script> <script>
import { AddObj, GetObj, GetList, UpdateObj, DelObj, GetSelfReceive } from './api' import { AddObj, GetObj, GetList, UpdateObj, DelObj, GetSelfReceive,receiveView } from './api'
import { crudOptions } from './crud' import { crudOptions } from './crud'
import { d2CrudPlus } from 'd2-crud-plus' import { d2CrudPlus } from 'd2-crud-plus'
import viewTemplate from './viewTemplate.js'
export default { export default {
name: 'messageCenter', name: 'messageCenter',
components: {}, components: {},
@ -83,10 +84,18 @@ export default {
return res.data return res.data
}) })
}, },
handleFormComponentReady (event, key, form) { onView ({ row, index }) {
// console.log('form component ready:', event, key, form) this.getD2Crud().showDialog({
mode: 'view',
rowIndex: index,
template: viewTemplate
})
this.infoRequest(row)
this.doRefresh()
}, },
onTabClick (obj) { onTabClick (tab) {
const { name } = tab
this.tabActivted = name
this.doRefresh() this.doRefresh()
} }
} }

View File

@ -0,0 +1,40 @@
export default {
title: {
title: '标题',
key: 'title',
component: {
span: 24,
placeholder: '请输入标题',
disabled: true
},
rules: [
{
required: true,
message: '必填项'
}
],
order: 10
},
content: {
title: '内容',
key: 'content',
component: {
name: 'd2p-quill',
span: 24,
disabled: true,
props: {
uploader: {
type: 'form'
}
},
events: {}
},
rules: [
{
required: true,
message: '必填项'
}
],
order: 10
}
}

View File

@ -206,6 +206,71 @@ export const crudOptions = (vm) => {
valueBinding: 'dept_name' valueBinding: 'dept_name'
} }
}, },
{
title: '角色',
key: 'role',
search: {
disabled: true
},
minWidth: 130,
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,
pagination: true,
props: { multiple: true },
elProps: {
columns: [
{
field: 'name',
title: '角色名称'
},
{
field: 'key',
title: '权限标识'
}
]
}
}
},
component: {
name: 'manyToMany',
valueBinding: 'role_info',
children: 'name'
}
},
{ {
title: '手机号码', title: '手机号码',
key: 'mobile', key: 'mobile',
@ -320,71 +385,6 @@ export const crudOptions = (vm) => {
}, },
helper: '限制文件大小不能超过500k' helper: '限制文件大小不能超过500k'
} }
},
{
title: '角色',
key: 'role',
search: {
disabled: true
},
minWidth: 130,
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,
pagination: true,
props: { multiple: true },
elProps: {
columns: [
{
field: 'name',
title: '角色名称'
},
{
field: 'key',
title: '权限标识'
}
]
}
}
},
component: {
name: 'manyToMany',
valueBinding: 'role_info',
children: 'name'
}
} }
].concat(vm.commonEndColumns({ ].concat(vm.commonEndColumns({
create_datetime: { showTable: false }, create_datetime: { showTable: false },