Skip to content

Commit 4a4c416

Browse files
committed
feat: Folder move
1 parent 8e3550e commit 4a4c416

File tree

6 files changed

+137
-17
lines changed

6 files changed

+137
-17
lines changed

apps/folders/serializers/folder.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def get_folder_tree_serializer(source):
5757
return None
5858

5959

60-
FOLDER_DEPTH = 2 # Folder 不能超过3层
60+
FOLDER_DEPTH = 10000
6161

6262

6363
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):
7979

8080
# 验证层级深度
8181
if depth + current_depth > FOLDER_DEPTH:
82-
raise serializers.ValidationError(_('Folder depth cannot exceed 3 levels'))
82+
raise serializers.ValidationError(_('Folder depth cannot exceed 10000 levels'))
8383

8484

8585
def get_max_depth(current_node):
@@ -100,6 +100,12 @@ def get_max_depth(current_node):
100100
return max_depth
101101

102102

103+
def has_target_permission(workspace_id, source, user_id, target):
104+
return QuerySet(WorkspaceUserResourcePermission).filter(workspace_id=workspace_id, user_id=user_id,
105+
auth_target_type=source, target=target,
106+
permission_list__contains=['MANAGE']).exists()
107+
108+
103109
class FolderSerializer(serializers.Serializer):
104110
id = serializers.CharField(required=True, label=_('folder id'))
105111
name = serializers.CharField(required=True, label=_('folder name'))
@@ -185,11 +191,22 @@ def edit(self, instance):
185191
QuerySet(Folder).filter(id=current_id).update(**edit_dict)
186192

187193
if parent_id is not None and current_id != current_node.workspace_id and current_node.parent_id != parent_id:
188-
# Folder 不能超过3层
189-
current_depth = get_max_depth(current_node)
190-
check_depth(self.data.get('source'), parent_id, current_node.workspace_id, current_depth)
191-
parent = Folder.objects.get(id=parent_id)
192-
current_node.move_to(parent)
194+
195+
source_type = self.data.get('source')
196+
if has_target_permission(current_node.workspace_id, source_type, self.data.get('user_id'),
197+
parent_id) or is_workspace_manage(self.data.get('user_id'),
198+
current_node.workspace_id):
199+
current_depth = get_max_depth(current_node)
200+
check_depth(self.data.get('source'), parent_id, current_node.workspace_id, current_depth)
201+
parent = Folder.objects.get(id=parent_id)
202+
203+
if QuerySet(Folder).filter(name=current_node.name, parent_id=parent_id,
204+
workspace_id=current_node.workspace_id).exists():
205+
raise serializers.ValidationError(_('Folder name already exists'))
206+
207+
current_node.move_to(parent)
208+
else:
209+
raise AppApiException(403, _('No permission for the target folder'))
193210

194211
return self.one()
195212

apps/locales/en_US/LC_MESSAGES/django.po

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2207,7 +2207,7 @@ msgid "parent id"
22072207
msgstr ""
22082208

22092209
#: apps/folders/serializers/folder.py:75
2210-
msgid "Folder depth cannot exceed 3 levels"
2210+
msgid "Folder depth cannot exceed 5 levels"
22112211
msgstr ""
22122212

22132213
#: apps/folders/serializers/folder.py:100
@@ -8763,4 +8763,7 @@ msgid "Tag value already exists"
87638763
msgstr ""
87648764

87658765
msgid "Non-existent id"
8766+
msgstr ""
8767+
8768+
msgid "No permission for the target folder"
87668769
msgstr ""

apps/locales/zh_CN/LC_MESSAGES/django.po

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,8 +2214,8 @@ msgid "parent id"
22142214
msgstr "父级 ID"
22152215

22162216
#: apps/folders/serializers/folder.py:75
2217-
msgid "Folder depth cannot exceed 3 levels"
2218-
msgstr "文件夹深度不能超过3级"
2217+
msgid "Folder depth cannot exceed 5 levels"
2218+
msgstr "文件夹深度不能超过5级"
22192219

22202220
#: apps/folders/serializers/folder.py:100
22212221
msgid "folder user id"
@@ -8890,3 +8890,7 @@ msgstr "标签值已存在"
88908890

88918891
msgid "Non-existent id"
88928892
msgstr "不存在的ID"
8893+
8894+
msgid "No permission for the target folder"
8895+
msgstr "没有目标文件夹的权限"
8896+

apps/locales/zh_Hant/LC_MESSAGES/django.po

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,8 +2214,8 @@ msgid "parent id"
22142214
msgstr "父級 ID"
22152215

22162216
#: apps/folders/serializers/folder.py:75
2217-
msgid "Folder depth cannot exceed 3 levels"
2218-
msgstr "文件夾深度不能超過3級"
2217+
msgid "Folder depth cannot exceed 5 levels"
2218+
msgstr "文件夾深度不能超過5級"
22192219

22202220
#: apps/folders/serializers/folder.py:100
22212221
msgid "folder user id"
@@ -8890,3 +8890,7 @@ msgstr "標籤值已存在"
88908890

88918891
msgid "Non-existent id"
88928892
msgstr "不存在的ID"
8893+
8894+
msgid "No permission for the target folder"
8895+
msgstr "沒有目標資料夾的權限"
8896+

ui/src/components/folder-tree/MoveToDialog.vue

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
</template>
3737
<script setup lang="ts">
3838
import { ref, watch, reactive } from 'vue'
39+
import folderApi from '@/api/folder'
3940
import { MsgError, MsgSuccess } from '@/utils/message'
4041
import { t } from '@/locales'
4142
import useStore from '@/stores'
@@ -71,8 +72,11 @@ watch(dialogVisible, (bool) => {
7172
}
7273
})
7374
74-
const open = (data: any) => {
75+
const isFolder = ref<boolean>(false)
76+
77+
const open = (data: any, is_folder?:any) => {
7578
detail.value = data
79+
isFolder.value = is_folder
7680
getFolder()
7781
dialogVisible.value = true
7882
}
@@ -99,7 +103,19 @@ const submitHandle = async () => {
99103
...detail.value,
100104
folder_id: selectForderId.value,
101105
}
102-
if (props.source === SourceTypeEnum.KNOWLEDGE) {
106+
if (isFolder.value) {
107+
const folder_obj = {
108+
...detail.value,
109+
parent_id: selectForderId.value,
110+
}
111+
folderApi.putFolder(detail.value.id, detail.value.folder_type, folder_obj, loading)
112+
.then(() => {
113+
MsgSuccess(t('common.saveSuccess'))
114+
emit('refresh')
115+
dialogVisible.value = false
116+
})
117+
}
118+
else if (props.source === SourceTypeEnum.KNOWLEDGE) {
103119
if (detail.value.type === 2) {
104120
KnowledgeApi.putLarkKnowledge(detail.value.id, obj, loading).then(() => {
105121
MsgSuccess(t('common.saveSuccess'))

ui/src/components/folder-tree/index.vue

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
:current-node-key="currentNodeKey"
3535
highlight-current
3636
class="overflow-inherit_node__children"
37+
draggable
38+
:allow-drop="allowDrop"
39+
:allow-drag="allowDrag"
40+
@node-drop="handleDrop"
3741
node-key="id"
3842
v-loading="loading"
3943
v-bind="$attrs"
@@ -63,7 +67,7 @@
6367
<el-dropdown-menu>
6468
<el-dropdown-item
6569
@click.stop="openCreateFolder(data)"
66-
v-if="node.level !== 3 && permissionPrecise.folderCreate(data.id)"
70+
v-if="permissionPrecise.folderCreate(data.id)"
6771
>
6872
<AppIcon iconName="app-add-folder" class="color-secondary"></AppIcon>
6973
{{ $t('components.folder.addChildFolder') }}
@@ -75,6 +79,13 @@
7579
<AppIcon iconName="app-edit" class="color-secondary"></AppIcon>
7680
{{ $t('common.edit') }}
7781
</el-dropdown-item>
82+
<el-dropdown-item
83+
@click.stop="openMoveToDialog(data)"
84+
v-if="node.level !== 1 && permissionPrecise.folderEdit(data.id)"
85+
>
86+
<AppIcon iconName="app-migrate" class="color-secondary"></AppIcon>
87+
{{ $t('common.moveTo') }}
88+
</el-dropdown-item>
7889
<el-dropdown-item
7990
@click.stop="openAuthorization(data)"
8091
v-if="permissionPrecise.folderAuth(data.id)"
@@ -101,6 +112,11 @@
101112
</el-scrollbar>
102113
</div>
103114
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" :title="title" />
115+
<MoveToDialog
116+
ref="MoveToDialogRef"
117+
:source="props.source"
118+
@refresh="emit('refreshTree')"
119+
/>
104120
<ResourceAuthorizationDrawer
105121
:type="props.source"
106122
:is-folder="true"
@@ -117,13 +133,14 @@ import type { TreeInstance } from 'element-plus'
117133
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
118134
import ResourceAuthorizationDrawer from '@/components/resource-authorization-drawer/index.vue'
119135
import { t } from '@/locales'
136+
import MoveToDialog from '@/components/folder-tree/MoveToDialog.vue'
120137
import { i18n_name } from '@/utils/common'
121138
import folderApi from '@/api/folder'
122139
import { EditionConst } from '@/utils/permission/data'
123140
import { hasPermission } from '@/utils/permission/index'
124141
import useStore from '@/stores'
125142
import { TreeToFlatten } from '@/utils/array'
126-
import { MsgConfirm } from '@/utils/message'
143+
import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message'
127144
import permissionMap from '@/permission'
128145
import bus from '@/bus'
129146
defineOptions({ name: 'FolderTree' })
@@ -177,13 +194,59 @@ const permissionPrecise = computed(() => {
177194
178195
const MoreFilledPermission = (node: any, data: any) => {
179196
return (
180-
(node.level !== 3 && permissionPrecise.value.folderCreate(data.id)) ||
197+
permissionPrecise.value.folderCreate(data.id) ||
181198
permissionPrecise.value.folderEdit(data.id) ||
182199
permissionPrecise.value.folderDelete(data.id) ||
183200
permissionPrecise.value.folderAuth(data.id)
184201
)
185202
}
186203
204+
const MoveToDialogRef = ref()
205+
function openMoveToDialog(data:any) {
206+
const obj = {
207+
id: data.id,
208+
folder_type: props.source,
209+
}
210+
MoveToDialogRef.value.open(obj, true)
211+
}
212+
213+
const allowDrag = (node: any) => {
214+
return permissionPrecise.value.folderEdit(node.data.id)
215+
}
216+
217+
const allowDrop = (draggingNode: any, dropNode: any, type: string) => {
218+
const dropData = dropNode.data
219+
if (type === 'inner') {
220+
return permissionPrecise.value.folderEdit(dropData.id)
221+
}
222+
return false
223+
}
224+
225+
const handleDrop = (draggingNode: any, dropNode: any, dropType: string, ev: DragEvent) => {
226+
const dragData = draggingNode.data
227+
const dropData = dropNode.data
228+
229+
let newParentId: string
230+
if (dropType === 'inner') {
231+
newParentId = dropData.id
232+
} else {
233+
newParentId = dropData.parent_id
234+
}
235+
const obj = {
236+
...dragData,
237+
parent_id: newParentId
238+
}
239+
folderApi.putFolder(dragData.id, props.source, obj, loading)
240+
.then(() => {
241+
MsgSuccess(t('common.saveSuccess'))
242+
emit('refreshTree')
243+
})
244+
.catch(() => {
245+
MsgError(t('components.folder.requiredMessage'))
246+
emit('refreshTree')
247+
})
248+
}
249+
187250
const { folder } = useStore()
188251
onBeforeRouteLeave((to, from) => {
189252
folder.setCurrentFolder({})
@@ -328,6 +391,19 @@ onUnmounted(() => {
328391
height: calc(100vh - 210px);
329392
}
330393
}
394+
:deep(.el-tree) {
395+
.el-tree-node.is-dragging {
396+
opacity: 0.5;
397+
}
398+
.el-tree-node.is-drop-inner > .el-tree-node__content {
399+
background-color: var(--el-color-primary-light-9);
400+
border: 2px dashed var(--el-color-primary);
401+
border-radius: 4px;
402+
}
403+
.el-tree-node__content {
404+
position: relative;
405+
}
406+
}
331407
:deep(.overflow-inherit_node__children) {
332408
.el-tree-node__children {
333409
overflow: inherit !important;

0 commit comments

Comments
 (0)