diff --git a/frontend/package.json b/frontend/package.json index 7fab8f1d6..196869130 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -86,7 +86,7 @@ "@babel/preset-env": "~7.4.5", "@babel/register": "~7.4.4", "@babel/runtime": "~7.4.5", - "@blueking/notice-component-vue2": "^2.0.1", + "@blueking/notice-component-vue2": "^2.0.3", "@blueking/user-selector": "^1.0.11", "@commitlint/cli": "~13.1.0", "@commitlint/config-conventional": "~13.1.0", diff --git a/frontend/src/views/transfer/group.vue b/frontend/src/views/transfer/group.vue index 1ecee85f2..d58584b90 100644 --- a/frontend/src/views/transfer/group.vue +++ b/frontend/src/views/transfer/group.vue @@ -17,7 +17,7 @@ { - if (String(item.department_id) !== '0' || item.expired_at < this.user.timestamp) { - this.groupNotTransferCount += 1; - item.canNotTransfer = true; - } - }); this.groupList.splice(0, this.groupList.length, ...groupList); this.isEmpty = groupList.length < 1; this.emptyData = formatCodeData(code, this.emptyData, this.isEmpty); + this.handleGetCheckData(); } catch (e) { console.error(e); this.emptyData = formatCodeData(e.code, this.emptyData); @@ -159,6 +154,26 @@ } }, + handleGetCheckData () { + const selectGroup = this.groupSelectData.length + ? this.groupSelectData.map(item => String(item.id)) : []; + this.groupList.forEach(item => { + if (String(item.department_id) !== '0' || item.expired_at < this.user.timestamp) { + this.groupNotTransferCount += 1; + item.canNotTransfer = true; + } + setTimeout(() => { + if (selectGroup.includes(String(item.id))) { + this.$refs.groupTableRef && this.$refs.groupTableRef.toggleRowSelection(item, true); + } + if (this.groupSelectData.length < 1) { + this.$refs.groupTableRef && this.$refs.groupTableRef.clearSelection(); + } + }, 0); + }); + this.fetchSelectedGroupCount(); + }, + handleEmptyRefresh () { this.pagination = Object.assign(this.pagination, { current: 1, limit: 10 }); this.fetchData(); @@ -166,6 +181,9 @@ handleGroupExpanded () { this.groupExpanded = !this.groupExpanded; + if (this.groupExpanded) { + this.handleGetCheckData(); + } }, handleGroupShowAll () { @@ -186,26 +204,50 @@ } }, - handleSelectAll (selection) { - this.isSelectAllChecked = !!selection.length; - if (this.isSelectAllChecked) { - const validGroupList = this.groupList.filter(item => !item.canNotTransfer); - this.groupSelectData.splice( - 0, - this.groupSelectData.length, - ...validGroupList - ); - } else { - this.groupSelectData.splice(0, this.groupSelectData.length, ...[]); - } + fetchSelectedGroupCount () { + setTimeout(() => { + const paginationWrapper = this.$refs.groupTableRef.$refs.paginationWrapper; + if (paginationWrapper && paginationWrapper.getElementsByClassName('bk-page-selection-count')) { + const selectCount = paginationWrapper.getElementsByClassName('bk-page-selection-count'); + if (selectCount.length && selectCount[0].children && selectCount[0].children.length) { + selectCount[0].children[0].innerHTML = this.groupSelectData.length; + } + } + }, 0); + }, + fetchSelectedGroups (type, payload, row) { + const typeMap = { + multiple: () => { + const isChecked = payload.length && payload.indexOf(row) !== -1; + if (isChecked) { + this.groupSelectData.push(row); + } else { + this.groupSelectData = this.groupSelectData.filter((item) => item.id !== row.id); + } + this.fetchSelectedGroupCount(); + }, + all: () => { + const validGroupList = payload.filter(item => !item.canNotTransfer); + const selectGroups = this.groupSelectData.filter((item) => + !this.groupList.map((v) => v.id).includes(item.id)); + this.groupSelectData = [...selectGroups, ...validGroupList]; + this.fetchSelectedGroupCount(); + } + }; + return typeMap[type](); + }, + + handleSelectAll (selection) { + // this.isSelectAllChecked = !!selection.length; + this.fetchSelectedGroups('all', selection); this.$emit('group-selection-change', this.groupSelectData); }, - handleSelect (selection) { - const validGroupList = this.groupList.filter(item => !item.canNotTransfer); - this.isSelectAllChecked = selection.length === validGroupList.length; - this.groupSelectData.splice(0, this.groupSelectData.length, ...selection); + handleSelect (selection, row) { + // const validGroupList = this.groupList.filter(item => !item.canNotTransfer); + // this.isSelectAllChecked = selection.length === validGroupList.length; + this.fetchSelectedGroups('multiple', selection, row); this.$emit('group-selection-change', this.groupSelectData); }, diff --git a/frontend/src/views/transfer/manager.vue b/frontend/src/views/transfer/manager.vue index 08deeb059..5ebc116b4 100644 --- a/frontend/src/views/transfer/manager.vue +++ b/frontend/src/views/transfer/manager.vue @@ -10,11 +10,12 @@
String(item.id)) : []; + setTimeout(() => { + this.managerList.forEach(item => { + if (selectGroup.includes(String(item.id))) { + this.$refs.manageTableRef && this.$refs.manageTableRef.toggleRowSelection(item, true); + } + if (this.managerSelectData.length < 1) { + this.$refs.manageTableRef && this.$refs.manageTableRef.clearSelection(); + } + }); + }, 0); + this.fetchSelectedGroupCount(); + }, + handleRateExpanded () { this.rateExpanded = !this.rateExpanded; + if (this.rateExpanded) { + this.handleGetCheckData(); + } }, handleManagerShowAll () { @@ -159,25 +180,44 @@ } }, - handleSelectAll (selection) { - this.isSelectAllChecked = !!selection.length; - if (this.isSelectAllChecked) { - this.managerSelectData.splice( - 0, - this.managerSelectData.length, - ...this.managerList - ); - } else { - this.managerSelectData.splice(0, this.managerSelectData.length, ...[]); - } + fetchSelectedGroupCount () { + setTimeout(() => { + const paginationWrapper = this.$refs.manageTableRef.$refs.paginationWrapper; + const selectCount = paginationWrapper.getElementsByClassName('bk-page-selection-count'); + if (selectCount.length && selectCount[0].children && selectCount[0].children.length) { + selectCount[0].children[0].innerHTML = this.managerSelectData.length; + } + }, 0); + }, - this.$emit('manager-selection-change', this.managerSelectData); + fetchSelectedGroups (type, payload, row) { + const typeMap = { + multiple: () => { + const isChecked = payload.length && payload.indexOf(row) !== -1; + if (isChecked) { + this.managerSelectData.push(row); + } else { + this.managerSelectData = this.managerSelectData.filter((item) => item.id !== row.id); + } + this.fetchSelectedGroupCount(); + }, + all: () => { + const selectGroups = this.managerSelectData.filter((item) => + !this.managerList.map((v) => v.id).includes(item.id)); + this.managerSelectData = [...selectGroups, ...payload]; + this.fetchSelectedGroupCount(); + } + }; + return typeMap[type](); }, - handleSelect (selection) { - this.isSelectAllChecked = selection.length === this.managerList.length; - this.managerSelectData.splice(0, this.managerSelectData.length, ...selection); + handleSelectAll (selection) { + this.fetchSelectedGroups('all', selection); + this.$emit('manager-selection-change', this.managerSelectData); + }, + handleSelect (selection, row) { + this.fetchSelectedGroups('multiple', selection, row); this.$emit('manager-selection-change', this.managerSelectData); }, diff --git a/saas/backend/apps/group/serializers.py b/saas/backend/apps/group/serializers.py index a86f5c321..a499f9eed 100644 --- a/saas/backend/apps/group/serializers.py +++ b/saas/backend/apps/group/serializers.py @@ -276,6 +276,16 @@ class GroupMemberUpdateExpiredAtSLZ(serializers.Serializer): members = serializers.ListField(label="成员列表", child=GroupMemberExpiredAtSLZ(label="成员"), allow_empty=False) +class GroupMemberExpiredAtItemSLZ(GroupMemberExpiredAtSLZ): + group_id = serializers.IntegerField(label="用户组ID") + + +class BatchGroupMemberUpdateExpiredAtSLZ(serializers.Serializer): + group_members = serializers.ListField( + label="成员列表", child=GroupMemberExpiredAtItemSLZ(label="成员"), allow_empty=False + ) + + class GroupPolicyUpdateSLZ(serializers.Serializer): system_id = serializers.CharField(label="系统ID") template_id = serializers.IntegerField(label="模板ID", required=False, default=0) diff --git a/saas/backend/apps/group/urls.py b/saas/backend/apps/group/urls.py index 91c416339..3b5603042 100644 --- a/saas/backend/apps/group/urls.py +++ b/saas/backend/apps/group/urls.py @@ -24,6 +24,11 @@ views.GroupsMemberViewSet.as_view({"post": "destroy"}), name="group.members_delete", ), + path( + "members/renew/", + views.GroupsMemberRenewViewSet.as_view({"post": "create"}), + name="group.members_renew", + ), path("transfer/", views.GroupTransferView.as_view(), name="group.transfer"), path("search/", views.GroupSearchViewSet.as_view({"post": "search"}), name="group.search"), # 用户组详情 diff --git a/saas/backend/apps/group/views.py b/saas/backend/apps/group/views.py index 94c69e08e..0f59c3d99 100644 --- a/saas/backend/apps/group/views.py +++ b/saas/backend/apps/group/views.py @@ -68,6 +68,7 @@ from .filters import GroupFilter, GroupSubjectTemplateFilter, GroupTemplateSystemFilter from .serializers import ( BatchGroupDeleteMemberSLZ, + BatchGroupMemberUpdateExpiredAtSLZ, GradeManagerGroupTransferSLZ, GroupAddMemberSLZ, GroupAuthorizationSLZ, @@ -561,6 +562,47 @@ def destroy(self, request, *args, **kwargs): raise error_codes.ACTIONS_PARTIAL_FAILED.format(failed_info) +class GroupsMemberRenewViewSet(GenericViewSet): + + group_biz = GroupBiz() + + @swagger_auto_schema( + operation_description="批量用户组添加成员", + request_body=BatchGroupMemberUpdateExpiredAtSLZ(label="成员"), + responses={status.HTTP_200_OK: serializers.Serializer()}, + tags=["group"], + ) + def create(self, request, *args, **kwargs): + serializer = BatchGroupMemberUpdateExpiredAtSLZ(data=request.data) + serializer.is_valid(raise_exception=True) + + data = serializer.validated_data + + role_checker = RoleObjectRelationChecker(request.role) + for group_member in data["group_members"]: + group = Group.objects.filter(id=group_member["group_id"]).first() + if not group: + continue + + if not role_checker.check_group(group): + self.permission_denied(request, message=f"{request.role.type} role can not access group {group.id}") + + if group_member["type"] != GroupMemberType.TEMPLATE.value: + member = parse_obj_as(GroupMemberExpiredAtBean, group_member) + self.group_biz.update_members_expired_at(group.id, [member]) + + # 处理人员模版的续期 + if group_member["type"] == GroupMemberType.TEMPLATE.value: + self.group_biz.update_subject_template_expired_at( + group.id, int(group_member["id"]), group_member["expired_at"] + ) + + # 写入审计上下文 + audit_context_setter(group=group, members=data["members"]) + + return Response({}) + + class GroupMemberUpdateExpiredAtViewSet(GroupPermissionMixin, GenericViewSet): permission_classes = [role_perm_class(PermissionCodeEnum.MANAGE_GROUP.value)]