wujingjing
2025-02-17 0f01c4bbce19fa8489a4e835c83cb9415549f681
表单校验
已修改5个文件
205 ■■■■■ 文件已修改
src/components/vue-flow/MainCanvas.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/vue-flow/ui/nodes/LLMNode.vue 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/vue-flow/ui/nodes/components/FieldLayout.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/flowApp/FlowApp.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/flowApp/components/Header.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/vue-flow/MainCanvas.vue
@@ -2,17 +2,17 @@
    <div class="relative h-full w-full" id="main-canvas" @drop="handleOnDrop" @dragover="handleOnDragOver">
        <VueFlow v-model="elements" :node-types="nodeTypes" :connection-mode="ConnectionMode.Strict">
            <template #node-start="startNodeProps">
                <StartNode v-bind="startNodeProps" :isViewMode="isViewMode" />
                <StartNode ref="nodeRef" v-bind="startNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-condition="conditionNodeProps">
                <ConditionNode v-bind="conditionNodeProps" :isViewMode="isViewMode" />
                <ConditionNode ref="nodeRef" v-bind="conditionNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-output_msg="outputNodeProps">
                <OutputNode v-bind="outputNodeProps" :isViewMode="isViewMode" />
                <OutputNode ref="nodeRef" v-bind="outputNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-end="endNodeProps">
                <EndNode v-bind="endNodeProps" :isViewMode="isViewMode" />
                <EndNode ref="nodeRef" v-bind="endNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #edge-custom="customEdgeProps">
@@ -20,30 +20,30 @@
            </template>
            <template #node-agent="agentNodeProps">
                <AgentNode v-bind="agentNodeProps" :agentNames="agentNames" :isViewMode="isViewMode" />
                <AgentNode ref="nodeRef" v-bind="agentNodeProps" :agentNames="agentNames" :isViewMode="isViewMode" />
            </template>
            <template #node-func="funcNodeProps">
                <FuncNode v-bind="funcNodeProps" :funcNames="funcNames" :isViewMode="isViewMode" />
                <FuncNode ref="nodeRef" v-bind="funcNodeProps" :funcNames="funcNames" :isViewMode="isViewMode" />
            </template>
            <template #node-code="codeNodeProps">
                <CodeNode v-bind="codeNodeProps" :isViewMode="isViewMode" />
                <CodeNode ref="nodeRef" v-bind="codeNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-python_code="pythonCodeNodeProps">
                <PythonCodeNode v-bind="pythonCodeNodeProps" :isViewMode="isViewMode" />
                <PythonCodeNode ref="nodeRef" v-bind="pythonCodeNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-text_resource="textResourceNodeProps">
                <TextResourceNode v-bind="textResourceNodeProps" :isViewMode="isViewMode" />
                <TextResourceNode ref="nodeRef" v-bind="textResourceNodeProps" :isViewMode="isViewMode" />
            </template>
            <template #node-analysis="analysisNodeProps">
                <AnalysisNode v-bind="analysisNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" />
                <AnalysisNode ref="nodeRef" v-bind="analysisNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" />
            </template>
            <template #node-LLM="llmNodeProps">
                <LLMNode v-bind="llmNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" />
                <LLMNode ref="nodeRef" v-bind="llmNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" />
            </template>
            <Controls :showInteractive="false"/>
            <Controls :showInteractive="false" />
            <Background />
            <MiniMap pannable zoomable />
        </VueFlow>
@@ -55,7 +55,7 @@
import { Controls } from '@vue-flow/controls';
import { MiniMap } from '@vue-flow/minimap';
import type { Dimensions, Elements, FlowOptions, FlowProps } from '@vue-flow/core';
import type { Dimensions, Elements, FlowProps } from '@vue-flow/core';
import { ConnectionMode, MarkerType, useVueFlow, VueFlow } from '@vue-flow/core';
import { nextTick, onMounted, ref, watch } from 'vue';
import { Test_data } from './testData';
@@ -63,12 +63,12 @@
import AgentNode from './ui/nodes/AgentNode.vue';
import AnalysisNode from './ui/nodes/AnalysisNode.vue';
import CodeNode from './ui/nodes/CodeNode.vue';
import PythonCodeNode from './ui/nodes/PythonCodeNode.vue';
import ConditionNode from './ui/nodes/ConditionNode.vue';
import EndNode from './ui/nodes/EndNode.vue';
import FuncNode from './ui/nodes/FuncNode.vue';
import LLMNode from './ui/nodes/LLMNode.vue';
import OutputNode from './ui/nodes/OutputNode.vue';
import PythonCodeNode from './ui/nodes/PythonCodeNode.vue';
import StartNode from './ui/nodes/StartNode.vue';
import TextResourceNode from './ui/nodes/TextResourceNode.vue';
import { NodeType, nodeTypeMap } from './vueFlowEnum';
@@ -78,7 +78,20 @@
const nodeTypes = {};
const elements = ref<Elements>();
const { findNode, nodes, addNodes, addEdges, project, vueFlowRef, onConnect, setNodes, setEdges, setViewport, fitView,setInteractive } = useVueFlow(
const {
    findNode,
    nodes,
    addNodes,
    addEdges,
    project,
    vueFlowRef,
    onConnect,
    setNodes,
    setEdges,
    setViewport,
    fitView,
    setInteractive,
} = useVueFlow(
    Object.keys(props.flowJson).length > 0
        ? props.flowJson
        : ({
@@ -92,8 +105,6 @@
                    },
                ],
                edges: [],
          } as FlowProps)
);
@@ -115,11 +126,11 @@
    // addEdges());
});
const initFlowStatus = () =>{
const initFlowStatus = () => {
    fitView();
    setInteractive(!props.isViewMode);
}
};
onMounted(() => {
    setTimeout(() => {
        initFlowStatus();
@@ -178,6 +189,15 @@
        event.dataTransfer.dropEffect = 'move';
    }
}
const nodeRef = ref();
const validateForm = () => {
    // nodeRef.value.validateForm();
    console.log('🚀 ~ nodeRef.value:', nodeRef.value);
};
defineExpose({
    validateForm,
});
</script>
<style lang="scss">
@import '@vue-flow/core/dist/style.css';
src/components/vue-flow/ui/nodes/LLMNode.vue
@@ -6,48 +6,68 @@
        :isViewMode="isViewMode"
    >
        <Handle :id="targetHandleId" type="target" :position="Position.Left" />
        <FieldLayout :title="VueFlowHelper.getConfigValue(modelSetting, 'name', '模型设置')">
            <div class="w-full flex-items-center gap-x-2">
                <div class="flex-column gap-y-1.5">
                    <span class="text-gray-500">{{ VueFlowHelper.getParams(modelSetting, 'llm_model').label }}</span>
                    <el-tree-select
                        v-model="VueFlowHelper.getParams(modelSetting, 'llm_model').value"
                        :data="llmInfoList"
                        :props="{ label: 'name', value: 'id' }"
                        :render-after-expand="false"
                        :check-strictly="false"
                        :show-checkbox="false"
                        :accordion="true"
                        :disabled="isViewMode"
                        filterable
                        :placeholder="VueFlowHelper.getParams(modelSetting, 'llm_model').placeholder"
                    />
        <el-form :model="data" :rules="formRules" label-position="right" label-width="60px" status-icon>
            <FieldLayout :title="VueFlowHelper.getConfigValue(modelSetting, 'name', '模型设置')">
                <div class="w-full flex-items-center gap-x-2">
                    <div class="flex-column gap-y-1.5">
                        <el-form-item
                            labelPosition="top"
                            :label="VueFlowHelper.getParams(modelSetting, 'llm_model').label"
                            prop="group_params.0.params.0.value"
                        >
                            <el-tree-select
                                v-model="VueFlowHelper.getParams(modelSetting, 'llm_model').value"
                                :data="llmInfoList"
                                :props="{ label: 'name', value: 'id' }"
                                :render-after-expand="false"
                                :check-strictly="false"
                                :show-checkbox="false"
                                :accordion="true"
                                :disabled="isViewMode"
                                filterable
                                clearable
                                :placeholder="VueFlowHelper.getParams(modelSetting, 'llm_model').placeholder"
                            />
                        </el-form-item>
                    </div>
                    <div class="flex-column gap-y-1.5">
                        <el-form-item
                            labelPosition="top"
                            :label="VueFlowHelper.getParams(modelSetting, 'temperature').label"
                            prop="group_params.0.params.1.value"
                        >
                            <el-input-number
                                class="w-full"
                                controls-position="right"
                                v-model="VueFlowHelper.getParams(modelSetting, 'temperature').value"
                                :readonly="isViewMode"
                                placeholder=""
                            ></el-input-number>
                        </el-form-item>
                    </div>
                </div>
                <div class="flex-column gap-y-1.5">
                    <span class="text-gray-500">{{ VueFlowHelper.getParams(modelSetting, 'temperature').label }}</span>
                    <el-input-number
                        class="w-full"
                        controls-position="right"
                        v-model="VueFlowHelper.getParams(modelSetting, 'temperature').value"
            </FieldLayout>
            <FieldLayout :title="prompt.name">
                <CodeEditor
                    :title="prompt.name"
                    class="nowheel"
                    v-model:editValue="VueFlowHelper.getParams(prompt, 'prompt').value"
                    :maxHeight="180"
                    :disabled="isViewMode"
                />
            </FieldLayout>
            <FieldLayout :title="outputParam.name" >
                <el-form-item labelWidth="0" prop="group_params.2.params.0.value">
                    <el-input
                        class="w-full flex-0"
                        v-model="outputParam.params[0].value"
                        placeholder="输出参数名"
                        :readonly="isViewMode"
                        placeholder=""
                    ></el-input-number>
                </div>
            </div>
        </FieldLayout>
        <FieldLayout :title="prompt.name">
            <CodeEditor
                :title="prompt.name"
                class="nowheel"
                v-model:editValue="VueFlowHelper.getParams(prompt, 'prompt').value"
                :maxHeight="180"
                :disabled="isViewMode"
            />
        </FieldLayout>
        <FieldLayout :title="outputParam.name">
            <el-input class="w-full flex-0" v-model="outputParam.params[0].value" placeholder="输出参数名" :readonly="isViewMode"></el-input>
        </FieldLayout>
                    ></el-input>
                </el-form-item>
            </FieldLayout>
        </el-form>
        <Handle :id="sourceHandleId" type="source" :position="Position.Right" />
    </NodeBasicLayout>
</template>
@@ -75,8 +95,27 @@
const targetHandleId = ref(VueFlowHelper.getHandleId(node.node, 'target'));
const data = ref(node.node.data);
console.log('🚀 ~ data.value:', data.value);
const modelSetting = ref(VueFlowHelper.getGroupParam(data.value, 0));
const prompt = ref(VueFlowHelper.getGroupParam(data.value, 1));
const outputParam = ref(VueFlowHelper.getGroupParam(data.value, 2));
VueFlowHelper.getConfigValue(VueFlowHelper.getParams(prompt.value, 'prompt'), 'label', '');
const formRules = ref({
    'group_params.0.params.0.value': [{ required: true, message: '请选择模型', trigger: 'change' }],
    // 'group_params.0.params.1.value': [{ required: true, message: '请输入温度', trigger: 'blur' }],
    // 'group_params.2.params.0.value': [{ required: true, message: '请输入输出参数名', trigger: 'blur' }],
});
const formRef = ref();
const validateForm = async () => {
    const valid = await formRef.value.validate();
    if (valid) {
        console.log('submit!');
    }
};
defineExpose({
    validateForm,
});
</script>
src/components/vue-flow/ui/nodes/components/FieldLayout.vue
@@ -1,7 +1,9 @@
<template>
    <div class="flex flex-col" :class="[`gap-${gapLen}`]">
        <div class="flex-items-center justify-between">
            <div class="text-base" :class="[level === '1' ? 'font-bold' : 'text-secondary font-bold']">{{ title }}</div>
            <div class="text-base" :class="[level === '1' ? 'font-bold' : 'text-secondary font-bold', { 'required-field': required }]">
                {{ title }}
            </div>
            <div class="flex-items-center gap-3" v-if="$slots.right">
                <slot name="right"></slot>
            </div>
@@ -25,6 +27,10 @@
        type: String as () => HeaderLevel,
        default: '1',
    },
    required: {
        type: Boolean,
        default: false,
    },
});
const gapLen = computed(() => {
@@ -35,4 +41,10 @@
    }
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.required-field::before {
    color: var(--el-color-danger);
    content: '*';
    margin-right: 4px;
}
</style>
src/views/project/yw/systemManage/flowApp/FlowApp.vue
@@ -1,11 +1,12 @@
<template>
    <div class="absolute bottom-0 left-0 right-0 top-0 bg-page text-base">
        <div class="relative flex h-full w-full flex-col">
            <Header :isViewMode="isViewMode" v-if="flowAgent" :flowAgent="flowAgent" :queryId="queryId" />
            <Header :isViewMode="isViewMode" v-if="flowAgent" :flowAgent="flowAgent" :queryId="queryId" @saveClick="validateForm" />
            <main class="relative flex h-full w-full flex-1">
                <Sidebar v-if="!isViewMode" class="w-52" @dragstart="handleOnDragStart" />
                <div class="relative h-full flex-1 overflow-hidden" v-if="flowJson">
                    <MainCanvas
                        ref="mainCanvasRef"
                        :isViewMode="isViewMode"
                        :flowJson="flowJson"
                        :agentNames="agentNames"
@@ -110,6 +111,12 @@
    llmInfoList.value = logicTree;
};
const mainCanvasRef = ref();
const validateForm = () => {
    mainCanvasRef.value.validateForm();
};
onMounted(() => {
    if (!queryId.value) return;
    handleGetJSON(queryId.value);
src/views/project/yw/systemManage/flowApp/components/Header.vue
@@ -53,7 +53,7 @@
        default: false,
    },
});
const emit = defineEmits(['export']);
const emit = defineEmits(['export','saveClick']);
const { toObject, fromObject, nodes, edges, removeEdges, removeNodes } = useVueFlow();
const { files, open, reset, onCancel, onChange } = useFileDialog({
    //   accept: 'image/*', // Set to accept only image files
@@ -84,6 +84,7 @@
        json_flow: jsonStr,
    });
    ElMessage.success('保存成功!');
    emit('saveClick');
};
onChange((file) => {