mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 10:12:51 +00:00
feat: Retrieve the pagination list of resource relationships (#4570)
This commit is contained in:
parent
592e429e3b
commit
97ae4e012b
|
|
@ -8886,4 +8886,10 @@ msgid "Role IDs cannot be empty"
|
|||
msgstr ""
|
||||
|
||||
msgid "Some roles do not exist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Authorized pagination list for obtaining resources"
|
||||
msgstr ""
|
||||
|
||||
msgid "Resources mapping"
|
||||
msgstr ""
|
||||
|
|
@ -9013,3 +9013,9 @@ msgstr "角色 ID 不能为空"
|
|||
|
||||
msgid "Some roles do not exist"
|
||||
msgstr "部分角色不存在"
|
||||
|
||||
msgid "Authorized pagination list for obtaining resources"
|
||||
msgstr "获取资源的关系分页列表"
|
||||
|
||||
msgid "Resources mapping"
|
||||
msgstr "资源映射"
|
||||
|
|
@ -9013,3 +9013,9 @@ msgstr "角色 ID 不能为空"
|
|||
|
||||
msgid "Some roles do not exist"
|
||||
msgstr "部分角色不存在"
|
||||
|
||||
msgid "Authorized pagination list for obtaining resources"
|
||||
msgstr "獲取資源的關係分頁清單"
|
||||
|
||||
msgid "Resources mapping"
|
||||
msgstr "資源映射"
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: resource_mapping.py
|
||||
@date:2025/12/26 14:07
|
||||
@desc:
|
||||
"""
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.mixins.api_mixin import APIMixin
|
||||
|
||||
|
||||
class ResourceMappingResponse(serializers.Serializer):
|
||||
id = serializers.UUIDField(required=True, label="主键id")
|
||||
target_id = serializers.CharField(required=True, label="被关联资源名称")
|
||||
target_type = serializers.CharField(required=True, label="被关联资源类型")
|
||||
source_id = serializers.CharField(required=True, label="关联资源Id")
|
||||
source_type = serializers.CharField(required=True, label="关联资源类型")
|
||||
name = serializers.CharField(required=True, label="名称")
|
||||
desc = serializers.CharField(required=False, label="描述")
|
||||
user_id = serializers.UUIDField(required=True, label="主键id")
|
||||
|
||||
|
||||
class ResourceMappingAPI(APIMixin):
|
||||
|
||||
@staticmethod
|
||||
def get_parameters():
|
||||
return [
|
||||
OpenApiParameter(
|
||||
name="workspace_id",
|
||||
description="工作空间id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="source",
|
||||
description="资源类型",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="source_id",
|
||||
description="资源id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="current_page",
|
||||
description=_("Current page"),
|
||||
type=OpenApiTypes.INT,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="page_size",
|
||||
description=_("Page size"),
|
||||
type=OpenApiTypes.INT,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="resource_name",
|
||||
description="名称",
|
||||
type=OpenApiTypes.STR,
|
||||
location='query',
|
||||
required=False
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_response():
|
||||
return ResourceMappingResponse(many=True)
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: workspace_user_resource_permission.py
|
||||
@date:2025/4/28 17:17
|
||||
@desc:
|
||||
"""
|
||||
import os
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.db.search import native_page_search, get_dynamics_model
|
||||
from common.utils.common import get_file_content
|
||||
from maxkb.conf import PROJECT_DIR
|
||||
|
||||
|
||||
class ResourceMappingSerializer(serializers.Serializer):
|
||||
resource = serializers.CharField(required=True, label=_('resource'))
|
||||
resource_id = serializers.UUIDField(required=True, label=_('resource Id'))
|
||||
resource_name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('resource'))
|
||||
|
||||
def get_query_set(self):
|
||||
queryset = QuerySet(model=get_dynamics_model({
|
||||
'name': models.CharField(),
|
||||
'target_id': models.CharField(),
|
||||
"target_type": models.CharField()
|
||||
}))
|
||||
|
||||
queryset = queryset.filter(target_id=self.data.get('resource_id'),
|
||||
target_type=self.data.get('resource'))
|
||||
|
||||
if self.data.get('resource_name'):
|
||||
queryset = queryset.filter(name__icontains=self.data.get('resource_name'))
|
||||
|
||||
return queryset
|
||||
|
||||
def page(self, current_page, page_size):
|
||||
return native_page_search(current_page, page_size, self.get_query_set(), get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "system_manage",
|
||||
'sql', 'list_resource_mapping.sql')), with_table_name=False)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
WITH source_data_cte AS (
|
||||
SELECT 'APPLICATION' as source_type, id, "name", "desc","user_id"
|
||||
FROM application
|
||||
UNION ALL
|
||||
SELECT 'KNOWLEDGE' as source_type, id, "name", "desc","user_id"
|
||||
FROM knowledge)
|
||||
SELECT rm.*,
|
||||
sdc.*
|
||||
FROM resource_mapping rm
|
||||
LEFT JOIN source_data_cte sdc
|
||||
ON rm.source_type = sdc.source_type
|
||||
AND rm.source_id::uuid = sdc.id
|
||||
|
|
@ -9,6 +9,7 @@ urlpatterns = [
|
|||
path('workspace/<str:workspace_id>/user_resource_permission/user/<str:user_id>/resource/<str:resource>/<int:current_page>/<int:page_size>', views.WorkSpaceUserResourcePermissionView.Page.as_view()),
|
||||
path('workspace/<str:workspace_id>/resource_user_permission/resource/<str:target>/resource/<str:resource>', views.WorkspaceResourceUserPermissionView.as_view()),
|
||||
path('workspace/<str:workspace_id>/resource_user_permission/resource/<str:target>/resource/<str:resource>/<int:current_page>/<int:page_size>', views.WorkspaceResourceUserPermissionView.Page.as_view()),
|
||||
path('workspace/<str:workspace_id>/resource_mapping/<str:resource>/<str:resource_id>/<int:current_page>/<int:page_size>', views.ResourceMappingView.as_view()),
|
||||
path('email_setting', views.SystemSetting.Email.as_view()),
|
||||
path('profile', views.SystemProfile.as_view()),
|
||||
path('valid/<str:valid_type>/<int:valid_count>', views.Valid.as_view())
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ from .user_resource_permission import *
|
|||
from .email_setting import *
|
||||
from .system_profile import *
|
||||
from .valid import *
|
||||
from .resource_mapping import *
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎虎
|
||||
@file: resource_mapping.py
|
||||
@date:2025/12/25 15:28
|
||||
@desc:
|
||||
"""
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from common import result
|
||||
from common.auth import TokenAuth
|
||||
from system_manage.api.resource_mapping import ResourceMappingAPI
|
||||
from system_manage.serializers.resource_mapping_serializers import ResourceMappingSerializer
|
||||
|
||||
|
||||
class ResourceMappingView(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@extend_schema(
|
||||
methods=['GET'],
|
||||
description=_('Retrieve the pagination list of resource relationships'),
|
||||
operation_id=_('Retrieve the pagination list of resource relationships'), # type: ignore
|
||||
responses=ResourceMappingAPI.get_response(),
|
||||
parameters=ResourceMappingAPI.get_parameters(),
|
||||
tags=[_('Resources mapping')] # type: ignore
|
||||
)
|
||||
def get(self, request: Request, workspace_id: str, resource: str, resource_id: str, current_page, page_size):
|
||||
return result.success(ResourceMappingSerializer({
|
||||
'resource': resource,
|
||||
'resource_id': resource_id,
|
||||
'resource_name': request.query_params.get('resource_name')
|
||||
}).page(current_page, page_size))
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, put, post, del } from '@/request/index'
|
||||
import type { Ref } from 'vue'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
const prefix = '/workspace'
|
||||
|
||||
/**
|
||||
* 工作空间下各个资源的映射关系
|
||||
* @query 参数
|
||||
*/
|
||||
const getResourceMapping: (
|
||||
workspace_id: string,
|
||||
resource: string,
|
||||
resource_id: string,
|
||||
page: pageRequest,
|
||||
params?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (workspace_id, resource, resource_id, page, params, loading) => {
|
||||
return get(
|
||||
`${prefix}/${workspace_id}/resource_mapping/${resource}/${resource_id}/${page.current_page}/${page.page_size}`,
|
||||
params,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
getResourceMapping,
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
v-model="visible"
|
||||
:title="$t('views.system.resourceAuthorization.title')"
|
||||
size="60%"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<div class="flex-between mb-16">
|
||||
<div class="flex-between complex-search">
|
||||
<el-select class="complex-search__left" v-model="searchType" style="width: 100px">
|
||||
<el-option
|
||||
:label="$t('views.userManage.userForm.resourceName.label')"
|
||||
value="resource_name"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="searchType === 'resource_name'"
|
||||
v-model="query.resource_name"
|
||||
:placeholder="$t('common.search')"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
@keyup.enter="pageResouceMapping()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-table
|
||||
ref="multipleTableRef"
|
||||
class="mt-16"
|
||||
:data="tableData"
|
||||
:pagination-config="paginationConfig"
|
||||
@sizeChange="handleSizeChange"
|
||||
@changePage="pageResouceMapping"
|
||||
:maxTableHeight="200"
|
||||
:row-key="(row: any) => row.id"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('views.userManage.userForm.name.label', '名称')"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="desc"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
:label="$t('views.login.loginForm.desc.label', '描述')"
|
||||
/>
|
||||
</app-table>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||
import useStore from '@/stores'
|
||||
const route = useRoute()
|
||||
const { user } = useStore()
|
||||
const searchType = ref<string>('resource_name')
|
||||
const query = ref<any>({
|
||||
resource_name: '',
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
const tableData = ref<Array<any>>()
|
||||
const visible = ref<boolean>(false)
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 20,
|
||||
total: 0,
|
||||
})
|
||||
const apiType = computed(() => {
|
||||
if (route.path.includes('resource-management')) {
|
||||
return 'systemManage'
|
||||
} else {
|
||||
return 'workspace'
|
||||
}
|
||||
})
|
||||
|
||||
const pageResouceMapping = () => {
|
||||
const workspaceId = user.getWorkspaceId() || 'default'
|
||||
const params: any = {}
|
||||
if (query.value[searchType.value]) {
|
||||
params[searchType.value] = query.value[searchType.value]
|
||||
}
|
||||
loadSharedApi({ type: 'resourceMapping', systemType: apiType.value })
|
||||
.getResourceMapping(
|
||||
workspaceId,
|
||||
currentSource.value,
|
||||
currentSourceId.value,
|
||||
paginationConfig,
|
||||
params,
|
||||
loading,
|
||||
)
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data.records || []
|
||||
paginationConfig.total = res.data.total || 0
|
||||
})
|
||||
}
|
||||
function handleSizeChange() {
|
||||
paginationConfig.current_page = 1
|
||||
pageResouceMapping()
|
||||
}
|
||||
const currentSource = ref<string>()
|
||||
const currentSourceId = ref<string>()
|
||||
const open = (source: string, sourceId: string) => {
|
||||
visible.value = true
|
||||
currentSource.value = source
|
||||
currentSourceId.value = sourceId
|
||||
pageResouceMapping()
|
||||
}
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
paginationConfig.current_page = 1
|
||||
}
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -2,6 +2,7 @@ import knowledgeWorkspaceApi from '@/api/knowledge/knowledge'
|
|||
import documentWorkspaceApi from '@/api/knowledge/document'
|
||||
import paragraphWorkspaceApi from '@/api/knowledge/paragraph'
|
||||
import problemWorkspaceApi from '@/api/knowledge/problem'
|
||||
import resourceMappingApi from '@/api/workspace/resource-mapping'
|
||||
import modelWorkspaceApi from '@/api/model/model'
|
||||
import toolWorkspaceApi from '@/api/tool/tool'
|
||||
import chatUserWorkspaceApi from '@/api/chat-user/chat-user'
|
||||
|
|
@ -34,6 +35,7 @@ import workflowVersionResourceApi from '@/api/system-resource-management/workflo
|
|||
import chatLogResourceApi from '@/api/system-resource-management/chat-log'
|
||||
import resourceAuthorizationResourceApi from '@/api/system-resource-management/resource-authorization'
|
||||
import folderResourceApi from '@/api/system-resource-management/folder'
|
||||
|
||||
// 普通 API
|
||||
const workspaceApiMap = {
|
||||
knowledge: knowledgeWorkspaceApi,
|
||||
|
|
@ -50,6 +52,7 @@ const workspaceApiMap = {
|
|||
chatLog: chatLogWorkspaceApi,
|
||||
resourceAuthorization: resourceAuthorizationWorkspaceApi,
|
||||
folder: folderWorkspaceApi,
|
||||
resourceMapping: resourceMappingApi,
|
||||
} as any
|
||||
|
||||
// 系统分享 API
|
||||
|
|
|
|||
|
|
@ -104,10 +104,21 @@
|
|||
<AppIcon iconName="app-setting" class="color-secondary"></AppIcon>
|
||||
{{ $t('views.model.modelForm.title.paramSetting') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="openAuthorization(model)" v-if="apiType === 'workspace' && permissionPrecise.auth(model.id)">
|
||||
<el-dropdown-item
|
||||
@click.stop="openAuthorization(model)"
|
||||
v-if="apiType === 'workspace' && permissionPrecise.auth(model.id)"
|
||||
>
|
||||
<AppIcon iconName="app-resource-authorization" class="color-secondary"></AppIcon>
|
||||
{{ $t('views.system.resourceAuthorization.title') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
text
|
||||
@click.stop="openResourceMappingDrawer(model)"
|
||||
v-if="permissionPrecise.delete(model.id)"
|
||||
>
|
||||
<AppIcon iconName="app-delete" class="color-secondary"></AppIcon>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
divided
|
||||
text
|
||||
|
|
@ -132,6 +143,7 @@
|
|||
ref="ResourceAuthorizationDrawerRef"
|
||||
v-if="apiType === 'workspace'"
|
||||
/>
|
||||
<ResourceMappingDrawer ref="resourceMappingDrawerRef"></ResourceMappingDrawer>
|
||||
</card-box>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -144,13 +156,14 @@ import { modelType } from '@/enums/model'
|
|||
import ParamSettingDialog from './ParamSettingDialog.vue'
|
||||
import AuthorizedWorkspace from '@/views/system-shared/AuthorizedWorkspaceDialog.vue'
|
||||
import ResourceAuthorizationDrawer from '@/components/resource-authorization-drawer/index.vue'
|
||||
import ResourceMappingDrawer from '@/components/resource_mapping/index.vue'
|
||||
import { SourceTypeEnum } from '@/enums/common'
|
||||
import { t } from '@/locales'
|
||||
import { i18n_name } from '@/utils/common'
|
||||
import permissionMap from '@/permission'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||
|
||||
const resourceMappingDrawerRef = ref<InstanceType<typeof ResourceMappingDrawer>>()
|
||||
const route = useRoute()
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -161,7 +174,9 @@ const props = defineProps<{
|
|||
isSystemShare?: boolean | undefined
|
||||
apiType: 'systemShare' | 'workspace' | 'systemManage'
|
||||
}>()
|
||||
|
||||
const openResourceMappingDrawer = (model: any) => {
|
||||
resourceMappingDrawerRef.value?.open('MODEL', model.id)
|
||||
}
|
||||
const isSystemShare = computed(() => {
|
||||
return props.apiType === 'systemShare'
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue