Files
EasyFlow/easyflow-ui-admin/app/src/views/ai/workflow/components/SingleRun.vue
陈子默 71b3d3d620 fix: 修正工作流试运行参数名称展示
- 默认字段标题回退为实际配置的参数名称

- 保留显式展示标题并统一表单与引用口径
2026-08-26 22:49:01 +08:00

292 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { FormInstance } from 'element-plus';
import { computed, ref } from 'vue';
import { Position } from '@element-plus/icons-vue';
import { ElAlert, ElButton, ElForm, ElFormItem, ElMessage } from 'element-plus';
import { api } from '#/api/request';
import ShowJson from '#/components/json/ShowJson.vue';
import { $t } from '#/locales';
import WorkflowFormItem from '#/views/ai/workflow/components/WorkflowFormItem.vue';
import { buildSingleRunModel } from '../../../../../../packages/tinyflow-ui/src/utils/workflowNodeFields';
import { resolveWorkflowParameterDisplayName } from './workflowFormParameters';
interface Props {
workflowId: any;
node: any;
}
const props = defineProps<Props>();
const singleRunForm = ref<FormInstance>();
const runParams = ref<any>({});
const submitLoading = ref(false);
const result = ref<any>('');
const singleRunModel = computed(() => buildSingleRunModel(props.node));
const isFieldMode = computed(() => singleRunModel.value.mode === 'fields');
const singleRunParameters = computed(() => singleRunModel.value.parameters);
const singleRunFields = computed(() => singleRunModel.value.fields);
const parameterDisplayNameMap = computed(() => {
return new Map(
singleRunParameters.value.map((parameter: any) => [
String(parameter.name || ''),
resolveWorkflowParameterDisplayName(parameter),
]),
);
});
function buildFieldSegments(value: string) {
const source = String(value || '');
const segments: Array<{ text: string; token: boolean }> = [];
const regex = /\{\{\s*([^{}]+?)\s*}}/g;
let lastIndex = 0;
for (const match of source.matchAll(regex)) {
const matchedText = match[0] || '';
const tokenKey = String(match[1] || '').trim();
const start = match.index ?? 0;
if (start > lastIndex) {
segments.push({
text: source.slice(lastIndex, start),
token: false,
});
}
segments.push({
text: parameterDisplayNameMap.value.get(tokenKey) || tokenKey,
token: true,
});
lastIndex = start + matchedText.length;
}
if (lastIndex < source.length) {
segments.push({
text: source.slice(lastIndex),
token: false,
});
}
return segments.length > 0 ? segments : [{ text: '', token: false }];
}
function submit() {
singleRunForm.value?.validate((valid) => {
if (valid) {
const params = {
workflowId: props.workflowId,
nodeId: props.node.id,
variables: runParams.value,
};
submitLoading.value = true;
api.post('/api/v1/workflow/singleRun', params).then((res) => {
submitLoading.value = false;
result.value = res.data;
if (res.errorCode === 0) {
ElMessage.success(res.message);
} else {
ElMessage.error(res.message);
}
});
}
});
}
</script>
<template>
<div class="single-run">
<ElForm
class="single-run__form"
label-position="top"
ref="singleRunForm"
:model="runParams"
>
<template v-if="isFieldMode">
<div class="single-run-section">
<div class="single-run-section__title">字段值</div>
<ElAlert
type="info"
:closable="false"
show-icon
title="当前节点字段按正式配置展示;下方只需填写这些字段引用到的上游值。"
/>
<div class="single-run-field-list">
<div
v-for="field in singleRunFields"
:key="field.key"
class="single-run-field-card"
>
<div class="single-run-field-card__label">
{{ field.label }}
</div>
<div
class="single-run-field-card__value"
:class="{ 'is-multiline': field.multiline }"
>
<template v-if="field.value">
<template
v-for="(segment, index) in buildFieldSegments(field.value)"
:key="`${field.key}-${index}`"
>
<span
v-if="segment.token"
class="single-run-token-chip"
>
{{ segment.text }}
</span>
<span
v-else
class="single-run-field-card__text"
>
{{ segment.text }}
</span>
</template>
</template>
<span v-else class="single-run-field-card__placeholder">
{{ field.placeholder || '未设置' }}
</span>
</div>
</div>
</div>
</div>
<div class="single-run-section">
<div class="single-run-section__title">上游引用</div>
<ElAlert
v-if="singleRunParameters.length === 0"
type="success"
:closable="false"
show-icon
title="当前字段没有引用上游参数,可直接运行。"
/>
<WorkflowFormItem
v-else
v-model:run-params="runParams"
:parameters="singleRunParameters"
/>
</div>
</template>
<WorkflowFormItem
v-else
v-model:run-params="runParams"
:parameters="singleRunParameters"
/>
<ElFormItem>
<ElButton
type="primary"
@click="submit"
:loading="submitLoading"
:icon="Position"
>
{{ $t('button.run') }}
</ElButton>
</ElFormItem>
</ElForm>
<section class="single-run-result">
<div class="single-run-result__title">{{ $t('workflow.result') }}</div>
<ShowJson class="single-run-result__viewer" :value="result" />
</section>
</div>
</template>
<style scoped>
.single-run {
display: flex;
flex-direction: column;
min-width: 0;
min-height: 100%;
}
.single-run__form {
flex: 0 0 auto;
}
.single-run-result {
display: flex;
flex: 1;
flex-direction: column;
min-width: 0;
min-height: 240px;
}
.single-run-result__title {
margin: 10px 0;
font-weight: 600;
}
.single-run-result__viewer {
max-width: 100%;
max-height: min(60vh, 720px);
overflow: auto;
overscroll-behavior: contain;
}
.single-run-section + .single-run-section {
margin-top: 20px;
}
.single-run-section__title {
margin-bottom: 10px;
font-size: 13px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.single-run-field-list {
display: flex;
flex-direction: column;
gap: 12px;
margin-top: 12px;
}
.single-run-field-card {
border: 1px solid var(--el-border-color-light);
border-radius: 10px;
padding: 12px;
background: var(--el-fill-color-blank);
}
.single-run-field-card__label {
margin-bottom: 8px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
.single-run-field-card__value {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
min-height: 20px;
line-height: 1.7;
color: var(--el-text-color-primary);
white-space: nowrap;
}
.single-run-field-card__value.is-multiline {
white-space: pre-wrap;
}
.single-run-field-card__text {
white-space: inherit;
}
.single-run-field-card__placeholder {
color: var(--el-text-color-placeholder);
}
.single-run-token-chip {
display: inline-flex;
align-items: center;
max-width: 100%;
padding: 1px 8px;
border-radius: 999px;
border: 1px solid var(--el-color-primary-light-5);
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
font-size: 12px;
line-height: 20px;
}
</style>