perf: Variable aggregation node adds sorting and modifies some styling issues

v3.2
wangdan-fit2cloud 2025-10-29 18:26:24 +08:00
parent ebb3c74dc5
commit 3c7e5ff3b6
9 changed files with 104 additions and 71 deletions

View File

@ -126,7 +126,7 @@ function changeState(bool: boolean, row: any) {
const obj = { const obj = {
is_active: bool, is_active: bool,
} }
const str = bool ? t('common.enabled') : t('common.disabled') const str = bool ? t('common.status.enabled') : t('common.status.disabled')
systemKeyApi.putAPIKey(row.id, obj, loading).then((res) => { systemKeyApi.putAPIKey(row.id, obj, loading).then((res) => {
MsgSuccess(str) MsgSuccess(str)
getApiKeyList() getApiKeyList()

View File

@ -259,7 +259,7 @@ You are a master of problem optimization, adept at accurately inferring user int
}, },
variableAggregationNode: { variableAggregationNode: {
label: 'Variable Aggregation', label: 'Variable Aggregation',
text: 'Perform aggregation processing on the outputs of multiple branches', text: 'Aggregate variables of each group according to the aggregation strategy',
Strategy: 'Aggregation Strategy', Strategy: 'Aggregation Strategy',
placeholder: 'Return the first non-null value of each group', placeholder: 'Return the first non-null value of each group',
placeholder1: 'Return the set of variables for each group', placeholder1: 'Return the set of variables for each group',

View File

@ -260,7 +260,7 @@ export default {
}, },
variableAggregationNode: { variableAggregationNode: {
label: '变量聚合', label: '变量聚合',
text: '对多个分支的输出进行聚合处理', text: '按聚合策略聚合每组的变量',
Strategy: '聚合策略', Strategy: '聚合策略',
placeholder: '返回每组的第一个非空值', placeholder: '返回每组的第一个非空值',
placeholder1: '返回每组变量的集合', placeholder1: '返回每组变量的集合',

View File

@ -259,7 +259,7 @@ export default {
}, },
variableAggregationNode: { variableAggregationNode: {
label: '變量聚合', label: '變量聚合',
text: '對多個分支的輸出進行聚合處理', text: '按聚合策略聚合每組的變量',
Strategy: '聚合策略', Strategy: '聚合策略',
placeholder: '返回每組的第一個非空值', placeholder: '返回每組的第一個非空值',
placeholder1: '返回每組變量的集合', placeholder1: '返回每組變量的集合',

View File

@ -262,3 +262,23 @@
padding: 4px 16px 12px 12px; padding: 4px 16px 12px 12px;
} }
} }
//
.drag-card.no-drag {
.handle {
.handle-img {
display: none;
}
}
}
.drag-card:not(.no-drag) {
.handle {
.handle-img {
display: none;
}
&:hover {
.handle-img {
display: block;
}
}
}
}

View File

@ -259,11 +259,18 @@ function editTagValue(row: any) {
} }
function delTagValue(row: any) { function delTagValue(row: any) {
loadSharedApi({ type: 'knowledge', systemType: apiType.value }) MsgConfirm(t('views.document.tag.deleteConfirm') + row.value, t('views.document.tag.deleteTip'), {
.delTag(id, row.id, 'one') confirmButtonText: t('common.delete'),
confirmButtonClass: 'danger',
})
.then(() => { .then(() => {
getList() loadSharedApi({ type: 'knowledge', systemType: apiType.value })
.delTag(id, row.id, 'one')
.then(() => {
getList()
})
}) })
.catch(() => {})
} }
function getList() { function getList() {

View File

@ -636,7 +636,7 @@ export const menuNodes = [
}, },
{ {
label: t('views.knowledge.title'), label: t('views.knowledge.title'),
list: [searchKnowledgeNode, searchDocumentNode, rerankerNode], list: [searchKnowledgeNode, searchDocumentNode, rerankerNode, documentExtractNode],
}, },
{ {
label: t('views.applicationWorkflow.nodes.classify.businessLogic'), label: t('views.applicationWorkflow.nodes.classify.businessLogic'),
@ -653,7 +653,7 @@ export const menuNodes = [
}, },
{ {
label: t('views.applicationWorkflow.nodes.classify.other'), label: t('views.applicationWorkflow.nodes.classify.other'),
list: [mcpNode, documentExtractNode, toolNode], list: [mcpNode, toolNode],
}, },
] ]
export const applicationLoopMenuNodes = [ export const applicationLoopMenuNodes = [
@ -674,7 +674,7 @@ export const applicationLoopMenuNodes = [
}, },
{ {
label: t('views.knowledge.title'), label: t('views.knowledge.title'),
list: [searchKnowledgeNode, searchDocumentNode, rerankerNode], list: [searchKnowledgeNode, searchDocumentNode, rerankerNode, documentExtractNode],
}, },
{ {
label: t('views.applicationWorkflow.nodes.classify.businessLogic'), label: t('views.applicationWorkflow.nodes.classify.businessLogic'),
@ -682,11 +682,16 @@ export const applicationLoopMenuNodes = [
}, },
{ {
label: t('views.applicationWorkflow.nodes.classify.dataProcessing', '数据处理'), label: t('views.applicationWorkflow.nodes.classify.dataProcessing', '数据处理'),
list: [variableAssignNode, variableSplittingNode, parameterExtractionNode, variableAggregationNode], list: [
variableAssignNode,
variableSplittingNode,
parameterExtractionNode,
variableAggregationNode,
],
}, },
{ {
label: t('views.applicationWorkflow.nodes.classify.other'), label: t('views.applicationWorkflow.nodes.classify.other'),
list: [mcpNode, documentExtractNode, toolNode], list: [mcpNode, toolNode],
}, },
] ]

View File

@ -11,7 +11,7 @@
<VueDraggable <VueDraggable
ref="el" ref="el"
v-bind:modelValue="form_data.branch" v-bind:modelValue="form_data.branch"
:disabled="form_data.branch === 2" :disabled="form_data.branch.length === 2"
handle=".handle" handle=".handle"
:animation="150" :animation="150"
ghostClass="ghost" ghostClass="ghost"
@ -347,23 +347,5 @@ onMounted(() => {
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.drag-card.no-drag {
.handle {
.handle-img {
display: none;
}
}
}
.drag-card:not(.no-drag) {
.handle {
.handle-img {
display: none;
}
&:hover {
.handle-img {
display: block;
}
}
}
}
</style> </style>

View File

@ -42,8 +42,8 @@
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px"> <el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
<div class="flex-between mb-12"> <div class="flex-between mb-12">
<span class="ellipsis" :title="group.label">{{ group.label }}</span> <span class="ellipsis" :title="group.label">{{ group.label }}</span>
<div class="flex align-center" style="margin-right: -3px;"> <div class="flex align-center" style="margin-right: -3px">
<el-button @click="openAddOrEditDialog(group, gIndex)" link> <el-button @click="openAddOrEditDialog(group, gIndex)" link>
<el-icon><EditPen /></el-icon> <el-icon><EditPen /></el-icon>
</el-button> </el-button>
<el-button <el-button
@ -55,43 +55,50 @@
</el-button> </el-button>
</div> </div>
</div> </div>
<VueDraggable
<div v-for="(item, vIndex) in group.variable_list" :key="item.v_id"> ref="el"
<el-row> v-bind:modelValue="group.variable_list"
<el-col :span="22"> :disabled="group.variable_list.length === 1"
<el-form-item handle=".handle"
:prop="`group_list.${gIndex}.variable_list.${vIndex}.variable`" :animation="150"
:rules="{ ghostClass="ghost"
type: 'array', @end="onEnd($event, gIndex)"
required: true, >
message: $t( <div v-for="(item, vIndex) in group.variable_list" :key="item.v_id" class="drag-card">
'views.applicationWorkflow.variable.placeholder', <el-row class="handle">
), <el-col :span="22" class="flex">
trigger: 'change', <img src="@/assets/sort.svg" alt="" height="15" class="mr-4 mt-8" />
}" <el-form-item
> :prop="`group_list.${gIndex}.variable_list.${vIndex}.variable`"
<NodeCascader :rules="{
ref="nodeCascaderRef" type: 'array',
:nodeModel="nodeModel" required: true,
class="w-full" message: $t('views.applicationWorkflow.variable.placeholder'),
:placeholder="$t('views.applicationWorkflow.variable.placeholder')" trigger: 'change',
v-model="item.variable" }"
/> >
</el-form-item> <NodeCascader
</el-col> ref="nodeCascaderRef"
<el-col :span="2"> :nodeModel="nodeModel"
<el-button style="width: 200px"
link :placeholder="$t('views.applicationWorkflow.variable.placeholder')"
class="mt-4 ml-4" v-model="item.variable"
:disabled="group.variable_list.length <= 1" />
@click="deleteVariable(gIndex, vIndex)" </el-form-item>
> </el-col>
<AppIcon iconName="app-delete"></AppIcon> <el-col :span="2">
</el-button> <el-button
</el-col> link
</el-row> class="mt-4 ml-4"
</div> :disabled="group.variable_list.length <= 1"
@click="deleteVariable(gIndex, vIndex)"
>
<AppIcon iconName="app-delete"></AppIcon>
</el-button>
</el-col>
</el-row>
</div>
</VueDraggable>
<el-button @click="addVariable(gIndex)" type="primary" size="large" link> <el-button @click="addVariable(gIndex)" type="primary" size="large" link>
<AppIcon iconName="app-add-outlined" class="mr-4" /> <AppIcon iconName="app-add-outlined" class="mr-4" />
{{ $t('common.add') }} {{ $t('common.add') }}
@ -116,6 +123,7 @@ import { isLastNode } from '@/workflow/common/data'
import { t } from '@/locales' import { t } from '@/locales'
import { randomId } from '@/utils/common' import { randomId } from '@/utils/common'
import { MsgError } from '@/utils/message' import { MsgError } from '@/utils/message'
import { VueDraggable } from 'vue-draggable-plus'
const props = defineProps<{ nodeModel: any }>() const props = defineProps<{ nodeModel: any }>()
const VariableAggregationRef = ref() const VariableAggregationRef = ref()
@ -241,6 +249,17 @@ const validate = async () => {
}) })
} }
function onEnd(event: any, gIndex: number) {
const { oldIndex, newIndex } = event
if (oldIndex === undefined || newIndex === undefined) return
const list = cloneDeep(props.nodeModel.properties.node_data.group_list[gIndex].variable_list)
const newInstance = { ...list[oldIndex] }
const oldInstance = { ...list[newIndex] }
list[newIndex] = newInstance
list[oldIndex] = oldInstance
set(props.nodeModel.properties.node_data.group_list[gIndex], 'variable_list', list)
}
onMounted(() => { onMounted(() => {
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') { if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {
if (isLastNode(props.nodeModel)) { if (isLastNode(props.nodeModel)) {