Skip to content

Commit 0b06e20

Browse files
committed
refactor: toolTree and applicationTree
1 parent 0b049ad commit 0b06e20

File tree

3 files changed

+245
-121
lines changed

3 files changed

+245
-121
lines changed

ui/src/api/tool/tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Object.defineProperty(prefix, 'value', {
1818
* 工具列表带分页(无分页)
1919
* @params 参数 {folder_id: string}
2020
*/
21-
const getToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
21+
const getToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<{tools: any[], folders: any[]}>> = (
2222
data,
2323
loading,
2424
) => {

ui/src/views/application-workflow/component/DropdownMenu.vue

Lines changed: 154 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,13 @@
1919
<template v-for="(node, index) in filter_menu_nodes" :key="index">
2020
<el-text type="info" size="small" class="color-secondary ml-12">{{
2121
node.label
22-
}}</el-text>
22+
}}</el-text>
2323
<div class="flex-wrap mt-8">
2424
<template v-for="(item, index) in node.list" :key="index">
2525
<el-popover placement="right" :width="280">
2626
<template #reference>
27-
<div
28-
class="flex align-center border border-r-6 mb-12 p-8-12 cursor ml-12"
29-
style="width: 39%"
30-
@click.stop="clickNodes(item)"
31-
@mousedown.stop="onmousedown(item)"
32-
>
27+
<div class="list-item flex align-center border border-r-6 mb-12 p-8-12 cursor ml-12"
28+
style="width: 39%" @click.stop="clickNodes(item)" @mousedown.stop="onmousedown(item)">
3329
<component
3430
:is="iconComponent(`${item.type}-icon`)"
3531
class="mr-8"
@@ -49,7 +45,7 @@
4945
</div>
5046
<el-text type="info" size="small" class="color-secondary lighter">{{
5147
item.text
52-
}}</el-text>
48+
}}</el-text>
5349
</template>
5450
</el-popover>
5551
</template>
@@ -63,104 +59,58 @@
6359
</el-tab-pane>
6460
<el-tab-pane :label="$t('views.tool.title')" name="tool">
6561
<el-scrollbar height="400">
66-
<div
67-
class="workflow-dropdown-item cursor flex p-8-12"
68-
@click.stop="clickNodes(toolNode)"
69-
@mousedown.stop="onmousedown(toolNode)"
70-
>
71-
<component :is="iconComponent(`tool-lib-node-icon`)" class="mr-8 mt-4" :size="32" />
72-
<div class="pre-wrap">
73-
<div class="lighter">{{ toolNode.label }}</div>
74-
<el-text type="info" size="small">{{ toolNode.text }}</el-text>
75-
</div>
76-
</div>
77-
78-
<template v-for="(item, index) in filter_tool_lib_list" :key="index">
79-
<div
80-
class="workflow-dropdown-item cursor flex p-8-12 align-center"
81-
@click.stop="clickNodes(toolLibNode, item, 'tool')"
82-
@mousedown.stop="onmousedown(toolLibNode, item, 'tool')"
83-
>
84-
<component
85-
:is="iconComponent(`tool-lib-node-icon`)"
86-
class="mr-8"
87-
:size="32"
88-
:item="item"
89-
/>
62+
<!-- 共享工具 -->
63+
<el-collapse expand-icon-position="left">
64+
<el-collapse-item name="shared" :icon="CaretRight">
65+
<template #title>
66+
<div class="flex align-center">
67+
<AppIcon iconName="app-shared-active" style="font-size: 20px" class="color-primary"></AppIcon>
68+
<span class="ml-8 lighter">{{ $t('views.shared.shared_tool') }}</span>
69+
</div>
70+
</template>
71+
<NodeContent :list="sharedToolList" @clickNodes="(val: any) => clickNodes(toolLibNode, val, 'tool')"
72+
@onmousedown="(val: any) => onmousedown(toolLibNode, val, 'tool')" />
73+
</el-collapse-item>
74+
</el-collapse>
9075

91-
<div class="pre-wrap">
92-
<div class="lighter ellipsis-1" :title="item.name">{{ item.name }}</div>
93-
<p>
94-
<el-text
95-
class="ellipsis-1"
96-
type="info"
97-
size="small"
98-
:title="item.desc"
99-
v-if="item.desc"
100-
>{{ item.desc }}</el-text
101-
>
102-
</p>
103-
</div>
104-
</div>
105-
</template>
76+
<el-tree :data="toolTreeData" node-key="id"
77+
:props="{ children: 'children', isLeaf: 'isLeaf', class: getNodeClass }" lazy :load="loadNode">
78+
<template #default="{ data, node }">
79+
<NodeContent v-if="!data._fake" :data="data" :node="node"
80+
@clickNodes="(val: any) => clickNodes(toolLibNode, val, 'tool')"
81+
@onmousedown="(val: any) => onmousedown(toolLibNode, val, 'tool')" />
82+
</template>
83+
</el-tree>
10684
</el-scrollbar>
10785
</el-tab-pane>
10886
<el-tab-pane :label="$t('views.application.title')" name="application">
10987
<el-scrollbar height="400">
110-
<div v-if="filter_application_list.length > 0">
111-
<template v-for="(item, index) in filter_application_list" :key="index">
112-
<div
113-
class="workflow-dropdown-item cursor flex align-center p-8-12"
114-
@click.stop="clickNodes(applicationNode, item, 'application')"
115-
@mousedown.stop="onmousedown(applicationNode, item, 'application')"
116-
>
117-
<component
118-
:is="iconComponent(`application-node-icon`)"
119-
class="mr-8"
120-
:size="32"
121-
:item="item"
122-
/>
123-
<div class="pre-wrap" style="width: 60%">
124-
<div class="lighter ellipsis" :title="item.name">
125-
{{ item.name }}
126-
</div>
127-
<p>
128-
<el-text
129-
class="ellipsis"
130-
type="info"
131-
size="small"
132-
:title="item.desc"
133-
v-if="item.desc"
134-
>{{ item.desc }}</el-text
135-
>
136-
</p>
137-
</div>
138-
<div class="status-tag" style="margin-left: auto">
139-
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px">
140-
{{ $t('views.application.workflow') }}</el-tag
141-
>
142-
<el-tag class="blue-tag" v-else style="height: 22px">{{
143-
$t('views.application.simple')
144-
}}</el-tag>
145-
</div>
146-
</div>
88+
<el-tree :data="applicationTreeData" node-key="id"
89+
:props="{ children: 'children', isLeaf: 'isLeaf', class: getNodeClass }" lazy :load="loadNode">
90+
<template #default="{ data, node }">
91+
<NodeContent v-if="!data._fake" :data="data" :node="node"
92+
@clickNodes="(val: any) => clickNodes(applicationNode, val, 'application')"
93+
@onmousedown="(val: any) => onmousedown(applicationNode, val, 'application')" />
14794
</template>
148-
</div>
149-
<div v-else class="ml-16 mt-8">
150-
<el-text type="info">{{ $t('views.applicationWorkflow.tip.noData') }}</el-text>
151-
</div>
95+
</el-tree>
15296
</el-scrollbar>
15397
</el-tab-pane>
15498
</el-tabs>
15599
</div>
156100
</template>
157101
<script setup lang="ts">
158102
import { ref, onMounted, computed } from 'vue'
159-
import { menuNodes, toolNode, toolLibNode, applicationNode } from '@/workflow/common/data'
103+
import { menuNodes, toolLibNode, applicationNode } from '@/workflow/common/data'
160104
import { iconComponent } from '@/workflow/icons/utils'
161-
import applicationApi from '@/api/application/application'
105+
import ToolApi from '@/api/tool/tool'
162106
import { isWorkFlow } from '@/utils/application'
163-
import { isAppIcon } from '@/utils/common'
107+
import useStore from '@/stores'
108+
import NodeContent from './NodeContent.vue'
109+
import { SourceTypeEnum } from '@/enums/common'
110+
import sharedWorkspaceApi from '@/api/shared-workspace'
111+
import { CaretRight } from '@element-plus/icons-vue'
112+
import ApplicationApi from '@/api/application/application'
113+
164114
const search_text = ref<string>('')
165115
const props = defineProps({
166116
show: {
@@ -174,24 +124,12 @@ const props = defineProps({
174124
workflowRef: Object,
175125
})
176126
127+
const { folder } = useStore()
177128
const emit = defineEmits(['clickNodes', 'onmousedown'])
178129
179130
const loading = ref(false)
180131
const activeName = ref('base')
181132
182-
const toolList = ref<any[]>([])
183-
const filter_tool_lib_list = computed(() => {
184-
return toolList.value.filter((item: any) =>
185-
item.name.toLocaleLowerCase().includes(search_text.value.toLocaleLowerCase()),
186-
)
187-
})
188-
const applicationList = ref<any[]>([])
189-
const filter_application_list = computed(() => {
190-
return applicationList.value.filter((item: any) =>
191-
item.name.toLocaleLowerCase().includes(search_text.value.toLocaleLowerCase()),
192-
)
193-
})
194-
195133
const filter_menu_nodes = computed(() => {
196134
if (!search_text.value) return menuNodes
197135
const searchTerm = search_text.value.toLowerCase()
@@ -234,10 +172,10 @@ function clickNodes(item: any, data?: any, type?: string) {
234172
...(!fileUploadSetting
235173
? {}
236174
: {
237-
...(fileUploadSetting.document ? { document_list: [] } : {}),
238-
...(fileUploadSetting.image ? { image_list: [] } : {}),
239-
...(fileUploadSetting.audio ? { audio_list: [] } : {}),
240-
}),
175+
...(fileUploadSetting.document ? { document_list: [] } : {}),
176+
...(fileUploadSetting.image ? { image_list: [] } : {}),
177+
...(fileUploadSetting.audio ? { audio_list: [] } : {}),
178+
}),
241179
}
242180
} else {
243181
item['properties']['node_data'] = {
@@ -279,10 +217,10 @@ function onmousedown(item: any, data?: any, type?: string) {
279217
...(!fileUploadSetting
280218
? {}
281219
: {
282-
...(fileUploadSetting.document ? { document_list: [] } : {}),
283-
...(fileUploadSetting.image ? { image_list: [] } : {}),
284-
...(fileUploadSetting.audio ? { audio_list: [] } : {}),
285-
}),
220+
...(fileUploadSetting.document ? { document_list: [] } : {}),
221+
...(fileUploadSetting.image ? { image_list: [] } : {}),
222+
...(fileUploadSetting.audio ? { audio_list: [] } : {}),
223+
}),
286224
}
287225
} else {
288226
item['properties']['node_data'] = {
@@ -297,17 +235,72 @@ function onmousedown(item: any, data?: any, type?: string) {
297235
emit('onmousedown', item)
298236
}
299237
300-
function getList() {
301-
// applicationApi.listTool(props.id, loading).then((res: any) => {
302-
// toolList.value = res.data
303-
// })
304-
// applicationApi.getApplicationList(props.id, loading).then((res: any) => {
305-
// applicationList.value = res.data
306-
// })
238+
function getNodeClass(data: any) {
239+
return data._fake ? 'tree-node--hidden' : ''
240+
}
241+
242+
const loadNode = async (node: any, resolve: (children: any[]) => void) => {
243+
if (node.level === 0) return resolve([])
244+
try {
245+
let folders
246+
if (activeName.value === 'tool') {
247+
const res = await ToolApi.getToolList({ folder_id: node.data.id })
248+
node.data.cardList = res.data.tools
249+
folders = res.data?.folders
250+
} else {
251+
const res = await ApplicationApi.getAllApplication({ folder_id: node.data.id })
252+
node.data.cardList = res.data.filter(item => item.resource_type === "application")
253+
folders = res.data.filter(item => item.resource_type === "folder")
254+
}
255+
const children = folders.map(f => ({
256+
...f,
257+
children: [],
258+
isLeaf: false,
259+
}))
260+
261+
if (folders.length === 0 && node.data.cardList.length > 0) {
262+
// 插一个假子节点,确保树节点是“可折叠”的
263+
children.push({
264+
id: `__placeholder__${node.data.id}`,
265+
isLeaf: true,
266+
_fake: true,
267+
})
268+
}
269+
270+
resolve(children)
271+
} catch (e: any) {
272+
resolve([]) // 失败也要 resolve,否则树会卡住
273+
}
274+
}
275+
276+
const toolTreeData = ref<any[]>([])
277+
function getToolFolder() {
278+
folder.asyncGetFolder(SourceTypeEnum.TOOL, {}, loading).then((res: any) => {
279+
toolTreeData.value = res.data
280+
})
281+
}
282+
283+
const sharedToolList = ref<any[]>([])
284+
async function getShareTool() {
285+
try {
286+
const res = await sharedWorkspaceApi.getToolList(loading)
287+
sharedToolList.value = res.data
288+
} catch (error: any) {
289+
console.error(error)
290+
}
291+
}
292+
293+
const applicationTreeData = ref<any[]>([])
294+
function getApplicationFolder() {
295+
folder.asyncGetFolder(SourceTypeEnum.APPLICATION, {}, loading).then((res: any) => {
296+
applicationTreeData.value = res.data
297+
})
307298
}
308299
309300
onMounted(() => {
310-
// getList()
301+
getShareTool()
302+
getToolFolder()
303+
getApplicationFolder()
311304
})
312305
</script>
313306
<style lang="scss" scoped>
@@ -335,5 +328,46 @@ onMounted(() => {
335328
background: var(--app-text-color-light-1);
336329
}
337330
}
331+
332+
.list-item {
333+
&:hover {
334+
border-color: var(--el-color-primary);
335+
}
336+
}
337+
338+
:deep(.el-collapse) {
339+
border-top-width: 0;
340+
.el-collapse-item__header {
341+
height: 40px;
342+
gap: 0;
343+
.el-collapse-item__arrow {
344+
font-size: 16px;
345+
color: var(--app-text-color-secondary);
346+
padding: 6px;
347+
}
348+
}
349+
.el-collapse-item__content {
350+
padding: 0 12px 16px 12px;
351+
.list {
352+
margin-top: 0;
353+
transform: none;
354+
}
355+
}
356+
}
357+
358+
:deep(.el-tree-node):focus>.el-tree-node__content {
359+
background: transparent;
360+
}
361+
:deep(.el-tree-node__content) {
362+
height: auto;
363+
align-items: baseline;
364+
&:hover {
365+
background: transparent;
366+
}
367+
}
368+
369+
:deep(.tree-node--hidden) {
370+
display: none !important;
371+
}
338372
}
339373
</style>

0 commit comments

Comments
 (0)