mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: Folder move
This commit is contained in:
parent
8e3550eb83
commit
eb867daa6c
|
|
@ -57,7 +57,7 @@ def get_folder_tree_serializer(source):
|
|||
return None
|
||||
|
||||
|
||||
FOLDER_DEPTH = 2 # Folder 不能超过3层
|
||||
FOLDER_DEPTH = 10000
|
||||
|
||||
|
||||
def check_depth(source, parent_id, workspace_id, current_depth=0):
|
||||
|
|
@ -79,7 +79,7 @@ def check_depth(source, parent_id, workspace_id, current_depth=0):
|
|||
|
||||
# 验证层级深度
|
||||
if depth + current_depth > FOLDER_DEPTH:
|
||||
raise serializers.ValidationError(_('Folder depth cannot exceed 3 levels'))
|
||||
raise serializers.ValidationError(_('Folder depth cannot exceed 10000 levels'))
|
||||
|
||||
|
||||
def get_max_depth(current_node):
|
||||
|
|
@ -100,6 +100,12 @@ def get_max_depth(current_node):
|
|||
return max_depth
|
||||
|
||||
|
||||
def has_target_permission(workspace_id, source, user_id, target):
|
||||
return QuerySet(WorkspaceUserResourcePermission).filter(workspace_id=workspace_id, user_id=user_id,
|
||||
auth_target_type=source, target=target,
|
||||
permission_list__contains=['MANAGE']).exists()
|
||||
|
||||
|
||||
class FolderSerializer(serializers.Serializer):
|
||||
id = serializers.CharField(required=True, label=_('folder id'))
|
||||
name = serializers.CharField(required=True, label=_('folder name'))
|
||||
|
|
@ -185,11 +191,22 @@ class FolderSerializer(serializers.Serializer):
|
|||
QuerySet(Folder).filter(id=current_id).update(**edit_dict)
|
||||
|
||||
if parent_id is not None and current_id != current_node.workspace_id and current_node.parent_id != parent_id:
|
||||
# Folder 不能超过3层
|
||||
current_depth = get_max_depth(current_node)
|
||||
check_depth(self.data.get('source'), parent_id, current_node.workspace_id, current_depth)
|
||||
parent = Folder.objects.get(id=parent_id)
|
||||
current_node.move_to(parent)
|
||||
|
||||
source_type = self.data.get('source')
|
||||
if has_target_permission(current_node.workspace_id, source_type, self.data.get('user_id'),
|
||||
parent_id) or is_workspace_manage(self.data.get('user_id'),
|
||||
current_node.workspace_id):
|
||||
current_depth = get_max_depth(current_node)
|
||||
check_depth(self.data.get('source'), parent_id, current_node.workspace_id, current_depth)
|
||||
parent = Folder.objects.get(id=parent_id)
|
||||
|
||||
if QuerySet(Folder).filter(name=current_node.name, parent_id=parent_id,
|
||||
workspace_id=current_node.workspace_id).exists():
|
||||
raise serializers.ValidationError(_('Folder name already exists'))
|
||||
|
||||
current_node.move_to(parent)
|
||||
else:
|
||||
raise AppApiException(403, _('No permission for the target folder'))
|
||||
|
||||
return self.one()
|
||||
|
||||
|
|
|
|||
|
|
@ -2207,7 +2207,7 @@ msgid "parent id"
|
|||
msgstr ""
|
||||
|
||||
#: apps/folders/serializers/folder.py:75
|
||||
msgid "Folder depth cannot exceed 3 levels"
|
||||
msgid "Folder depth cannot exceed 5 levels"
|
||||
msgstr ""
|
||||
|
||||
#: apps/folders/serializers/folder.py:100
|
||||
|
|
@ -8763,4 +8763,7 @@ msgid "Tag value already exists"
|
|||
msgstr ""
|
||||
|
||||
msgid "Non-existent id"
|
||||
msgstr ""
|
||||
|
||||
msgid "No permission for the target folder"
|
||||
msgstr ""
|
||||
|
|
@ -2214,8 +2214,8 @@ msgid "parent id"
|
|||
msgstr "父级 ID"
|
||||
|
||||
#: apps/folders/serializers/folder.py:75
|
||||
msgid "Folder depth cannot exceed 3 levels"
|
||||
msgstr "文件夹深度不能超过3级"
|
||||
msgid "Folder depth cannot exceed 5 levels"
|
||||
msgstr "文件夹深度不能超过5级"
|
||||
|
||||
#: apps/folders/serializers/folder.py:100
|
||||
msgid "folder user id"
|
||||
|
|
@ -8890,3 +8890,7 @@ msgstr "标签值已存在"
|
|||
|
||||
msgid "Non-existent id"
|
||||
msgstr "不存在的ID"
|
||||
|
||||
msgid "No permission for the target folder"
|
||||
msgstr "没有目标文件夹的权限"
|
||||
|
||||
|
|
|
|||
|
|
@ -2214,8 +2214,8 @@ msgid "parent id"
|
|||
msgstr "父級 ID"
|
||||
|
||||
#: apps/folders/serializers/folder.py:75
|
||||
msgid "Folder depth cannot exceed 3 levels"
|
||||
msgstr "文件夾深度不能超過3級"
|
||||
msgid "Folder depth cannot exceed 5 levels"
|
||||
msgstr "文件夾深度不能超過5級"
|
||||
|
||||
#: apps/folders/serializers/folder.py:100
|
||||
msgid "folder user id"
|
||||
|
|
@ -8890,3 +8890,7 @@ msgstr "標籤值已存在"
|
|||
|
||||
msgid "Non-existent id"
|
||||
msgstr "不存在的ID"
|
||||
|
||||
msgid "No permission for the target folder"
|
||||
msgstr "沒有目標資料夾的權限"
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import folderApi from '@/api/folder'
|
||||
import { MsgError, MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores'
|
||||
|
|
@ -71,8 +72,11 @@ watch(dialogVisible, (bool) => {
|
|||
}
|
||||
})
|
||||
|
||||
const open = (data: any) => {
|
||||
const isFolder = ref<boolean>(false)
|
||||
|
||||
const open = (data: any, is_folder?:any) => {
|
||||
detail.value = data
|
||||
isFolder.value = is_folder
|
||||
getFolder()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
|
@ -99,7 +103,19 @@ const submitHandle = async () => {
|
|||
...detail.value,
|
||||
folder_id: selectForderId.value,
|
||||
}
|
||||
if (props.source === SourceTypeEnum.KNOWLEDGE) {
|
||||
if (isFolder.value) {
|
||||
const folder_obj = {
|
||||
...detail.value,
|
||||
parent_id: selectForderId.value,
|
||||
}
|
||||
folderApi.putFolder(detail.value.id, detail.value.folder_type, folder_obj, loading)
|
||||
.then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
else if (props.source === SourceTypeEnum.KNOWLEDGE) {
|
||||
if (detail.value.type === 2) {
|
||||
KnowledgeApi.putLarkKnowledge(detail.value.id, obj, loading).then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@
|
|||
:current-node-key="currentNodeKey"
|
||||
highlight-current
|
||||
class="overflow-inherit_node__children"
|
||||
draggable
|
||||
:allow-drop="allowDrop"
|
||||
:allow-drag="allowDrag"
|
||||
@node-drop="handleDrop"
|
||||
node-key="id"
|
||||
v-loading="loading"
|
||||
v-bind="$attrs"
|
||||
|
|
@ -63,7 +67,7 @@
|
|||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click.stop="openCreateFolder(data)"
|
||||
v-if="node.level !== 3 && permissionPrecise.folderCreate(data.id)"
|
||||
v-if="permissionPrecise.folderCreate(data.id)"
|
||||
>
|
||||
<AppIcon iconName="app-add-folder" class="color-secondary"></AppIcon>
|
||||
{{ $t('components.folder.addChildFolder') }}
|
||||
|
|
@ -75,6 +79,13 @@
|
|||
<AppIcon iconName="app-edit" class="color-secondary"></AppIcon>
|
||||
{{ $t('common.edit') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.stop="openMoveToDialog(data)"
|
||||
v-if="node.level !== 1 && permissionPrecise.folderEdit(data.id)"
|
||||
>
|
||||
<AppIcon iconName="app-migrate" class="color-secondary"></AppIcon>
|
||||
{{ $t('common.moveTo') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
@click.stop="openAuthorization(data)"
|
||||
v-if="permissionPrecise.folderAuth(data.id)"
|
||||
|
|
@ -101,6 +112,11 @@
|
|||
</el-scrollbar>
|
||||
</div>
|
||||
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" :title="title" />
|
||||
<MoveToDialog
|
||||
ref="MoveToDialogRef"
|
||||
:source="props.source"
|
||||
@refresh="emit('refreshTree')"
|
||||
/>
|
||||
<ResourceAuthorizationDrawer
|
||||
:type="props.source"
|
||||
:is-folder="true"
|
||||
|
|
@ -117,13 +133,14 @@ import type { TreeInstance } from 'element-plus'
|
|||
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
|
||||
import ResourceAuthorizationDrawer from '@/components/resource-authorization-drawer/index.vue'
|
||||
import { t } from '@/locales'
|
||||
import MoveToDialog from '@/components/folder-tree/MoveToDialog.vue'
|
||||
import { i18n_name } from '@/utils/common'
|
||||
import folderApi from '@/api/folder'
|
||||
import { EditionConst } from '@/utils/permission/data'
|
||||
import { hasPermission } from '@/utils/permission/index'
|
||||
import useStore from '@/stores'
|
||||
import { TreeToFlatten } from '@/utils/array'
|
||||
import { MsgConfirm } from '@/utils/message'
|
||||
import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message'
|
||||
import permissionMap from '@/permission'
|
||||
import bus from '@/bus'
|
||||
defineOptions({ name: 'FolderTree' })
|
||||
|
|
@ -177,13 +194,59 @@ const permissionPrecise = computed(() => {
|
|||
|
||||
const MoreFilledPermission = (node: any, data: any) => {
|
||||
return (
|
||||
(node.level !== 3 && permissionPrecise.value.folderCreate(data.id)) ||
|
||||
permissionPrecise.value.folderCreate(data.id) ||
|
||||
permissionPrecise.value.folderEdit(data.id) ||
|
||||
permissionPrecise.value.folderDelete(data.id) ||
|
||||
permissionPrecise.value.folderAuth(data.id)
|
||||
)
|
||||
}
|
||||
|
||||
const MoveToDialogRef = ref()
|
||||
function openMoveToDialog(data:any) {
|
||||
const obj = {
|
||||
id: data.id,
|
||||
folder_type: props.source,
|
||||
}
|
||||
MoveToDialogRef.value.open(obj, true)
|
||||
}
|
||||
|
||||
const allowDrag = (node: any) => {
|
||||
return permissionPrecise.value.folderEdit(node.data.id)
|
||||
}
|
||||
|
||||
const allowDrop = (draggingNode: any, dropNode: any, type: string) => {
|
||||
const dropData = dropNode.data
|
||||
if (type === 'inner') {
|
||||
return permissionPrecise.value.folderEdit(dropData.id)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const handleDrop = (draggingNode: any, dropNode: any, dropType: string, ev: DragEvent) => {
|
||||
const dragData = draggingNode.data
|
||||
const dropData = dropNode.data
|
||||
|
||||
let newParentId: string
|
||||
if (dropType === 'inner') {
|
||||
newParentId = dropData.id
|
||||
} else {
|
||||
newParentId = dropData.parent_id
|
||||
}
|
||||
const obj = {
|
||||
...dragData,
|
||||
parent_id: newParentId
|
||||
}
|
||||
folderApi.putFolder(dragData.id, props.source, obj, loading)
|
||||
.then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
emit('refreshTree')
|
||||
})
|
||||
.catch(() => {
|
||||
MsgError(t('components.folder.requiredMessage'))
|
||||
emit('refreshTree')
|
||||
})
|
||||
}
|
||||
|
||||
const { folder } = useStore()
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
folder.setCurrentFolder({})
|
||||
|
|
@ -328,6 +391,19 @@ onUnmounted(() => {
|
|||
height: calc(100vh - 210px);
|
||||
}
|
||||
}
|
||||
:deep(.el-tree) {
|
||||
.el-tree-node.is-dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.el-tree-node.is-drop-inner > .el-tree-node__content {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 2px dashed var(--el-color-primary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.el-tree-node__content {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
:deep(.overflow-inherit_node__children) {
|
||||
.el-tree-node__children {
|
||||
overflow: inherit !important;
|
||||
|
|
|
|||
Loading…
Reference in New Issue