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="标题")
content = models.TextField(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,
verbose_name="目标部门", help_text="目标部门")
target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False,
verbose_name="目标角色", help_text="目标角色")
is_read=models.BooleanField(default=False,blank=True,verbose_name="是否已读",help_text="是否已读")
class Meta:
db_table = table_prefix + "message_center"
verbose_name = "消息中心"
verbose_name_plural = verbose_name
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 django_restql.fields import DynamicSerializerMethodField
from rest_framework import serializers
from rest_framework.decorators import action, permission_classes
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.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.viewset import CustomModelViewSet
@ -19,6 +21,8 @@ class MessageCenterSerializer(CustomModelSerializer):
"""
role_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):
roles =instance.target_role.all()
# You can do what ever you want in here
@ -41,12 +45,49 @@ class MessageCenterSerializer(CustomModelSerializer):
)
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:
model = MessageCenter
fields = "__all__"
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):
"""
消息中心-新增-序列化器
@ -58,14 +99,21 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
target_type = initial_data.get('target_type')
# 在保存之前,根据目标类型,把目标用户查询出来并保存
users = initial_data.get('target_user',[])
if target_type in [1]:
if target_type in [1]: #按角色
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)
if target_type in [2]:
if target_type in [2]: #按部门
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)
data.save()
data.target_user.set(users)
if target_type in [3]: #系统通知
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
class Meta:
@ -90,16 +138,44 @@ class MessageCenterViewSet(CustomModelViewSet):
create_serializer_class = MessageCenterCreateSerializer
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])
def get_self_receive(self,request):
"""
获取接收到的消息
"""
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)
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)
serializer = self.get_serializer(queryset, many=True, request=request)
serializer = MessageCenterTargetUserListSerializer(queryset, many=True, request=request)
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,
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: {
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

View File

@ -52,3 +52,16 @@ export function DelObj (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) => {
return {
indexRow: { // 或者直接传true,不显示title不居中
width:60,
title: '序号',
align: 'center'
},
options: {
tableType: 'vxe-table',
rowKey: true, // 必须设置true or false
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: [
{
@ -24,7 +63,7 @@ export const crudOptions = (vm) => {
search: {
disabled: false
},
width: 400,
width: 200,
form: {
rules: [ // 表单校验规则
{
@ -35,12 +74,40 @@ export const crudOptions = (vm) => {
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: '目标类型',
key: 'target_type',
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: {
component: {
span: 24,
props: {
type: 'el-radio-button'
}
},
rules: [
{
required: true,
@ -56,8 +123,11 @@ export const crudOptions = (vm) => {
search: {
disabled: true
},
minWidth: 130,
width: 130,
type: 'table-selector',
show () {
return vm.tabActivted === 'send'
},
dict: {
cache: false,
url: '/api/system/user/',
@ -124,8 +194,10 @@ export const crudOptions = (vm) => {
search: {
disabled: true
},
minWidth: 130,
show () {
return vm.tabActivted === 'send'
},
width: 130,
type: 'table-selector',
dict: {
cache: false,
@ -193,11 +265,14 @@ export const crudOptions = (vm) => {
search: {
disabled: true
},
minWidth: 130,
width: 130,
show () {
return vm.tabActivted === 'send'
},
type: 'table-selector',
dict: {
cache: false,
url: '/api/system/dept/',
url: '/api/system/dept/all_dept/',
isTree: true,
value: 'id', // 数据字典中value字段的属性名
label: 'name', // 数据字典中label字段的属性名
@ -207,11 +282,7 @@ export const crudOptions = (vm) => {
component
}) => {
return request({
url: url,
params: {
page: 1,
limit: 999
}
url: url
}).then(ret => {
return ret.data.data
})
@ -269,7 +340,7 @@ export const crudOptions = (vm) => {
{
title: '内容',
key: 'content',
width: 300,
minWidth: 300,
type: 'editor-quill', // 富文本图片上传依赖file-uploader请先配置好file-uploader
form: {
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"
v-bind="_crudProps"
v-on="_crudListeners"
@form-component-ready="handleFormComponentReady"
@onView="onView"
>
<div slot="header">
<crud-search ref="search" :options="crud.searchOptions" @submit="handleSearch" />
@ -26,9 +26,10 @@
</template>
<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 { d2CrudPlus } from 'd2-crud-plus'
import viewTemplate from './viewTemplate.js'
export default {
name: 'messageCenter',
components: {},
@ -83,10 +84,18 @@ export default {
return res.data
})
},
handleFormComponentReady (event, key, form) {
// console.log('form component ready:', event, key, form)
onView ({ row, index }) {
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()
}
}

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'
}
},
{
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: '手机号码',
key: 'mobile',
@ -320,71 +385,6 @@ export const crudOptions = (vm) => {
},
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({
create_datetime: { showTable: false },