From 31eefcde578b689955b6ce3bcdc45160302b379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E5=B0=8F=E5=A4=A9?= <1638245306@qq.com> Date: Sun, 14 Aug 2022 19:11:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD:=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=B6=88=E6=81=AF=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/application/asgi.py | 12 +- backend/application/websocketConfig.py | 114 +++++++ backend/conf/env.example.py | 3 + backend/dvadmin/system/models.py | 9 +- .../dvadmin/system/views/message_center.py | 105 ++++++ web/src/api/websocket.js | 69 ++++ web/src/layout/header-aside/layout.vue | 7 + web/src/views/dashboard/workbench/index.vue | 2 +- web/src/views/system/messageCenter/api.js | 56 ++++ web/src/views/system/messageCenter/crud.js | 304 ++++++++++++++++++ web/src/views/system/messageCenter/index.vue | 94 ++++++ 11 files changed, 768 insertions(+), 7 deletions(-) create mode 100644 backend/application/websocketConfig.py create mode 100644 backend/dvadmin/system/views/message_center.py create mode 100644 web/src/api/websocket.js create mode 100644 web/src/views/system/messageCenter/api.js create mode 100644 web/src/views/system/messageCenter/crud.js create mode 100644 web/src/views/system/messageCenter/index.vue diff --git a/backend/application/asgi.py b/backend/application/asgi.py index 2321ae8..c38d873 100644 --- a/backend/application/asgi.py +++ b/backend/application/asgi.py @@ -14,4 +14,14 @@ from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings') os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" -application = get_asgi_application() +from application.websocketConfig import websocket_application + +http_application = get_asgi_application() + +async def application(scope,receive,send): + if scope['type'] == 'http': + await http_application(scope, receive, send) + elif scope['type'] == 'websocket': + await websocket_application(scope, receive, send) + else: + raise Exception("未知的scope类型,"+ scope['type']) \ No newline at end of file diff --git a/backend/application/websocketConfig.py b/backend/application/websocketConfig.py new file mode 100644 index 0000000..eb96d6f --- /dev/null +++ b/backend/application/websocketConfig.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +import django + +django.setup() +import json +import urllib + +#处理websocket传参 +from jwt import InvalidSignatureError + +from application import settings +from dvadmin.system.models import MessageCenter + + +def request_data(scope): + query_string = scope.get('query_string', b'').decode('utf-8') + qs = urllib.parse.parse_qs(query_string) + return qs + + +# 全部的websocket sender +CONNECTIONS = {} + + +# 判断用户是否已经连接 +def check_connection(key): + return key in CONNECTIONS + + +# 发送消息结构体 +def message(sender, msg_type, msg): + text = json.dumps({ + 'sender': sender, + 'contentType': msg_type, + 'content': msg, + }) + return { + 'type': 'websocket.send', + 'text': text + } + + +async def websocket_application(scope, receive, send): + while True: + event = await receive() + # print('[event] ', event) + qs = request_data(scope) + print(1,qs) + auth = qs.get('auth', [''])[0] + user_id = None + # 收到建立WebSocket连接的消息 + if event['type'] == 'websocket.connect': + # 昵称验证 + if not auth: + break + else: + try: + import jwt + decoded_result = jwt.decode(auth, settings.SECRET_KEY, algorithms=["HS256"]) + if decoded_result: + user_id = decoded_result.get('user_id') + # 记录 + CONNECTIONS[user_id] = send + except InvalidSignatureError: + break + if auth in CONNECTIONS: + break + + await send({'type': 'websocket.accept'}) + await send(message('system', 'INFO', '连接成功')) + # # 发送好友列表 + # friends_list = list(CONNECTIONS.keys()) + # await send(message('system', 'INFO', friends_list)) + # + # # 向其他人群发消息, 有人登录了 + # for other in CONNECTIONS.values(): + # await other(message('system', 'addFriend', auth)) + + + # 收到中断WebSocket连接的消息 + elif event['type'] == 'websocket.disconnect': + # 移除记录 + if user_id in CONNECTIONS: + CONNECTIONS.pop(user_id) + + # # 向其他人群发消息, 有人离线了 + # for other in CONNECTIONS.values(): + # await other(message('system', 'removeFriend', user_id)) + + # 其他情况,正常的WebSocket消息 + elif event['type'] == 'websocket.receive': + print(11,event) + if event['text'] == 'ping': + await send(message('system', 'text', 'pong!')) + else: + receive_msg = json.loads(event['text']) + message_id = receive_msg.get('message_id', None) + _MessageCenter = MessageCenter.objects.filter(id=message_id).first() + if _MessageCenter: + user_list = _MessageCenter.target_user.values_list('id',flat=True) + for send_user in user_list: + if send_user in CONNECTIONS: + content_type = receive_msg.get('contentType', 'TEXT') + content = receive_msg.get('content', '') + msg = message(user_id, content_type, content) + await CONNECTIONS[send_user](msg) + else: + msg = message('system', 'text', '对方已下线或不存在') + await send(msg) + else: + print('a1a1a1') + pass + + print('[disconnect]') \ No newline at end of file diff --git a/backend/conf/env.example.py b/backend/conf/env.example.py index 2ea54f7..829b73a 100644 --- a/backend/conf/env.example.py +++ b/backend/conf/env.example.py @@ -44,3 +44,6 @@ LOGIN_NO_CAPTCHA_AUTH = True # ================================================= # ALLOWED_HOSTS = ["*"] + +# daphne启动命令 +#daphne application.asgi:application -b 0.0.0.0 -p 8000 diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index 77f530d..1bc6273 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -416,10 +416,10 @@ 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.ForeignKey(to=Users,related_name="target_user",null=True,blank=True,db_constraint=False,on_delete=models.CASCADE,verbose_name="目标用户",help_text="目标用户") - target_dept = models.ForeignKey(to=Dept, null=True, blank=True, db_constraint=False, on_delete=models.CASCADE, + target_user = models.ManyToManyField(to=Users,related_name="target_user",blank=True,db_constraint=False,verbose_name="目标用户",help_text="目标用户") + target_dept = models.ManyToManyField(to=Dept, null=True, blank=True, db_constraint=False, verbose_name="目标部门", help_text="目标部门") - target_role = models.ForeignKey(to=Role, null=True, blank=True, db_constraint=False, on_delete=models.CASCADE, + 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="是否已读") @@ -427,5 +427,4 @@ class MessageCenter(CoreModel): db_table = table_prefix + "message_center" verbose_name = "消息中心" verbose_name_plural = verbose_name - ordering = ("-create_datetime",) - + ordering = ("-create_datetime",) \ No newline at end of file diff --git a/backend/dvadmin/system/views/message_center.py b/backend/dvadmin/system/views/message_center.py new file mode 100644 index 0000000..3571e9c --- /dev/null +++ b/backend/dvadmin/system/views/message_center.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +from itertools import chain + +from django_restql.fields import DynamicSerializerMethodField +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.views.role import RoleSerializer +from dvadmin.system.views.user import UserSerializer +from dvadmin.utils.json_response import SuccessResponse +from dvadmin.utils.serializers import CustomModelSerializer +from dvadmin.utils.viewset import CustomModelViewSet + + +class MessageCenterSerializer(CustomModelSerializer): + """ + 消息中心-序列化器 + """ + role_info = DynamicSerializerMethodField() + user_info = DynamicSerializerMethodField() + def get_role_info(self, instance, parsed_query): + roles =instance.target_role.all() + # You can do what ever you want in here + # `parsed_query` param is passed to BookSerializer to allow further querying + serializer = RoleSerializer( + roles, + many=True, + parsed_query=parsed_query + ) + return serializer.data + + def get_user_info(self, instance, parsed_query): + users = instance.target_user.all() + # You can do what ever you want in here + # `parsed_query` param is passed to BookSerializer to allow further querying + serializer = UserSerializer( + users, + many=True, + parsed_query=parsed_query + ) + return serializer.data + + class Meta: + model = MessageCenter + fields = "__all__" + read_only_fields = ["id"] + + +class MessageCenterCreateSerializer(CustomModelSerializer): + """ + 消息中心-新增-序列化器 + """ + + def save(self, **kwargs): + data = super().save(**kwargs) + initial_data = self.initial_data + target_type = initial_data.get('target_type') + # 在保存之前,根据目标类型,把目标用户查询出来并保存 + users = initial_data.get('target_user',[]) + 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]: + 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) + return data + + class Meta: + model = MessageCenter + fields = "__all__" + read_only_fields = ["id"] + + + + +class MessageCenterViewSet(CustomModelViewSet): + """ + 消息中心接口 + list:查询 + create:新增 + update:修改 + retrieve:单例 + destroy:删除 + """ + queryset = MessageCenter.objects.all() + serializer_class = MessageCenterSerializer + create_serializer_class = MessageCenterCreateSerializer + extra_filter_backends = [] + + @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) + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True, request=request) + return self.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True, request=request) + return SuccessResponse(data=serializer.data, msg="获取成功") \ No newline at end of file diff --git a/web/src/api/websocket.js b/web/src/api/websocket.js new file mode 100644 index 0000000..e6e52cc --- /dev/null +++ b/web/src/api/websocket.js @@ -0,0 +1,69 @@ +import ElementUI from 'element-ui' +import util from '@/libs/util' +function initWebSocket (e) { + const token = util.cookies.get('token') + if (token) { + const wsUri = 'ws://127.0.0.1:8000/?auth=' + token + this.socket = new WebSocket(wsUri)// 这里面的this都指向vue + this.socket.onerror = webSocketOnError + this.socket.onmessage = webSocketOnMessage + this.socket.onclose = closeWebsocket + } +} + +function webSocketOnError (e) { + ElementUI.Notification({ + title: '', + message: 'WebSocket连接发生错误' + JSON.stringify(e), + type: 'error', + duration: 0 + }) +} +function webSocketOnMessage (e) { + const data = JSON.parse(e.data) + if (data.contentType === 'INFO') { + ElementUI.Notification({ + title: 'websocket', + message: data.content, + type: 'success', + position: 'bottom-right', + duration: 3000 + }) + } else if (data.contentType === 'ERROR') { + ElementUI.Notification({ + title: '', + message: data.content, + type: 'error', + position: 'bottom-right', + duration: 0 + }) + } else if (data.contentType === 'TEXT') { + ElementUI.Notification({ + title: '温馨提示', + message: data.content, + type: 'success', + position: 'bottom-right', + duration: 0 + }) + } else { + console.log(data.content) + } +} +// 关闭websiocket +function closeWebsocket () { + console.log('连接已关闭...') + close() +} +function close () { + this.socket.close() // 关闭 websocket + this.socket.onclose = function (e) { + console.log(e)// 监听关闭事件 + console.log('关闭') + } +} +function webSocketSend (message) { + this.socket.send(JSON.stringify(message)) +} +export default { + initWebSocket, close,webSocketSend +} diff --git a/web/src/layout/header-aside/layout.vue b/web/src/layout/header-aside/layout.vue index 5370c16..4d7d312 100644 --- a/web/src/layout/header-aside/layout.vue +++ b/web/src/layout/header-aside/layout.vue @@ -191,6 +191,13 @@ export default { this.showView = true // DOM更新后再通过v-if添加router-view节点 }) } + }, + mounted () { + this.$websocket.initWebSocket() + }, + destroyed () { + // 离开路由之后断开websocket连接 + this.$websocket.close() } } diff --git a/web/src/views/dashboard/workbench/index.vue b/web/src/views/dashboard/workbench/index.vue index 9c15a8d..4719057 100644 --- a/web/src/views/dashboard/workbench/index.vue +++ b/web/src/views/dashboard/workbench/index.vue @@ -217,7 +217,7 @@ export default { gotoRoute (route) { this.$router.push(route) } - } + }, } diff --git a/web/src/views/system/messageCenter/api.js b/web/src/views/system/messageCenter/api.js new file mode 100644 index 0000000..7d108b8 --- /dev/null +++ b/web/src/views/system/messageCenter/api.js @@ -0,0 +1,56 @@ +import { request } from '@/api/service' +export const urlPrefix = '/api/system/message_center/' +export function GetList (query) { + return request({ + url: urlPrefix, + method: 'get', + params: query + }) +} + +/** + * 获取自己接收的消息 + * @param query + * @returns {*} + * @constructor + */ +export function GetSelfReceive (query) { + return request({ + url: urlPrefix + 'get_self_receive/', + method: 'get', + params: query + }) +} + + + +export function GetObj (obj) { + return request({ + url: urlPrefix + obj.id + '/', + method: 'get', + params: {} + }) +} + +export function AddObj (obj) { + return request({ + url: urlPrefix, + method: 'post', + data: obj + }) +} + +export function UpdateObj (obj) { + return request({ + url: urlPrefix + obj.id + '/', + method: 'put', + data: obj + }) +} +export function DelObj (id) { + return request({ + url: urlPrefix + id + '/', + method: 'delete', + data: { id } + }) +} diff --git a/web/src/views/system/messageCenter/crud.js b/web/src/views/system/messageCenter/crud.js new file mode 100644 index 0000000..5789209 --- /dev/null +++ b/web/src/views/system/messageCenter/crud.js @@ -0,0 +1,304 @@ +import { request } from '@/api/service' + +export const crudOptions = (vm) => { + return { + indexRow: { // 或者直接传true,不显示title,不居中 + title: '序号', + align: 'center' + }, + options: { + height: '100%' // 表格高度100%, 使用toolbar必须设置 + }, + viewOptions: { + }, + columns: [ + { + title: 'id', + key: 'id', + sortable: true, + width: 100, + form: { disabled: true } + }, + { + title: '标题', + key: 'title', + search: { + disabled: false + }, + width: 400, + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '必填项' + } + ], + component: { span: 24 } + } + }, + { + title: '目标类型', + key: 'target_type', + type: 'radio', + dict: { data: [{ value: 0, label: '按用户' }, { value: 1, label: '按角色' }, { value: 2, label: '按部门' }] }, + form: { + itemProps: { + class: { yxtInput: true } + }, + rules: [ + { + required: true, + message: '必选项', + trigger: ['blur', 'change'] + } + ] + } + }, + { + title: '目标用户', + key: 'target_user', + search: { + disabled: true + }, + minWidth: 130, + type: 'table-selector', + dict: { + cache: false, + url: '/api/system/user/', + 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: 24, + show (context) { + return context.form.target_type === 0 + }, + pagination: true, + props: { multiple: true }, + elProps: { + columns: [ + { + field: 'name', + title: '用户名称' + }, + { + field: 'phone', + title: '用户电话' + } + ] + } + } + }, + component: { + name: 'manyToMany', + valueBinding: 'user_info', + children: 'name' + } + }, + { + title: '目标角色', + key: 'target_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: 24, + show (context) { + return context.form.target_type === 1 + }, + pagination: true, + props: { multiple: true }, + elProps: { + columns: [ + { + field: 'name', + title: '角色名称' + }, + { + field: 'key', + title: '权限标识' + } + ] + } + } + }, + component: { + name: 'manyToMany', + valueBinding: 'role_info', + children: 'name' + } + }, + { + title: '目标部门', + key: 'target_dept', + search: { + disabled: true + }, + minWidth: 130, + type: 'table-selector', + dict: { + cache: false, + url: '/api/system/dept/', + isTree: true, + value: 'id', // 数据字典中value字段的属性名 + label: 'name', // 数据字典中label字段的属性名 + children: 'children', // 数据字典中children字段的属性名 + getData: (url, dict, { + form, + component + }) => { + return request({ + url: url, + params: { + page: 1, + limit: 999 + } + }).then(ret => { + return ret.data.data + }) + } + }, + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '必填项' + } + ], + itemProps: { + class: { yxtInput: true } + }, + component: { + span: 24, + show (context) { + return context.form.target_type === 2 + }, + props: { + multiple: true, + elProps: { + treeConfig: { + transform: true, + rowField: 'id', + parentField: 'parent', + expandAll: true + }, + columns: [ + { + field: 'name', + title: '部门名称', + treeNode: true + }, + { + field: 'status_label', + title: '状态' + }, + { + field: 'parent_name', + title: '父级部门' + } + ] + } + } + } + }, + component: { + name: 'manyToMany', + valueBinding: 'dept_info', + children: 'name' + } + }, + { + title: '内容', + key: 'content', + width: 300, + type: 'editor-quill', // 富文本图片上传依赖file-uploader,请先配置好file-uploader + form: { + rules: [ // 表单校验规则 + { + required: true, + message: '必填项' + } + ], + component: { + disabled: () => { + return vm.getEditForm().disable + }, + props: { + uploader: { + type: 'form' // 上传后端类型【cos,aliyun,oss,form】 + } + }, + events: { + 'text-change': (event) => { + console.log('text-change:', event) + } + } + } + } + } + ] + } +} diff --git a/web/src/views/system/messageCenter/index.vue b/web/src/views/system/messageCenter/index.vue new file mode 100644 index 0000000..46960a5 --- /dev/null +++ b/web/src/views/system/messageCenter/index.vue @@ -0,0 +1,94 @@ + + + From 63c6188591b91f10f345c7a59aff5c0d32e9b6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E5=B0=8F=E5=A4=A9?= <1638245306@qq.com> Date: Sun, 14 Aug 2022 19:19:17 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD:=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=B6=88=E6=81=AF=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dvadmin/system/fixtures/init_menu.json | 49 ++++++++++++++++++- web/src/views/system/messageCenter/crud.js | 3 -- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/backend/dvadmin/system/fixtures/init_menu.json b/backend/dvadmin/system/fixtures/init_menu.json index 0eeedd2..2ecd57d 100644 --- a/backend/dvadmin/system/fixtures/init_menu.json +++ b/backend/dvadmin/system/fixtures/init_menu.json @@ -272,10 +272,57 @@ } ] }, + { + "name": "消息中心", + "icon": "bullhorn", + "sort": 7, + "is_link": false, + "is_catalog": false, + "web_path": "/messageCenter", + "component": "system/messageCenter/index", + "component_name": "messageCenter", + "status": true, + "cache": false, + "visible": true, + "parent": 277, + "children": [], + "menu_button": [ + { + "name": "查询", + "value": "Search", + "api": "/api/system/message_center/", + "method": 0 + }, + { + "name": "详情", + "value": "Retrieve", + "api": "/api/system/message_center/{id}/", + "method": 0 + }, + { + "name": "新增", + "value": "Create", + "api": "/api/system/message_center/", + "method": 1 + }, + { + "name": "编辑", + "value": "Update", + "api": "/api/system/message_center/{id}/", + "method": 2 + }, + { + "name": "删除", + "value": "Delete", + "api": "/api/system/menu/{id}/", + "method": 3 + } + ] + }, { "name": "接口白名单", "icon": "compass", - "sort": 7, + "sort": 8, "is_link": false, "is_catalog": false, "web_path": "/apiWhiteList", diff --git a/web/src/views/system/messageCenter/crud.js b/web/src/views/system/messageCenter/crud.js index 5789209..abed551 100644 --- a/web/src/views/system/messageCenter/crud.js +++ b/web/src/views/system/messageCenter/crud.js @@ -42,9 +42,6 @@ export const crudOptions = (vm) => { type: 'radio', dict: { data: [{ value: 0, label: '按用户' }, { value: 1, label: '按角色' }, { value: 2, label: '按部门' }] }, form: { - itemProps: { - class: { yxtInput: true } - }, rules: [ { required: true, From 888a3b73e84ccd1076c04704b4406c6a9b12fcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E5=B0=8F=E5=A4=A9?= <1638245306@qq.com> Date: Sun, 14 Aug 2022 19:21:35 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD:=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=B6=88=E6=81=AF=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/dvadmin/system/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index 1bc6273..734a468 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -34,8 +34,8 @@ class Users(CoreModel,AbstractUser): user_type = models.IntegerField( choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, help_text="用户类型" ) - post = models.ManyToManyField(to="Post", verbose_name="关联岗位", db_constraint=False, help_text="关联岗位") - role = models.ManyToManyField(to="Role", verbose_name="关联角色", db_constraint=False, help_text="关联角色") + post = models.ManyToManyField(to="Post",blank=True, verbose_name="关联岗位", db_constraint=False, help_text="关联岗位") + role = models.ManyToManyField(to="Role", blank=True,verbose_name="关联角色", db_constraint=False, help_text="关联角色") dept = models.ForeignKey( to="Dept", verbose_name="所属部门", From ef2025f157343e739fb06719af58ae67c979bf78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E5=B0=8F=E5=A4=A9?= <1638245306@qq.com> Date: Sun, 14 Aug 2022 19:34:25 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD:=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E6=B6=88=E6=81=AF=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/api/websocket.js | 2 +- web/src/views/dashboard/workbench/index.vue | 2 +- web/src/views/system/messageCenter/api.js | 2 -- web/src/views/system/messageCenter/index.vue | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/web/src/api/websocket.js b/web/src/api/websocket.js index e6e52cc..e324f12 100644 --- a/web/src/api/websocket.js +++ b/web/src/api/websocket.js @@ -65,5 +65,5 @@ function webSocketSend (message) { this.socket.send(JSON.stringify(message)) } export default { - initWebSocket, close,webSocketSend + initWebSocket, close, webSocketSend } diff --git a/web/src/views/dashboard/workbench/index.vue b/web/src/views/dashboard/workbench/index.vue index 4719057..9c15a8d 100644 --- a/web/src/views/dashboard/workbench/index.vue +++ b/web/src/views/dashboard/workbench/index.vue @@ -217,7 +217,7 @@ export default { gotoRoute (route) { this.$router.push(route) } - }, + } } diff --git a/web/src/views/system/messageCenter/api.js b/web/src/views/system/messageCenter/api.js index 7d108b8..384a787 100644 --- a/web/src/views/system/messageCenter/api.js +++ b/web/src/views/system/messageCenter/api.js @@ -22,8 +22,6 @@ export function GetSelfReceive (query) { }) } - - export function GetObj (obj) { return request({ url: urlPrefix + obj.id + '/', diff --git a/web/src/views/system/messageCenter/index.vue b/web/src/views/system/messageCenter/index.vue index 46960a5..d0f84fe 100644 --- a/web/src/views/system/messageCenter/index.vue +++ b/web/src/views/system/messageCenter/index.vue @@ -26,7 +26,7 @@