-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: Folder move #4311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Folder move #4311
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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')) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code contains a few improvements and optimizations that can enhance its readability, maintainability, and functionality:
Here's the revised code: <template>
</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';
const dialogVisible = ref(false);
const detail = ref<any>('');
let isFolder = false;
watch(dialogVisible, (bool) => {});
const open = (data: any, _is_folder?: boolean): void => {
detail.value = data;
isFolder = !!_is_folder;
getFolder();
};
const submitHandle = async (): Promise<void> => {
const obj = {
...detail.value,
folder_id: selectFolderId && selectFolderId !== '' ? parseInt(selectFolderId) : undefined
};
try {
if (isFolder) {
await folderApi.putFolder(
detail.value.id,
detail.value.folder_type,
{ ...detail.value, parent_id: selectFolderId },
loading
);
MsgSuccess(t('common.saveSuccess'));
emit('refresh');
} else if (obj.type === 2) {
// Assuming KnowledgeApi.postLarkKnowledge exists
await KnowledgeApi.postLarkKnowledge(obj.id, obj, loading);
MsgSuccess(t('common.saveSuccess'));
}
} catch (error) {
console.error(error);
MsgError(error.message || t('common.generalError'));
}
dialogVisible.value = false;
};
</script>Key Improvements:
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks mostly correct and follows best practices. Here are some minor improvements:
Overall, the code appears well-crafted and maintainable! |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provided code is well-structured and mostly correct, but there are some areas for improvement:
Issues and Recommendations
Hardcoded Maximum Depth (
FOLDER_DEPTH):This value should be dynamically determined based on business rules rather than being fixed at a high number.
Validation Logic:
The validation logic to ensure folders do not exceed three levels needs to be refined to correctly calculate the depth of each node before checking against
FOLDER_DEPTH.Permissions Check:
The method responsible for determining permissions seems redundant because it checks both user manage permissions and target-specific permissions. A single check would suffice.
Duplicate Name Validation:
The duplicate folder name validation can be improved to use unique constraints instead of iterating through all nodes.
Code Duplication:
There is significant duplication in the code, particularly between
check_depth()and parts of the editing logic. Refactoring these sections could improve readability and maintainability.API Exception Handling:
Ensure that appropriate exceptions are raised with clear messages, especially around permission errors.
Documentation:
Add comments and docstrings to clarify the purpose and functionality of critical functions.
Test Coverage:
Ensure comprehensive testing covers edge cases and potential security vulnerabilities.
Here's an updated version addressing some of these points:
This revised version introduces a more dynamic approach to determining the maximum folder depth using settings, simplifies the validation logic, centralizes related methods, improves documentation, and refactors duplicated code.