feat: restore workflow editing experience
This commit is contained in:
@@ -0,0 +1,195 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
autocompletion?: boolean;
|
||||||
|
bracketMatching?: boolean;
|
||||||
|
foldGutter?: boolean;
|
||||||
|
height?: number | string;
|
||||||
|
language?: string;
|
||||||
|
lineNumbers?: boolean;
|
||||||
|
modelValue?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
readonly?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
autocompletion: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
foldGutter: false,
|
||||||
|
height: '240px',
|
||||||
|
language: 'text',
|
||||||
|
lineNumbers: false,
|
||||||
|
modelValue: '',
|
||||||
|
placeholder: '',
|
||||||
|
readonly: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
change: [value: string];
|
||||||
|
'update:modelValue': [value: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const lineNumbersRef = ref<HTMLPreElement | null>(null);
|
||||||
|
const textareaRef = ref<HTMLTextAreaElement | null>(null);
|
||||||
|
|
||||||
|
const editorStyle = computed(() => ({
|
||||||
|
height: typeof props.height === 'number' ? `${props.height}px` : props.height,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const lineNumbersText = computed(() => {
|
||||||
|
const count = Math.max(1, props.modelValue.split(/\r\n|\r|\n/).length);
|
||||||
|
return Array.from({ length: count }, (_, index) => index + 1).join('\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
const languageLabel = computed(() => {
|
||||||
|
const language = props.language === 'python3' ? 'python' : props.language;
|
||||||
|
return language.toUpperCase();
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleInput(event: Event) {
|
||||||
|
const value = (event.target as HTMLTextAreaElement).value;
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(event: Event) {
|
||||||
|
const value = (event.target as HTMLTextAreaElement).value;
|
||||||
|
emit('change', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
|
if (props.readonly || event.key !== 'Tab') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
const target = event.target as HTMLTextAreaElement;
|
||||||
|
const start = target.selectionStart;
|
||||||
|
const end = target.selectionEnd;
|
||||||
|
const value = props.modelValue || '';
|
||||||
|
const nextValue = `${value.slice(0, start)} ${value.slice(end)}`;
|
||||||
|
emit('update:modelValue', nextValue);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
target.selectionStart = start + 2;
|
||||||
|
target.selectionEnd = start + 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll(event: Event) {
|
||||||
|
if (!lineNumbersRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNumbersRef.value.scrollTop = (event.target as HTMLTextAreaElement).scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus: () => textareaRef.value?.focus(),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="code-editor"
|
||||||
|
:class="{ 'code-editor--readonly': readonly }"
|
||||||
|
:style="editorStyle"
|
||||||
|
>
|
||||||
|
<div class="code-editor__body">
|
||||||
|
<pre
|
||||||
|
v-if="lineNumbers"
|
||||||
|
ref="lineNumbersRef"
|
||||||
|
class="code-editor__lines"
|
||||||
|
aria-hidden="true"
|
||||||
|
>{{ lineNumbersText }}</pre
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
ref="textareaRef"
|
||||||
|
class="code-editor__textarea"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:readonly="readonly"
|
||||||
|
:spellcheck="false"
|
||||||
|
:value="modelValue"
|
||||||
|
@change="handleChange"
|
||||||
|
@input="handleInput"
|
||||||
|
@keydown="handleKeydown"
|
||||||
|
@scroll="handleScroll"
|
||||||
|
></textarea>
|
||||||
|
<span v-if="language" class="code-editor__language">
|
||||||
|
{{ languageLabel }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.code-editor {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid hsl(var(--border));
|
||||||
|
border-radius: 6px;
|
||||||
|
background: hsl(var(--muted) / 45%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor__body {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor__lines {
|
||||||
|
min-width: 42px;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-right: 1px solid hsl(var(--border));
|
||||||
|
color: hsl(var(--muted-foreground) / 70%);
|
||||||
|
font-family:
|
||||||
|
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
||||||
|
'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
text-align: right;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor__textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
resize: none;
|
||||||
|
background: transparent;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
font-family:
|
||||||
|
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
||||||
|
'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
tab-size: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor__textarea::placeholder {
|
||||||
|
color: hsl(var(--muted-foreground) / 55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor__language {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
bottom: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--muted-foreground) / 55%);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor--readonly .code-editor__textarea {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as CodeEditor } from './code-editor.vue';
|
||||||
@@ -946,6 +946,9 @@
|
|||||||
"userInput": "用户输入",
|
"userInput": "用户输入",
|
||||||
"sessionId": "会话 ID",
|
"sessionId": "会话 ID",
|
||||||
"agentId": "智能体 ID",
|
"agentId": "智能体 ID",
|
||||||
|
"appId": "应用 ID",
|
||||||
|
"appCode": "应用编码",
|
||||||
|
"formCode": "表单编码",
|
||||||
"renderResult": "渲染结果",
|
"renderResult": "渲染结果",
|
||||||
"subflowFullResult": "子流程完整结果",
|
"subflowFullResult": "子流程完整结果",
|
||||||
"execSuccess": "执行是否成功",
|
"execSuccess": "执行是否成功",
|
||||||
@@ -1066,7 +1069,10 @@
|
|||||||
"start": {
|
"start": {
|
||||||
"userInputDesc": "用户在对话框中输入的内容,自动传入此变量",
|
"userInputDesc": "用户在对话框中输入的内容,自动传入此变量",
|
||||||
"sessionIdDesc": "当前对话会话 ID,用于多轮记忆和执行追踪",
|
"sessionIdDesc": "当前对话会话 ID,用于多轮记忆和执行追踪",
|
||||||
"agentIdDesc": "当前调用智能体 ID,用于智能体协作和审计"
|
"agentIdDesc": "当前调用智能体 ID,用于智能体协作和审计",
|
||||||
|
"appIdDesc": "当前应用 ID,用于应用内流程隔离和上下文注入",
|
||||||
|
"appCodeDesc": "当前应用编码,用于跨流程引用应用上下文",
|
||||||
|
"formCodeDesc": "当前表单编码,用于表单触发或表单相关流程"
|
||||||
},
|
},
|
||||||
"choice": {
|
"choice": {
|
||||||
"label": "选项节点",
|
"label": "选项节点",
|
||||||
|
|||||||
+17
-24
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '@vben/icons';
|
} from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { CodeEditor } from '#/components/zq-form/code-editor';
|
||||||
import { ZqDialog } from '#/components/zq-dialog';
|
import { ZqDialog } from '#/components/zq-dialog';
|
||||||
|
|
||||||
export interface IterationResult {
|
export interface IterationResult {
|
||||||
@@ -253,8 +254,14 @@ const formattedInputs = computed(() => {
|
|||||||
<div class="text-foreground mb-2 text-sm font-medium">
|
<div class="text-foreground mb-2 text-sm font-medium">
|
||||||
{{ $t('ai-platform.workflow.editor.nodeResultCard.input') }}
|
{{ $t('ai-platform.workflow.editor.nodeResultCard.input') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="result-json-view min-h-0 flex-1 overflow-auto rounded border">
|
<div class="min-h-0 flex-1 overflow-hidden">
|
||||||
<pre>{{ formattedInputs }}</pre>
|
<CodeEditor
|
||||||
|
:model-value="formattedInputs"
|
||||||
|
language="json"
|
||||||
|
height="100%"
|
||||||
|
:line-numbers="true"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -262,8 +269,14 @@ const formattedInputs = computed(() => {
|
|||||||
<div class="text-foreground mb-2 text-sm font-medium">
|
<div class="text-foreground mb-2 text-sm font-medium">
|
||||||
{{ $t('ai-platform.workflow.editor.nodeResultCard.output') }}
|
{{ $t('ai-platform.workflow.editor.nodeResultCard.output') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="result-json-view min-h-0 flex-1 overflow-auto rounded border">
|
<div class="min-h-0 flex-1 overflow-hidden">
|
||||||
<pre>{{ formattedOutput }}</pre>
|
<CodeEditor
|
||||||
|
:model-value="formattedOutput"
|
||||||
|
language="json"
|
||||||
|
height="100%"
|
||||||
|
:line-numbers="true"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -462,23 +475,3 @@ const formattedInputs = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.result-json-view {
|
|
||||||
background: hsl(var(--muted) / 35%);
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-json-view pre {
|
|
||||||
min-height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
color: hsl(var(--muted-foreground));
|
|
||||||
font-family:
|
|
||||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
||||||
'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.6;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
+15
@@ -395,6 +395,21 @@ const availableNodes = computed(() => {
|
|||||||
),
|
),
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'application_id',
|
||||||
|
label: $t('ai-platform.workflow.editor.variableSelector.appId'),
|
||||||
|
isSystem: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'application_code',
|
||||||
|
label: $t('ai-platform.workflow.editor.variableSelector.appCode'),
|
||||||
|
isSystem: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'form_code',
|
||||||
|
label: $t('ai-platform.workflow.editor.variableSelector.formCode'),
|
||||||
|
isSystem: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
// 加上自定义变量
|
// 加上自定义变量
|
||||||
variables.push(
|
variables.push(
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import {
|
|||||||
runWorkflowStreamApi,
|
runWorkflowStreamApi,
|
||||||
updateWorkflowApi,
|
updateWorkflowApi,
|
||||||
} from '#/api/ai-platform/ai-platform';
|
} from '#/api/ai-platform/ai-platform';
|
||||||
|
import { useAppContextStore } from '#/store/app-context';
|
||||||
import { useWorkflowEditorStore } from '#/store/workflow-editor';
|
import { useWorkflowEditorStore } from '#/store/workflow-editor';
|
||||||
|
|
||||||
import RunDialog from './components/RunDialog.vue';
|
import RunDialog from './components/RunDialog.vue';
|
||||||
@@ -80,6 +81,7 @@ const AiChatPanel = defineAsyncComponent(
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const appContextStore = useAppContextStore();
|
||||||
const workflowId = route.params.id as string;
|
const workflowId = route.params.id as string;
|
||||||
|
|
||||||
// 使用 Pinia store 管理工作流编辑器状态
|
// 使用 Pinia store 管理工作流编辑器状态
|
||||||
@@ -1822,7 +1824,7 @@ const handleNodeComplete = (event: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
router.push('/ai-platform/workflow');
|
router.push(appContextStore.getContextPath('/ai-platform/workflow'));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 键盘快捷键
|
// 键盘快捷键
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
ElTooltip,
|
ElTooltip,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
|
|
||||||
|
import { CodeEditor } from '#/components/zq-form/code-editor';
|
||||||
import { ZqDialog } from '#/components/zq-dialog';
|
import { ZqDialog } from '#/components/zq-dialog';
|
||||||
|
|
||||||
import SmartInput from '../components/SmartInput.vue';
|
import SmartInput from '../components/SmartInput.vue';
|
||||||
@@ -185,13 +186,14 @@ const codeEditorVisible = ref(false);
|
|||||||
</ElButton>
|
</ElButton>
|
||||||
</ElTooltip>
|
</ElTooltip>
|
||||||
</div>
|
</div>
|
||||||
<ElInput
|
<CodeEditor
|
||||||
v-model="form.code"
|
v-model="form.code"
|
||||||
type="textarea"
|
language="python"
|
||||||
:rows="14"
|
height="280px"
|
||||||
resize="vertical"
|
|
||||||
class="workflow-code-textarea"
|
|
||||||
:placeholder="$t('ai-platform.workflow.panels.code.codePlaceholder')"
|
:placeholder="$t('ai-platform.workflow.panels.code.codePlaceholder')"
|
||||||
|
:autocompletion="true"
|
||||||
|
:bracket-matching="true"
|
||||||
|
:fold-gutter="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
@@ -204,12 +206,15 @@ const codeEditorVisible = ref(false);
|
|||||||
:show-footer="false"
|
:show-footer="false"
|
||||||
>
|
>
|
||||||
<div class="code-editor-dialog-content">
|
<div class="code-editor-dialog-content">
|
||||||
<ElInput
|
<CodeEditor
|
||||||
v-model="form.code"
|
v-model="form.code"
|
||||||
type="textarea"
|
language="python"
|
||||||
resize="none"
|
height="100%"
|
||||||
class="workflow-code-textarea workflow-code-textarea--dialog"
|
|
||||||
:placeholder="$t('ai-platform.workflow.panels.code.codePlaceholder')"
|
:placeholder="$t('ai-platform.workflow.panels.code.codePlaceholder')"
|
||||||
|
:autocompletion="true"
|
||||||
|
:bracket-matching="true"
|
||||||
|
:fold-gutter="true"
|
||||||
|
:line-numbers="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ZqDialog>
|
</ZqDialog>
|
||||||
@@ -239,6 +244,12 @@ const codeEditorVisible = ref(false);
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-editor-dialog .code-editor-dialog-content .code-editor {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
.code-editor-dialog.is-fullscreen .el-dialog__body {
|
.code-editor-dialog.is-fullscreen .el-dialog__body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -255,16 +266,4 @@ const codeEditorVisible = ref(false);
|
|||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-code-textarea textarea {
|
|
||||||
font-family:
|
|
||||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
||||||
'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow-code-textarea--dialog,
|
|
||||||
.workflow-code-textarea--dialog textarea {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
ElTabs,
|
ElTabs,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
|
|
||||||
|
import { CodeEditor } from '#/components/zq-form/code-editor';
|
||||||
|
|
||||||
import SmartInput from '../components/SmartInput.vue';
|
import SmartInput from '../components/SmartInput.vue';
|
||||||
|
|
||||||
const props = defineProps<{ data: any; nodeId: string }>();
|
const props = defineProps<{ data: any; nodeId: string }>();
|
||||||
@@ -173,13 +175,13 @@ const removeParam = (index: number) => {
|
|||||||
</ElTabPane>
|
</ElTabPane>
|
||||||
|
|
||||||
<ElTabPane label="Body">
|
<ElTabPane label="Body">
|
||||||
<ElInput
|
<CodeEditor
|
||||||
v-model="form.body"
|
v-model="form.body"
|
||||||
type="textarea"
|
language="json"
|
||||||
:rows="10"
|
height="200px"
|
||||||
resize="vertical"
|
|
||||||
class="workflow-code-textarea"
|
|
||||||
placeholder='{"key": "value"}'
|
placeholder='{"key": "value"}'
|
||||||
|
:bracket-matching="true"
|
||||||
|
:fold-gutter="true"
|
||||||
/>
|
/>
|
||||||
<div class="text-muted-foreground mt-1 text-xs">
|
<div class="text-muted-foreground mt-1 text-xs">
|
||||||
{{ $t('ai-platform.workflow.panels.http.bodyHint') }}
|
{{ $t('ai-platform.workflow.panels.http.bodyHint') }}
|
||||||
@@ -205,11 +207,4 @@ const removeParam = (index: number) => {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.workflow-code-textarea textarea) {
|
|
||||||
font-family:
|
|
||||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
||||||
'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -76,6 +76,42 @@ const handleDelete = (index: number) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="border-primary/30 bg-primary/5 rounded border p-3">
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-primary text-sm font-medium">application_id</span>
|
||||||
|
<span class="text-muted-foreground text-xs">{{
|
||||||
|
$t('ai-platform.workflow.panels.common.sysVar')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted-foreground text-xs">
|
||||||
|
{{ $t('ai-platform.workflow.panels.start.appIdDesc') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-primary/30 bg-primary/5 rounded border p-3">
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-primary text-sm font-medium">application_code</span>
|
||||||
|
<span class="text-muted-foreground text-xs">{{
|
||||||
|
$t('ai-platform.workflow.panels.common.sysVar')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted-foreground text-xs">
|
||||||
|
{{ $t('ai-platform.workflow.panels.start.appCodeDesc') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-primary/30 bg-primary/5 rounded border p-3">
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-primary text-sm font-medium">form_code</span>
|
||||||
|
<span class="text-muted-foreground text-xs">{{
|
||||||
|
$t('ai-platform.workflow.panels.common.sysVar')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted-foreground text-xs">
|
||||||
|
{{ $t('ai-platform.workflow.panels.start.formCodeDesc') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-foreground text-sm font-medium">{{
|
<span class="text-foreground text-sm font-medium">{{
|
||||||
$t('ai-platform.workflow.panels.common.customVars')
|
$t('ai-platform.workflow.panels.common.customVars')
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
ElOption,
|
ElOption,
|
||||||
ElPagination,
|
ElPagination,
|
||||||
ElSelect,
|
ElSelect,
|
||||||
|
ElSwitch,
|
||||||
ElTag,
|
ElTag,
|
||||||
ElTooltip,
|
ElTooltip,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
@@ -51,11 +52,14 @@ import {
|
|||||||
updateWorkflowApi,
|
updateWorkflowApi,
|
||||||
} from '#/api/ai-platform/ai-platform';
|
} from '#/api/ai-platform/ai-platform';
|
||||||
import { ZqDialog } from '#/components/zq-dialog';
|
import { ZqDialog } from '#/components/zq-dialog';
|
||||||
|
import { useAppContextStore } from '#/store/app-context';
|
||||||
|
|
||||||
import RunDialog from '../workflow/editor/components/RunDialog.vue';
|
import RunDialog from '../workflow/editor/components/RunDialog.vue';
|
||||||
import ImportDialog from './modules/import-dialog.vue';
|
import ImportDialog from './modules/import-dialog.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const appContextStore = useAppContextStore();
|
||||||
|
const isMainApp = computed(() => appContextStore.isMainApp);
|
||||||
|
|
||||||
// 工作流类型选项
|
// 工作流类型选项
|
||||||
const workflowTypeOptions = computed(() => [
|
const workflowTypeOptions = computed(() => [
|
||||||
@@ -114,6 +118,7 @@ async function handleConfirm() {
|
|||||||
} else {
|
} else {
|
||||||
// 创建模式
|
// 创建模式
|
||||||
const res = await createWorkflowApi({
|
const res = await createWorkflowApi({
|
||||||
|
application_id: appContextStore.currentApp?.id,
|
||||||
is_global: createForm.is_global,
|
is_global: createForm.is_global,
|
||||||
name: createForm.name,
|
name: createForm.name,
|
||||||
code: createForm.code,
|
code: createForm.code,
|
||||||
@@ -123,7 +128,11 @@ async function handleConfirm() {
|
|||||||
ElMessage.success($t('ai-platform.workflow.createSuccess'));
|
ElMessage.success($t('ai-platform.workflow.createSuccess'));
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
fetchList();
|
fetchList();
|
||||||
router.push(`/ai-platform/workflow/editor/${res.id}`);
|
router.push(
|
||||||
|
appContextStore.getContextPath(
|
||||||
|
`/ai-platform/workflow/editor/${res.id}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// error handled by request interceptor
|
// error handled by request interceptor
|
||||||
@@ -156,6 +165,7 @@ async function fetchList() {
|
|||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
name: searchKeyword.value || undefined,
|
name: searchKeyword.value || undefined,
|
||||||
|
applicationId: appContextStore.currentApp?.id,
|
||||||
});
|
});
|
||||||
workflowList.value = res.items;
|
workflowList.value = res.items;
|
||||||
pagination.total = res.total;
|
pagination.total = res.total;
|
||||||
@@ -223,7 +233,9 @@ const handleQuickRun = async (row: WorkflowListItem) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (row: WorkflowListItem) => {
|
const handleEdit = (row: WorkflowListItem) => {
|
||||||
router.push(`/ai-platform/workflow/editor/${row.id}`);
|
router.push(
|
||||||
|
appContextStore.getContextPath(`/ai-platform/workflow/editor/${row.id}`),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (row: WorkflowListItem) => {
|
const handleDelete = async (row: WorkflowListItem) => {
|
||||||
@@ -296,7 +308,9 @@ const handleCopy = async (row: WorkflowListItem) => {
|
|||||||
const res = await copyWorkflowApi(row.id);
|
const res = await copyWorkflowApi(row.id);
|
||||||
ElMessage.success($t('ai-platform.workflow.copySuccess'));
|
ElMessage.success($t('ai-platform.workflow.copySuccess'));
|
||||||
fetchList();
|
fetchList();
|
||||||
router.push(`/ai-platform/workflow/editor/${res.id}`);
|
router.push(
|
||||||
|
appContextStore.getContextPath(`/ai-platform/workflow/editor/${res.id}`),
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error !== 'cancel') {
|
if (error !== 'cancel') {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -333,7 +347,7 @@ const getWorkflowTypeText = (type: string) => {
|
|||||||
const item = workflowTypeOptions.value.find(
|
const item = workflowTypeOptions.value.find(
|
||||||
(o: { label: string; value: string }) => o.value === type,
|
(o: { label: string; value: string }) => o.value === type,
|
||||||
);
|
);
|
||||||
return item?.label || workflowTypeLabelMap[type] || '智能体协作';
|
return item?.label || workflowTypeLabelMap[type] || type;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusType = (status: string) => {
|
const getStatusType = (status: string) => {
|
||||||
@@ -458,7 +472,11 @@ onMounted(() => {
|
|||||||
{{ $t('ai-platform.workflow.publish') }}
|
{{ $t('ai-platform.workflow.publish') }}
|
||||||
</ElDropdownItem>
|
</ElDropdownItem>
|
||||||
<ElDropdownItem
|
<ElDropdownItem
|
||||||
v-if="item.status === 'published'"
|
v-if="
|
||||||
|
item.status === 'published' &&
|
||||||
|
(item.workflow_type === 'data_process' ||
|
||||||
|
item.workflow_type === 'automation')
|
||||||
|
"
|
||||||
command="run"
|
command="run"
|
||||||
>
|
>
|
||||||
<Play class="mr-2 h-4 w-4" />
|
<Play class="mr-2 h-4 w-4" />
|
||||||
@@ -503,8 +521,10 @@ onMounted(() => {
|
|||||||
<!-- 应用名称 + 创建时间 -->
|
<!-- 应用名称 + 创建时间 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="text-muted-foreground text-xs">
|
<div class="text-muted-foreground text-xs">
|
||||||
v{{ item.published_version || item.version || '-' }} ·
|
<span v-if="item.application_name">
|
||||||
{{ item.run_count || 0 }} 次运行
|
{{ item.application_name }}
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ $t('ai-platform.workflow.mainApp') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-muted-foreground text-xs">
|
<span class="text-muted-foreground text-xs">
|
||||||
{{ item.sys_create_datetime }}
|
{{ item.sys_create_datetime }}
|
||||||
@@ -608,6 +628,12 @@ onMounted(() => {
|
|||||||
:rows="3"
|
:rows="3"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
<ElFormItem
|
||||||
|
v-if="isMainApp"
|
||||||
|
:label="$t('ai-platform.workflow.form.globalVisible')"
|
||||||
|
>
|
||||||
|
<ElSwitch v-model="createForm.is_global" />
|
||||||
|
</ElFormItem>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
</ZqDialog>
|
</ZqDialog>
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
importWorkflowConfigApi,
|
importWorkflowConfigApi,
|
||||||
} from '#/api/ai-platform/ai-platform';
|
} from '#/api/ai-platform/ai-platform';
|
||||||
import { ZqDialog } from '#/components/zq-dialog';
|
import { ZqDialog } from '#/components/zq-dialog';
|
||||||
|
import { useAppContextStore } from '#/store/app-context';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
@@ -36,6 +37,8 @@ const emit = defineEmits<{
|
|||||||
'update:modelValue': [value: boolean];
|
'update:modelValue': [value: boolean];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const appContextStore = useAppContextStore();
|
||||||
|
|
||||||
const dialogVisible = computed({
|
const dialogVisible = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (val) => emit('update:modelValue', val),
|
set: (val) => emit('update:modelValue', val),
|
||||||
@@ -129,6 +132,7 @@ async function handleConfirmImport() {
|
|||||||
try {
|
try {
|
||||||
const payload: WorkflowImportInput = {
|
const payload: WorkflowImportInput = {
|
||||||
...importData.value,
|
...importData.value,
|
||||||
|
application_id: appContextStore.currentApp?.id,
|
||||||
code: checkResult.value?.code_exists
|
code: checkResult.value?.code_exists
|
||||||
? newCode.value.trim()
|
? newCode.value.trim()
|
||||||
: importData.value.code,
|
: importData.value.code,
|
||||||
|
|||||||
Reference in New Issue
Block a user