feat: Folder directories support moving or dragging.
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
Typos Check / Spell Check with Typos (push) Waiting to run

This commit is contained in:
wangdan-fit2cloud 2025-11-06 21:02:47 +08:00
parent 6df1e19eaa
commit 1263592221
10 changed files with 166 additions and 139 deletions

View File

@ -3,14 +3,34 @@
{{ breadcrumbData[0]?.name }}
</h2>
<el-breadcrumb separator-icon="ArrowRight" style="line-height: normal" class="mt-4" v-else>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index">
<h5 class="ml-4 ellipsis" v-if="index === breadcrumbData.length - 1" :title="item.name">
{{ item.name }}
</h5>
<el-button v-else link @click="handleClick(item)" :title="item.name">
<span class="ellipsis"> {{ item.name }}</span>
</el-button>
</el-breadcrumb-item>
<template v-if="breadcrumbData.length > 3">
<el-breadcrumb-item>
<el-button link @click="handleClick(breadcrumbData[0])" :title="breadcrumbData[0].name">
<span class="ellipsis"> {{ breadcrumbData[0].name }}</span>
</el-button>
</el-breadcrumb-item>
<el-breadcrumb-item>
<el-button link @click="handleClick(breadcrumbData[breadcrumbData.length - 2])">
<el-icon><MoreFilled /></el-icon>
</el-button>
</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 ellipsis" :title="breadcrumbData[breadcrumbData.length - 1].name">
{{ breadcrumbData[breadcrumbData.length - 1].name }}
</h5>
</el-breadcrumb-item>
</template>
<template v-else>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index">
<h5 class="ml-4 ellipsis" v-if="index === breadcrumbData.length - 1" :title="item.name">
{{ item.name }}
</h5>
<el-button v-else link @click="handleClick(item)" :title="item.name">
<span class="ellipsis"> {{ item.name }}</span>
</el-button>
</el-breadcrumb-item>
</template>
</el-breadcrumb>
</template>

View File

@ -11,12 +11,6 @@
ref="treeRef"
:source="source"
:data="folderList"
:treeStyle="{
height: 'calc(100vh - 320px)',
border: '1px solid #ebeef5',
borderRadius: '6px',
padding: '8px',
}"
:default-expanded-keys="[currentNodeKey]"
:canOperation="false"
class="move-to-dialog-tree"
@ -74,7 +68,7 @@ watch(dialogVisible, (bool) => {
const isFolder = ref<boolean>(false)
const open = (data: any, is_folder?:any) => {
const open = (data: any, is_folder?: any) => {
detail.value = data
isFolder.value = is_folder
getFolder()
@ -105,17 +99,17 @@ const submitHandle = async () => {
}
if (isFolder.value) {
const folder_obj = {
...detail.value,
parent_id: selectForderId.value,
}
folderApi.putFolder(detail.value.id, detail.value.folder_type, folder_obj, loading)
...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) {
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'))
@ -155,8 +149,12 @@ defineExpose({ open })
padding: 0 !important;
margin-bottom: 8px;
}
:deep(.tree-label) {
max-width: 100% !important;
:deep(.el-scrollbar) {
border: 1px solid var(--el-border-color-light);
border-radius: 6px;
}
:deep(.el-tree) {
height: calc(100vh - 320px)!important;
}
}
</style>

View File

@ -5,27 +5,28 @@
:placeholder="$t('common.search')"
prefix-icon="Search"
clearable
class="p-8"
class="p-16 pb-0"
/>
<div v-if="showShared && hasPermission(EditionConst.IS_EE, 'OR')" class="border-b mb-4">
<div
@click="handleSharedNodeClick"
class="shared-button flex cursor"
:class="currentNodeKey === 'share' && 'active'"
>
<AppIcon
iconName="app-shared-active"
style="font-size: 18px"
class="color-primary"
></AppIcon>
<span class="ml-8">{{ shareTitle }}</span>
<div class="p-8 pb-0" v-if="showShared && hasPermission(EditionConst.IS_EE, 'OR')">
<div class="border-b">
<div
@click="handleSharedNodeClick"
class="shared-button flex cursor border-r-6"
:class="currentNodeKey === 'share' && 'active'"
>
<AppIcon
iconName="app-shared-active"
style="font-size: 18px"
class="color-primary"
></AppIcon>
<span class="ml-8">{{ shareTitle }}</span>
</div>
</div>
</div>
<el-scrollbar>
<el-tree
class="overflow-inherit_node__children"
class="folder-tree__main p-8"
:class="
showShared && hasPermission(EditionConst.IS_EE, 'OR')
? 'tree-height-shared'
@ -40,7 +41,7 @@
:default-expanded-keys="[currentNodeKey]"
:current-node-key="currentNodeKey"
highlight-current
draggable
:draggable="draggable"
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drop="handleDrop"
@ -49,13 +50,12 @@
v-bind="$attrs"
>
<template #default="{ node, data }">
<div class="flex-between w-full" @mouseenter.stop="handleMouseEnter(data)">
<div class="flex align-center">
<AppIcon iconName="app-folder" style="font-size: 20px"></AppIcon>
<span class="ml-8 ellipsis tree-label" style="max-width: 110px" :title="node.label">{{
i18n_name(node.label)
}}</span>
</div>
<div
@mouseenter.stop="handleMouseEnter(data)"
class="flex align-center w-full custom-tree-node"
>
<AppIcon iconName="app-folder" style="font-size: 20px"></AppIcon>
<span class="tree-label ml-8" :title="node.label">{{ i18n_name(node.label) }}</span>
<div
v-if="canOperation && MoreFilledPermission(node, data)"
@ -63,7 +63,7 @@
v-show="hoverNodeId === data.id"
@mouseenter.stop="handleMouseEnter(data)"
@mouseleave.stop="handleMouseleave"
class="mr-16"
class="mr-8 tree-operation-button"
>
<el-dropdown trigger="click" :teleported="false">
<el-button text class="w-full" v-if="MoreFilledPermission(node, data)">
@ -178,6 +178,10 @@ const props = defineProps({
type: Object,
default: () => ({}),
},
draggable: {
type: Boolean,
default: false,
},
})
const resourceType = computed(() => {
if (props.source === 'APPLICATION') {
@ -399,7 +403,7 @@ onUnmounted(() => {
padding-top: 4px;
height: calc(100vh - 180px);
}
:deep(.el-tree) {
:deep(.folder-tree__main) {
.el-tree-node.is-dragging {
opacity: 0.5;
}
@ -408,11 +412,19 @@ onUnmounted(() => {
border: 2px dashed var(--el-color-primary);
border-radius: 4px;
}
.custom-tree-node {
box-sizing: content-box;
width: calc(100% - 27px);
}
.tree-label {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.el-tree-node__content {
position: relative;
}
}
:deep(.overflow-inherit_node__children) {
.el-tree-node__children {
overflow: inherit !important;
}

View File

@ -14,6 +14,9 @@
cursor: pointer;
flex-shrink: 0;
}
.el-icon {
flex-shrink: 0;
}
// card
.el-card {

View File

@ -75,18 +75,16 @@
<el-tab-pane :label="$t('views.tool.title')" name="tool">
<LayoutContainer>
<template #left>
<div class="p-8">
<folder-tree
:source="SourceTypeEnum.TOOL"
:data="toolTreeData"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:shareTitle="$t('views.shared.shared_tool')"
:showShared="permissionPrecise['is_share']()"
:canOperation="false"
:treeStyle="{ height: '400px' }"
/>
</div>
<folder-tree
:source="SourceTypeEnum.TOOL"
:data="toolTreeData"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:shareTitle="$t('views.shared.shared_tool')"
:showShared="permissionPrecise['is_share']()"
:canOperation="false"
:treeStyle="{ height: '400px' }"
/>
</template>
<el-scrollbar height="450">
<NodeContent
@ -101,16 +99,14 @@
<el-tab-pane :label="$t('views.application.title')" name="application">
<LayoutContainer>
<template #left>
<div class="p-8">
<folder-tree
:source="SourceTypeEnum.APPLICATION"
:data="applicationTreeData"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:canOperation="false"
:treeStyle="{ height: '400px' }"
/>
</div>
<folder-tree
:source="SourceTypeEnum.APPLICATION"
:data="applicationTreeData"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:canOperation="false"
:treeStyle="{ height: '400px' }"
/>
</template>
<el-scrollbar height="450">
<NodeContent

View File

@ -27,18 +27,16 @@
</template>
<LayoutContainer class="application-manage">
<template #left>
<div class="p-8">
<folder-tree
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandle"
v-loading="folderLoading"
:canOperation="false"
showShared
:shareTitle="$t('views.shared.shared_knowledge')"
:treeStyle="{ height: 'calc(100vh - 240px)' }"
/>
</div>
<folder-tree
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandle"
v-loading="folderLoading"
:canOperation="false"
showShared
:shareTitle="$t('views.shared.shared_knowledge')"
:treeStyle="{ height: 'calc(100vh - 240px)' }"
/>
</template>
<div class="layout-bg">
<div class="flex-between p-16 ml-8">

View File

@ -23,18 +23,16 @@
</template>
<LayoutContainer class="application-manage">
<template #left>
<div class="p-8">
<folder-tree
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandle"
v-loading="folderLoading"
:canOperation="false"
showShared
:shareTitle="$t('views.shared.shared_tool')"
:treeStyle="{ height: 'calc(100vh - 240px)' }"
/>
</div>
<folder-tree
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandle"
v-loading="folderLoading"
:canOperation="false"
showShared
:shareTitle="$t('views.shared.shared_tool')"
:treeStyle="{ height: 'calc(100vh - 240px)' }"
/>
</template>
<div class="layout-bg">
<div class="flex-between p-16 ml-8">
@ -198,15 +196,17 @@ function getList() {
type: 'tool',
isShared: folder_id === 'share',
systemType: 'workspace',
}).getToolList({
folder_id: folder_id,
tool_type: 'CUSTOM'
}).then((res: any) => {
toolList.value = res.data?.tools || res.data || []
toolList.value = toolList.value?.filter((item: any) => item.is_active)
searchData.value = res.data.tools || res.data
searchData.value = searchData.value?.filter((item: any) => item.is_active)
})
.getToolList({
folder_id: folder_id,
tool_type: 'CUSTOM',
})
.then((res: any) => {
toolList.value = res.data?.tools || res.data || []
toolList.value = toolList.value?.filter((item: any) => item.is_active)
searchData.value = res.data.tools || res.data
searchData.value = searchData.value?.filter((item: any) => item.is_active)
})
}
defineExpose({ open })

View File

@ -2,15 +2,15 @@
<LayoutContainer showCollapse class="application-manage">
<template #left>
<h4 class="p-12-16 pb-0 mt-12">{{ $t('views.application.title') }}</h4>
<div class="p-8">
<folder-tree
:source="SourceTypeEnum.APPLICATION"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
@refreshTree="refreshFolder"
/>
</div>
<folder-tree
:source="SourceTypeEnum.APPLICATION"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
@refreshTree="refreshFolder"
:draggable="true"
/>
</template>
<ContentContainer>
<template #header>

View File

@ -2,17 +2,17 @@
<LayoutContainer showCollapse class="knowledge-manage">
<template #left>
<h4 class="p-12-16 pb-0 mt-12">{{ $t('views.knowledge.title') }}</h4>
<div class="p-8">
<folder-tree
:source="SourceTypeEnum.KNOWLEDGE"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:shareTitle="$t('views.shared.shared_knowledge')"
:showShared="permissionPrecise['is_share']()"
@refreshTree="refreshFolder"
/>
</div>
<folder-tree
:source="SourceTypeEnum.KNOWLEDGE"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
:shareTitle="$t('views.shared.shared_knowledge')"
:showShared="permissionPrecise['is_share']()"
@refreshTree="refreshFolder"
:draggable="true"
/>
</template>
<KnowledgeListContainer @refreshFolder="refreshFolder">
<template #header>

View File

@ -2,17 +2,17 @@
<LayoutContainer showCollapse class="tool-manage">
<template #left>
<h4 class="p-12-16 pb-0 mt-12">{{ $t('views.tool.title') }}</h4>
<div class="p-8">
<folder-tree
:source="SourceTypeEnum.TOOL"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
@refreshTree="refreshFolder"
:shareTitle="$t('views.shared.shared_tool')"
:showShared="permissionPrecise['is_share']()"
/>
</div>
<folder-tree
:source="SourceTypeEnum.TOOL"
:data="folderList"
:currentNodeKey="folder.currentFolder?.id"
@handleNodeClick="folderClickHandle"
@refreshTree="refreshFolder"
:shareTitle="$t('views.shared.shared_tool')"
:showShared="permissionPrecise['is_share']()"
:draggable="true"
/>
</template>
<ToolListContainer @refreshFolder="refreshFolder">
<template #header>
@ -40,7 +40,7 @@ import { SourceTypeEnum } from '@/enums/common'
import permissionMap from '@/permission'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
import bus from "@/bus"
import bus from '@/bus'
const route = useRoute()
const { folder, tool } = useStore()