Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ui/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ Keep these behaviors:
## Frontend Implementation Notes

- Prefer existing project structure and naming.
- Before changing existing behavior, trace the affected flow and dependent requirements, including
why the current implementation exists. If the requested change is likely to remove a requirement
or introduce a regression, pause before editing, explain the risk, recommend a safer approach,
and wait for confirmation.
- Keep UI code typed and data-driven.
- Use Lodash `cloneDeep` for deep cloning; do not use `structuredClone`.
- Explicitly import APIs used from Vue, Vue Router, and Pinia; do not restore API auto import.
Expand Down
8 changes: 8 additions & 0 deletions ui/src/api/admin/workspace/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ const getPrefix = () => {
return `/workspace/${workspaceId}/application`
}

/**
* 获取不分页的全部应用
*/
const getAllApplication = (query?: Dict<unknown>) => {
return get<ApplicationDetail[]>(`${getPrefix()}`, query)
}

/** 获取工作空间智能体列表。 */
const getApplicationPage = (page: ParamsPage, query?: Dict<unknown>) => {
return get<ResponsePage<ApplicationDetail>>(`${getPrefix()}/${page.currentPage}/${page.pageSize}`, query)
Expand Down Expand Up @@ -77,4 +84,5 @@ export default {
putBatchDeleteApplications,
putBatchMoveApplications,
putApplicationPublish,
getAllApplication,
}
8 changes: 6 additions & 2 deletions ui/src/api/admin/workspace/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ const getModelList = (query?: Dict<unknown>) => {
return get<ModelItem[]>(`${getPrefix()}/model`, query)
}

/** 获取工作空间共享的工具列表。 */
/** 获取工作空间共享的分页工具列表。 */
const getToolPage = (page: ParamsPage, query?: Dict<unknown>) => {
return get<ResponsePage<ToolItem>>(`${getPrefix()}/tool/${page.currentPage}/${page.pageSize}`, query)
}
/** 获取工作空间共享的不分页所有工具列表。 */
const getAllTool = (query?: Dict<unknown>) => {
return get<ToolItem[]>(`${getPrefix()}/tool`, query)
}

/** 获取工作空间共享的知识库列表。 */
const getKnowledgePage = (page: ParamsPage, query?: Dict<unknown>) => {
return get<ResponsePage<KnowledgeItem>>(`${getPrefix()}/knowledge/${page.currentPage}/${page.pageSize}`, query)
}

export default { getKnowledgePage, getModelList, getToolPage }
export default { getKnowledgePage, getModelList, getToolPage, getAllTool }
6 changes: 6 additions & 0 deletions ui/src/api/admin/workspace/tool/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const getPrefix = () => {
return `/workspace/${workspaceId}/tool`
}

/** 获取不分页的全部工具列表。 */
const getAllTool = (query?: Dict<unknown>) => {
return get<{ tools: ToolItem[] }>(`${getPrefix()}`, query).then(({ tools }) => tools)
}

/** 获取工具分页列表。 */
const getToolPage = (page: ParamsPage, query?: Dict<unknown>) => {
return get<ResponsePage<ToolItem>>(`${getPrefix()}/${page.currentPage}/${page.pageSize}`, query)
Expand Down Expand Up @@ -104,4 +109,5 @@ export default {
putBatchDeleteTools,
putBatchMoveTools,
putTool,
getAllTool,
}
2 changes: 1 addition & 1 deletion ui/src/api/enums/tool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** 后端 Workspace 工具作用域枚举值。 */
/** TODO 后端 Workspace 工具作用域枚举值。 */
export const TOOL_SCOPE = { INTERNAL: 'INTERNAL', SHARED: 'SHARED', WORKSPACE: 'WORKSPACE' } as const

/** 后端 Workspace 工具类型枚举值。 */
Expand Down
4 changes: 2 additions & 2 deletions ui/src/views/tool/ToolView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ToolApi from '@/api/admin/workspace/tool/tool'
import SharedApi from '@/api/admin/workspace/shared'
import ToolStoreApi from '@/api/admin/tool-store.ts'
import type { Dict, FolderItem, OptionItem, ToolItem, ToolStoreResponse, ToolType } from '@/api/types'
import { RESOURCE_TYPE, TOOL_SCOPE, TOOL_TYPE } from '@/api/enums'
import { RESOURCE_TYPE, TOOL_TYPE } from '@/api/enums'
import { TOOL_TYPE_OPTIONS, FOLDER_ENTRIES, FOLDER_ENTRY_ID } from '@/constants'
import FolderTree from '@/components/business/folder-tree/index.vue'
import MoveToDialog from '@/components/business/folder-tree/MoveToDialog.vue'
Expand Down Expand Up @@ -69,7 +69,7 @@ const toolType = ref<ToolType | ''>('')
function loadToolsPage(pagination: { currentPage: number; pageSize: number }) {
const request = isShared.value ? SharedApi : ToolApi
const folderId = isShared.value ? {} : { folder_id: currentFolder.value.id || FOLDER_ENTRY_ID.ALL }
return request.getToolPage(pagination, { ...toolQuery.value, scope: TOOL_SCOPE.WORKSPACE, tool_type: toolType.value, ...folderId })
return request.getToolPage(pagination, { ...toolQuery.value, tool_type: toolType.value, ...folderId })
}

// 加载工具商店
Expand Down
53 changes: 25 additions & 28 deletions ui/src/workflow-canvas/node-menu/ResourceNodeMenu.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { cloneDeep } from 'lodash'
import ApplicationApi from '@/api/admin/workspace/application/application'
import SharedApi from '@/api/admin/workspace/shared'
import ToolApi from '@/api/admin/workspace/tool/tool'
import { APPLICATION_TYPE, RESOURCE_TYPE, TOOL_SCOPE, TOOL_TYPE } from '@/api/enums'
import { APPLICATION_TYPE, RESOURCE_TYPE, TOOL_TYPE } from '@/api/enums'
import type { ApplicationDetail, ApplicationType, FolderItem, ToolItem, ToolType } from '@/api/types'
import FolderTree from '@/components/business/folder-tree/index.vue'
import { FOLDER_ENTRY_ID } from '@/constants'
Expand All @@ -30,10 +30,9 @@ interface ResourceMenuItem {
toolType?: ToolType
}

const RESOURCE_PAGE = { currentPage: 1, pageSize: 1_000 }
const SUPPORTED_TOOL_TYPES: ToolType[] = [TOOL_TYPE.CUSTOM, TOOL_TYPE.WORKFLOW]
const route = useRoute()
const currentFolderId = ref('')
const currentFolderId = ref(FOLDER_ENTRY_ID.ALL)
const loading = ref(false)
const resourceItems = ref<ResourceMenuItem[]>([])
const searchKeyword = ref('')
Expand Down Expand Up @@ -71,13 +70,17 @@ function createApplicationNode(application: ApplicationDetail): NodeMenuItem {
return node
}

function loadTools(folder: FolderItem) {
const requestApi = folder.id === FOLDER_ENTRY_ID.SHARED ? SharedApi : ToolApi
const folderQuery = folder.id === FOLDER_ENTRY_ID.SHARED ? {} : { folder_id: folder.id }
function loadTools(folder?: FolderItem) {
const isSharedFolder = folder?.id === FOLDER_ENTRY_ID.SHARED
const requestApi = isSharedFolder ? SharedApi : ToolApi
const folderQuery = {
tool_type_list: SUPPORTED_TOOL_TYPES,
...(!isSharedFolder ? { folder_id: folder?.id || FOLDER_ENTRY_ID.ALL } : {}),
}

return requestApi.getToolPage(RESOURCE_PAGE, { ...folderQuery, scope: TOOL_SCOPE.WORKSPACE }).then((page) => {
resourceItems.value = page.records
.filter((tool) => tool.is_active && SUPPORTED_TOOL_TYPES.includes(tool.tool_type))
return requestApi.getAllTool(folderQuery).then((tools) => {
resourceItems.value = tools
.filter((tool) => tool.is_active)
.map((tool) => ({
desc: tool.desc,
icon: tool.icon,
Expand All @@ -89,10 +92,12 @@ function loadTools(folder: FolderItem) {
})
}

function loadApplications(folder: FolderItem) {
return ApplicationApi.getApplicationPage(RESOURCE_PAGE, { folder_id: folder.id, publish_status: 'published' }).then((page) => {
resourceItems.value = page.records
.filter((application) => application.resource_type === 'application' && application.is_publish && application.id !== currentApplicationId.value)
function loadApplications(folder?: FolderItem) {
const folderQuery = { folder_id: folder?.id || FOLDER_ENTRY_ID.ALL }

return ApplicationApi.getAllApplication({ ...folderQuery, publish_status: 'published' }).then((res) => {
resourceItems.value = res
.filter((application) => application.id !== currentApplicationId.value)
.map((application) => ({
applicationType: application.type,
desc: application.desc,
Expand All @@ -104,12 +109,7 @@ function loadApplications(folder: FolderItem) {
})
}

function handleFolderChange(folder?: FolderItem) {
if (!folder) {
resourceItems.value = []
return
}

function loadResources(folder?: FolderItem) {
loading.value = true
const request = isToolMenu.value ? loadTools(folder) : loadApplications(folder)
return request.finally(() => {
Expand All @@ -120,19 +120,16 @@ function handleFolderChange(folder?: FolderItem) {
function handleNodeDragStart(event: PointerEvent, node: NodeMenuItem) {
if (event.button === 0) emit('dragstart', node, event)
}

onMounted(() => {
void loadResources()
})
</script>

<template>
<div class="flex h-[450px] min-h-0">
<aside class="flex w-60 shrink-0 border-r pt-3">
<FolderTree
v-model="currentFolderId"
:can-edit="false"
:show-shared="isToolMenu"
:source="source"
@loaded="handleFolderChange"
@select="handleFolderChange"
/>
<FolderTree v-model="currentFolderId" :can-edit="false" :show-shared="isToolMenu" :source="source" @select="loadResources" />
</aside>

<section v-loading="loading" class="flex min-w-0 flex-1 flex-col">
Expand Down
5 changes: 2 additions & 3 deletions ui/src/workflow-canvas/nodes/start-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { computed, inject, onBeforeUnmount, onMounted } from 'vue'

import { CopyDocument } from '@element-plus/icons-vue'
import { cloneDeep, set } from 'lodash'
import type { WorkflowNodeField, WorkflowNodeModel } from '@/workflow-canvas/core/workflow-node'
import NodeContainer from '@/workflow-canvas/core/node-container/index.vue'
import { WorkflowNodeType } from '@/workflow-canvas/types'
import { WorkflowNodeType, type WorkflowNodeField } from '@/workflow-canvas/types'
import { copyText } from '@/utils/clipboard'
import type { BaseEdgeModel, BaseNodeModel, GraphModel } from '@logicflow/core'
import type { BaseNodeModel } from '@logicflow/core'

defineOptions({ name: 'WorkflowStartNode' })

Expand Down
2 changes: 1 addition & 1 deletion ui/src/workflow-canvas/nodes/text-to-video-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { QuestionFilled } from '@element-plus/icons-vue'
import type { FormInstance } from 'element-plus'
import ModelSelect from '@/components/business/model-select/index.vue'
import NodeCascader from '@/workflow-canvas/core/NodeCascader.vue'
import NodeContainer from '@/workflow-canvas/core/NodeContainer.vue'
import NodeContainer from '@/workflow-canvas/core/node-container/index.vue'
import { useWorkflowStore } from '@/workflow-canvas/store'
import type { BaseNodeModel } from '@logicflow/core'
import type { ModelItem, ModelProviderItem } from '@/api/types'
Expand Down
Loading