| @@ -22,8 +22,8 @@ export default { | |||
| // 要代理的地址 | |||
| // target: 'http://172.20.32.197:31213', // 开发环境 | |||
| // target: 'http://172.20.32.235:31213', // 测试环境 | |||
| target: 'http://172.20.32.44:8082', | |||
| // target: 'http://172.20.32.150:8082', | |||
| // target: 'http://172.20.32.44:8082', | |||
| target: 'http://172.20.32.164:8082', | |||
| // 配置了这个可以从 http 代理到 https | |||
| // 依赖 origin 的功能可能需要这个,比如 cookie | |||
| changeOrigin: true, | |||
| @@ -1,26 +1,18 @@ | |||
| import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource'; | |||
| import { DatasetData, ModelData } from '@/pages/Dataset/config'; | |||
| import { ServiceData } from '@/pages/ModelDeployment/types'; | |||
| import { getDatasetList, getModelList } from '@/services/dataset/index.js'; | |||
| import { getServiceListReq } from '@/services/modelDeployment'; | |||
| import { type SelectProps } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| // id 从 number 转换为 string | |||
| const convertId = (item: any) => ({ | |||
| ...item, | |||
| id: JSON.stringify({ | |||
| id: `${item.id}`, | |||
| name: item.name, | |||
| identifier: item.identifier, | |||
| owner: item.owner, | |||
| }), | |||
| }); | |||
| export type SelectPropsConfig = { | |||
| getOptions: () => Promise<any>; // 获取下拉数据 | |||
| getOptions?: () => Promise<any>; // 获取下拉数据 | |||
| fieldNames?: SelectProps['fieldNames']; // 下拉数据字段 | |||
| optionFilterProp?: SelectProps['optionFilterProp']; // 过滤字段名 | |||
| filterOption?: SelectProps['filterOption']; // 过滤函数 | |||
| getValue: (value: any) => string | number; | |||
| getLabel: (value: any) => string; | |||
| isObjectValue: boolean; // value 是对象 | |||
| }; | |||
| export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||
| @@ -31,13 +23,16 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||
| size: 1000, | |||
| is_public: false, | |||
| }); | |||
| return res?.data?.content?.map(convertId) ?? []; | |||
| return res?.data?.content ?? []; | |||
| }, | |||
| optionFilterProp: 'label', | |||
| getValue: (value: DatasetData) => { | |||
| return value.id; | |||
| }, | |||
| fieldNames: { | |||
| label: 'name', | |||
| value: 'id', | |||
| getLabel: (value: DatasetData) => { | |||
| return value.name; | |||
| }, | |||
| optionFilterProp: 'name', | |||
| isObjectValue: true, | |||
| }, | |||
| model: { | |||
| getOptions: async () => { | |||
| @@ -46,13 +41,16 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||
| size: 1000, | |||
| is_public: false, | |||
| }); | |||
| return res?.data?.content?.map(convertId) ?? []; | |||
| return res?.data?.content ?? []; | |||
| }, | |||
| fieldNames: { | |||
| label: 'name', | |||
| value: 'id', | |||
| optionFilterProp: 'label', | |||
| getValue: (value: ModelData) => { | |||
| return value.id; | |||
| }, | |||
| getLabel: (value: ModelData) => { | |||
| return value.name; | |||
| }, | |||
| optionFilterProp: 'name', | |||
| isObjectValue: true, | |||
| }, | |||
| service: { | |||
| getOptions: async () => { | |||
| @@ -60,25 +58,28 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = { | |||
| page: 0, | |||
| size: 1000, | |||
| }); | |||
| return ( | |||
| res?.data?.content?.map((item: ServiceData) => ({ | |||
| label: item.service_name, | |||
| value: JSON.stringify(pick(item, ['id', 'service_name'])), | |||
| })) ?? [] | |||
| ); | |||
| }, | |||
| fieldNames: { | |||
| label: 'label', | |||
| value: 'value', | |||
| return res?.data?.content ?? []; | |||
| }, | |||
| optionFilterProp: 'label', | |||
| getValue: (value: ServiceData) => { | |||
| return value.id; | |||
| }, | |||
| getLabel: (value: ServiceData) => { | |||
| return value.service_name; | |||
| }, | |||
| isObjectValue: true, | |||
| }, | |||
| resource: { | |||
| getOptions: async () => { | |||
| // 不需要这个函数 | |||
| return []; | |||
| }, | |||
| fieldNames: resourceFieldNames, | |||
| filterOption: filterResourceStandard as SelectProps['filterOption'], | |||
| // 不会用到 | |||
| getValue: () => { | |||
| return ''; | |||
| }, | |||
| // 不会用的 | |||
| getLabel: () => { | |||
| return ''; | |||
| }, | |||
| isObjectValue: false, | |||
| }, | |||
| }; | |||
| @@ -7,7 +7,7 @@ | |||
| import { useComputingResource } from '@/hooks/useComputingResource'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Select, type SelectProps } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useEffect, useMemo, useState } from 'react'; | |||
| import FormInfo from '../FormInfo'; | |||
| import { paramSelectConfig } from './config'; | |||
| @@ -36,69 +36,105 @@ function ParameterSelect({ | |||
| dataType, | |||
| display = false, | |||
| value, | |||
| isPipeline = false, | |||
| // isPipeline = false, | |||
| onChange, | |||
| ...rest | |||
| }: ParameterSelectProps) { | |||
| const [options, setOptions] = useState<SelectProps['options']>([]); | |||
| const propsConfig = paramSelectConfig[dataType]; | |||
| const valueText = typeof value === 'object' && value !== null ? value.value : value; | |||
| const { | |||
| getLabel, | |||
| getValue, | |||
| getOptions, | |||
| filterOption, | |||
| fieldNames, | |||
| optionFilterProp, | |||
| isObjectValue, | |||
| } = propsConfig; | |||
| const selectValue = typeof value === 'object' && value !== null ? value.value : value; | |||
| // 数据集、模型、服务,对象转换成 json 字符串 | |||
| const valueText = | |||
| typeof selectValue === 'object' && selectValue !== null ? getValue(selectValue) : selectValue; | |||
| const [resourceStandardList] = useComputingResource(); | |||
| const computingResource = isPipeline | |||
| ? resourceStandardList.map((v) => ({ | |||
| ...v, | |||
| id: String(v.id), | |||
| })) | |||
| : resourceStandardList; | |||
| // const computingResource = isPipeline | |||
| // ? resourceStandardList.map((v) => ({ | |||
| // ...v, | |||
| // id: String(v.id), | |||
| // })) | |||
| // : resourceStandardList; | |||
| const objectOptions = useMemo(() => { | |||
| return options?.map((v) => ({ | |||
| label: getLabel(v), | |||
| value: getValue(v), | |||
| })); | |||
| }, [getLabel, getValue, options]); | |||
| const valueMap = useMemo(() => { | |||
| const map = new Map<string | number, any>(); | |||
| options?.forEach((v) => { | |||
| map.set(getValue(v), v); | |||
| }); | |||
| return map; | |||
| }, [options, getValue]); | |||
| useEffect(() => { | |||
| // 获取下拉数据 | |||
| const getSelectOptions = async () => { | |||
| if (!propsConfig) { | |||
| return; | |||
| } | |||
| const getOptions = propsConfig.getOptions; | |||
| const [res] = await to(getOptions()); | |||
| if (res) { | |||
| setOptions(res); | |||
| if (getOptions) { | |||
| const [res] = await to(getOptions()); | |||
| if (res) { | |||
| setOptions(res); | |||
| } | |||
| } | |||
| }; | |||
| getSelectOptions(); | |||
| }, [propsConfig]); | |||
| }, [getOptions]); | |||
| const selectOptions = dataType === 'resource' ? computingResource : options; | |||
| const selectOptions = dataType === 'resource' ? resourceStandardList : objectOptions; | |||
| const handleChange = (text: string) => { | |||
| if (typeof value === 'object' && value !== null) { | |||
| onChange?.({ | |||
| ...value, | |||
| value: text, | |||
| }); | |||
| // 数据集、模型、服务,转换成对象 | |||
| if (isObjectValue) { | |||
| // 设置为 null 是因为 antv g6 的 bug | |||
| // 如果值为 undefined 时, graph.changeData(data) 会保留前面的值 | |||
| const selectValue = text ? valueMap.get(text) : null; | |||
| if (typeof value === 'object' && value !== null) { | |||
| onChange?.({ | |||
| ...value, | |||
| value: selectValue, | |||
| }); | |||
| } else { | |||
| onChange?.(selectValue); | |||
| } | |||
| } else { | |||
| onChange?.(text); | |||
| if (typeof value === 'object' && value !== null) { | |||
| onChange?.({ | |||
| ...value, | |||
| value: text, | |||
| }); | |||
| } else { | |||
| onChange?.(text); | |||
| } | |||
| } | |||
| }; | |||
| // 只用于展示,FormInfo 组件带有 Tooltip | |||
| if (display) { | |||
| return ( | |||
| <FormInfo | |||
| select | |||
| value={valueText} | |||
| options={selectOptions} | |||
| fieldNames={propsConfig?.fieldNames} | |||
| ></FormInfo> | |||
| <FormInfo select value={valueText} options={selectOptions} fieldNames={fieldNames}></FormInfo> | |||
| ); | |||
| } | |||
| return ( | |||
| <Select | |||
| {...rest} | |||
| filterOption={propsConfig?.filterOption} | |||
| options={selectOptions} | |||
| fieldNames={propsConfig?.fieldNames} | |||
| optionFilterProp={propsConfig?.optionFilterProp} | |||
| fieldNames={fieldNames} | |||
| optionFilterProp={optionFilterProp} | |||
| filterOption={filterOption} | |||
| value={valueText} | |||
| onChange={handleChange} | |||
| showSearch | |||
| @@ -251,7 +251,7 @@ function ResourceSelectorModal({ | |||
| } | |||
| : { | |||
| activeTab: activeTab, | |||
| id, | |||
| id: Number(id), | |||
| name, | |||
| path: versionPath, | |||
| version, | |||
| @@ -3,7 +3,7 @@ import { useStateRef } from '@/hooks/useStateRef'; | |||
| import { useVisible } from '@/hooks/useVisible'; | |||
| import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { fittingString, parseJsonText, s8 } from '@/utils'; | |||
| import { fittingString, s8 } from '@/utils'; | |||
| import { to } from '@/utils/promise'; | |||
| import G6 from '@antv/g6'; | |||
| import { useNavigate, useParams } from '@umijs/max'; | |||
| @@ -120,8 +120,8 @@ const EditPipeline = () => { | |||
| const params = { | |||
| ...locationParams, | |||
| name: workflowInfo?.name, | |||
| dag: JSON.stringify(data), | |||
| global_param: JSON.stringify(globalParamRes.global_param), | |||
| dag: data, | |||
| global_param: globalParamRes.global_param, | |||
| }; | |||
| saveWorkflow(params).then((ret) => { | |||
| message.success('保存成功'); | |||
| @@ -1,5 +1,4 @@ | |||
| import { PipelineGlobalParam, PipelineNodeModelParameter } from '@/types'; | |||
| import { parseJsonText } from '@/utils'; | |||
| import { Graph, INode } from '@antv/g6'; | |||
| import { type MenuProps } from 'antd'; | |||
| @@ -42,8 +41,7 @@ export function createMenuItems( | |||
| ): MenuProps['items'] { | |||
| const nodes: MenuProps['items'] = parentNodes.map((item) => { | |||
| const model = item.getModel(); | |||
| const out_parameters = model.out_parameters as string | undefined | null; | |||
| const out_parametersObj = parseJsonText(out_parameters); | |||
| const out_parametersObj = model.out_parameters as Record<string, PipelineNodeModelParameter>; | |||
| const outParametersList = Object.keys(out_parametersObj ?? {}); | |||
| return { | |||
| key: model.id as string, | |||
| @@ -23,6 +23,7 @@ import { INode } from '@antv/g6'; | |||
| import { Button, Drawer, Flex, Form, Input, MenuProps } from 'antd'; | |||
| import { RuleObject } from 'antd/es/form'; | |||
| import { NamePath } from 'antd/es/form/interface'; | |||
| import { omit } from 'lodash'; | |||
| import { forwardRef, useImperativeHandle, useState } from 'react'; | |||
| import PropsLabel from '../PropsLabel'; | |||
| import styles from './index.less'; | |||
| @@ -75,8 +76,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| console.log('getFieldsValue', fields); | |||
| const res = { | |||
| ...stagingItem, | |||
| ...fields, | |||
| ...omit(stagingItem, ['control_strategy', 'task_info', 'in_parameters', 'out_parameters']), | |||
| task_info: task_info, | |||
| control_strategy: control_strategy, | |||
| in_parameters: in_parameters, | |||
| @@ -371,7 +371,6 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| ) : null)} | |||
| {item.value.type === ComponentType.Map && ( | |||
| <Form.Item name={[parentName, item.key]} rules={getFormRules(item)} noStyle> | |||
| {' '} | |||
| <Form.List name={[parentName, item.key, 'value']}> | |||
| {(fields, { add, remove }) => ( | |||
| <> | |||
| @@ -385,7 +384,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| <Form.Item | |||
| {...restField} | |||
| name={[name, 'name']} | |||
| style={{ flex: 1 }} | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| dependencies={fields.map((_, i) => [ | |||
| parentName, | |||
| item.key, | |||
| @@ -429,7 +428,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| <Form.Item | |||
| {...restField} | |||
| name={[name, 'value']} | |||
| style={{ flex: 1 }} | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| rules={[ | |||
| { | |||
| validator: requiredValidator, | |||
| @@ -447,7 +446,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| title="" | |||
| onClick={(value) => { | |||
| handleParameterClick( | |||
| [parentName, item.key, 'list', name, 'value'], | |||
| [parentName, item.key, 'value', name, 'value'], | |||
| { | |||
| ...item.value, | |||
| value, | |||