Skip to content

Commit 23b4765

Browse files
feat: The workflow condition node supports drag and drop sorting (#2648)
1 parent 76d050b commit 23b4765

File tree

3 files changed

+146
-120
lines changed

3 files changed

+146
-120
lines changed

ui/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@
5050
"vue-clipboard3": "^2.0.0",
5151
"vue-codemirror": "^6.1.1",
5252
"vue-demi": "latest",
53+
"vue-draggable-plus": "^0.6.0",
5354
"vue-i18n": "^9.13.1",
5455
"vue-router": "^4.2.4",
55-
"vue3-menus": "^1.1.2"
56+
"vue3-menus": "^1.1.2",
57+
"vuedraggable": "^4.1.0"
5658
},
5759
"devDependencies": {
5860
"@rushstack/eslint-patch": "^1.3.2",

ui/src/components/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import LogoIcon from './logo/LogoIcon.vue'
2525
import SendIcon from './logo/SendIcon.vue'
2626
import CodemirrorEditor from './codemirror-editor/index.vue'
2727
import ModelSelect from './model-select/index.vue'
28-
2928
export default {
3029
install(app: App) {
3130
app.component(AppIcon.name, AppIcon)

ui/src/workflow/nodes/condition-node/index.vue

Lines changed: 143 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -8,130 +8,144 @@
88
ref="ConditionNodeFormRef"
99
@submit.prevent
1010
>
11-
<template v-for="(item, index) in form_data.branch" :key="item.id">
12-
<el-card
13-
v-resize="(wh: any) => resizeCondition(wh, item, index)"
14-
shadow="never"
15-
class="card-never mb-8"
16-
style="--el-card-padding: 12px"
17-
>
18-
<div class="flex-between lighter">
19-
{{ item.type }}
20-
<div class="info" v-if="item.conditions.length > 1">
21-
<span>{{ $t('views.applicationWorkflow.nodes.conditionNode.conditions.info') }}</span>
22-
<el-select
23-
:teleported="false"
24-
v-model="item.condition"
25-
size="small"
26-
style="width: 60px; margin: 0 8px"
27-
>
28-
<el-option :label="$t('views.applicationWorkflow.condition.AND')" value="and" />
29-
<el-option :label="$t('views.applicationWorkflow.condition.OR')" value="or" />
30-
</el-select>
31-
<span>{{
32-
$t('views.applicationWorkflow.nodes.conditionNode.conditions.label')
33-
}}</span>
11+
<VueDraggable
12+
ref="el"
13+
v-model="form_data.branch"
14+
:disabled="form_data.branch === 2"
15+
:filter="'.no-drag'"
16+
handle=".handle"
17+
:animation="150"
18+
ghostClass="ghost"
19+
@end="onEnd"
20+
>
21+
<template v-for="(item, index) in form_data.branch" :key="item.id">
22+
<el-card
23+
v-resize="(wh: any) => resizeCondition(wh, item, index)"
24+
shadow="never"
25+
class="card-never mb-8"
26+
:class="{ 'no-drag': index === form_data.branch.length - 1 }"
27+
style="--el-card-padding: 12px"
28+
>
29+
<div class="handle flex-between lighter">
30+
{{ item.type }}
31+
<div class="info" v-if="item.conditions.length > 1">
32+
<span>{{
33+
$t('views.applicationWorkflow.nodes.conditionNode.conditions.info')
34+
}}</span>
35+
<el-select
36+
:teleported="false"
37+
v-model="item.condition"
38+
size="small"
39+
style="width: 60px; margin: 0 8px"
40+
>
41+
<el-option :label="$t('views.applicationWorkflow.condition.AND')" value="and" />
42+
<el-option :label="$t('views.applicationWorkflow.condition.OR')" value="or" />
43+
</el-select>
44+
<span>{{
45+
$t('views.applicationWorkflow.nodes.conditionNode.conditions.label')
46+
}}</span>
47+
</div>
3448
</div>
35-
</div>
36-
<div v-if="index !== form_data.branch.length - 1" class="mt-8">
37-
<template v-for="(condition, cIndex) in item.conditions" :key="cIndex">
38-
<el-row :gutter="8">
39-
<el-col :span="11">
40-
<el-form-item
41-
:prop="'branch.' + index + '.conditions.' + cIndex + '.field'"
42-
:rules="{
43-
type: 'array',
44-
required: true,
45-
message: $t('views.applicationWorkflow.variable.placeholder'),
46-
trigger: 'change'
47-
}"
48-
>
49-
<NodeCascader
50-
ref="nodeCascaderRef"
51-
:nodeModel="nodeModel"
52-
class="w-full"
53-
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
54-
v-model="condition.field"
55-
/>
56-
</el-form-item>
57-
</el-col>
58-
<el-col :span="6">
59-
<el-form-item
60-
:prop="'branch.' + index + '.conditions.' + cIndex + '.compare'"
61-
:rules="{
62-
required: true,
63-
message: $t(
64-
'views.applicationWorkflow.nodes.conditionNode.conditions.requiredMessage'
65-
),
66-
trigger: 'change'
67-
}"
68-
>
69-
<el-select
70-
@wheel="wheel"
71-
:teleported="false"
72-
v-model="condition.compare"
73-
:placeholder="
74-
$t(
49+
<div v-if="index !== form_data.branch.length - 1" class="mt-8">
50+
<template v-for="(condition, cIndex) in item.conditions" :key="cIndex">
51+
<el-row :gutter="8">
52+
<el-col :span="11">
53+
<el-form-item
54+
:prop="'branch.' + index + '.conditions.' + cIndex + '.field'"
55+
:rules="{
56+
type: 'array',
57+
required: true,
58+
message: $t('views.applicationWorkflow.variable.placeholder'),
59+
trigger: 'change'
60+
}"
61+
>
62+
<NodeCascader
63+
ref="nodeCascaderRef"
64+
:nodeModel="nodeModel"
65+
class="w-full"
66+
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
67+
v-model="condition.field"
68+
/>
69+
</el-form-item>
70+
</el-col>
71+
<el-col :span="6">
72+
<el-form-item
73+
:prop="'branch.' + index + '.conditions.' + cIndex + '.compare'"
74+
:rules="{
75+
required: true,
76+
message: $t(
7577
'views.applicationWorkflow.nodes.conditionNode.conditions.requiredMessage'
78+
),
79+
trigger: 'change'
80+
}"
81+
>
82+
<el-select
83+
@wheel="wheel"
84+
:teleported="false"
85+
v-model="condition.compare"
86+
:placeholder="
87+
$t(
88+
'views.applicationWorkflow.nodes.conditionNode.conditions.requiredMessage'
89+
)
90+
"
91+
clearable
92+
@change="changeCondition($event, index, cIndex)"
93+
>
94+
<template v-for="(item, index) in compareList" :key="index">
95+
<el-option :label="item.label" :value="item.value" />
96+
</template>
97+
</el-select>
98+
</el-form-item>
99+
</el-col>
100+
<el-col :span="6">
101+
<el-form-item
102+
v-if="
103+
!['is_null', 'is_not_null', 'is_true', 'is_not_true'].includes(
104+
condition.compare
76105
)
77106
"
78-
clearable
79-
@change="changeCondition($event, index, cIndex)"
107+
:prop="'branch.' + index + '.conditions.' + cIndex + '.value'"
108+
:rules="{
109+
required: true,
110+
message: $t('views.applicationWorkflow.nodes.conditionNode.valueMessage'),
111+
trigger: 'blur'
112+
}"
80113
>
81-
<template v-for="(item, index) in compareList" :key="index">
82-
<el-option :label="item.label" :value="item.value" />
83-
</template>
84-
</el-select>
85-
</el-form-item>
86-
</el-col>
87-
<el-col :span="6">
88-
<el-form-item
89-
v-if="
90-
!['is_null', 'is_not_null', 'is_true', 'is_not_true'].includes(
91-
condition.compare
92-
)
93-
"
94-
:prop="'branch.' + index + '.conditions.' + cIndex + '.value'"
95-
:rules="{
96-
required: true,
97-
message: $t('views.applicationWorkflow.nodes.conditionNode.valueMessage'),
98-
trigger: 'blur'
99-
}"
100-
>
101-
<el-input
102-
v-model="condition.value"
103-
:placeholder="
104-
$t('views.applicationWorkflow.nodes.conditionNode.valueMessage')
105-
"
106-
/>
107-
</el-form-item>
108-
</el-col>
109-
<el-col :span="1">
110-
<el-button
111-
:disabled="index === 0 && cIndex === 0"
112-
link
113-
type="info"
114-
class="mt-4"
115-
@click="deleteCondition(index, cIndex)"
116-
>
117-
<el-icon><Delete /></el-icon>
118-
</el-button>
119-
</el-col>
120-
</el-row>
121-
</template>
122-
</div>
114+
<el-input
115+
v-model="condition.value"
116+
:placeholder="
117+
$t('views.applicationWorkflow.nodes.conditionNode.valueMessage')
118+
"
119+
/>
120+
</el-form-item>
121+
</el-col>
122+
<el-col :span="1">
123+
<el-button
124+
:disabled="index === 0 && cIndex === 0"
125+
link
126+
type="info"
127+
class="mt-4"
128+
@click="deleteCondition(index, cIndex)"
129+
>
130+
<el-icon><Delete /></el-icon>
131+
</el-button>
132+
</el-col>
133+
</el-row>
134+
</template>
135+
</div>
123136

124-
<el-button
125-
link
126-
type="primary"
127-
@click="addCondition(index)"
128-
v-if="index !== form_data.branch.length - 1"
129-
>
130-
<el-icon class="mr-4"><Plus /></el-icon>
131-
{{ $t('views.applicationWorkflow.nodes.conditionNode.addCondition') }}
132-
</el-button>
133-
</el-card>
134-
</template>
137+
<el-button
138+
link
139+
type="primary"
140+
@click="addCondition(index)"
141+
v-if="index !== form_data.branch.length - 1"
142+
>
143+
<el-icon class="mr-4"><Plus /></el-icon>
144+
{{ $t('views.applicationWorkflow.nodes.conditionNode.addCondition') }}
145+
</el-button>
146+
</el-card>
147+
</template>
148+
</VueDraggable>
135149
<el-button link type="primary" @click="addBranch">
136150
<el-icon class="mr-4"><Plus /></el-icon>
137151
{{ $t('views.applicationWorkflow.nodes.conditionNode.addBranch') }}
@@ -147,6 +161,7 @@ import type { FormInstance } from 'element-plus'
147161
import { ref, computed, onMounted, nextTick } from 'vue'
148162
import { randomId } from '@/utils/utils'
149163
import { compareList } from '@/workflow/common/data'
164+
import { type DraggableEvent, type UseDraggableReturn, VueDraggable } from 'vue-draggable-plus'
150165
151166
const props = defineProps<{ nodeModel: any }>()
152167
const form = {
@@ -223,6 +238,16 @@ const validate = () => {
223238
})
224239
}
225240
241+
function onEnd(evt: DraggableEvent) {
242+
const { oldIndex, newIndex, clonedData } = evt
243+
if (oldIndex === undefined || newIndex === undefined) return
244+
const list = cloneDeep(props.nodeModel.properties.node_data.branch)
245+
246+
list[newIndex].type = list[oldIndex].type
247+
list[oldIndex].type = clonedData.type // 恢复原始 type
248+
set(props.nodeModel.properties.node_data, 'branch', list)
249+
}
250+
226251
function addBranch() {
227252
const list = cloneDeep(props.nodeModel.properties.node_data.branch)
228253
const obj = {

0 commit comments

Comments
 (0)