diff --git a/.aci.yml b/.aci.yml new file mode 100644 index 0000000..04882f5 --- /dev/null +++ b/.aci.yml @@ -0,0 +1,14 @@ +version: "2.0" + +stages: + - 敏感信息检测 + +jobs: + Keymap扫描: + id: scan + stage: 敏感信息检测 + component: keymapscan + +workflow: + stages: + 敏感信息检测: diff --git a/apps/platform/package.json b/apps/platform/package.json index 00114b6..c736d64 100644 --- a/apps/platform/package.json +++ b/apps/platform/package.json @@ -54,6 +54,5 @@ "peerDependencies": { "antd": "^5.0.0", "react": "^18.0.0" - }, - "repository": "https://code.alipay.com/Secret-Flow/platform-web.git" + } } diff --git a/apps/platform/src/assets/data-source.svg b/apps/platform/src/assets/data-source.svg new file mode 100644 index 0000000..da1a56f --- /dev/null +++ b/apps/platform/src/assets/data-source.svg @@ -0,0 +1,8 @@ + + + + diff --git a/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-service.ts b/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-service.ts index 0d8f940..598e4bc 100644 --- a/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-service.ts +++ b/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-service.ts @@ -1,33 +1,72 @@ +import { message } from 'antd'; + +import { getGraphDetail, fullUpdateGraph } from '@/services/secretpad/GraphController'; +import { projectGraphDomainDataSourceList } from '@/services/secretpad/ProjectController'; + import { Model } from '@/util/valtio-helper'; -import API from '@/services/secretpad'; -import { message } from 'antd'; +export type DataSourceConfig = { + editAllowed: boolean; + nodeId: string; + nodeName: string; + dataSourceId: string; +}; +type SelectOption = { + label: string; + value: string; +}; interface GlobalConfig { maxParallelism: number; + dataSourceConfig: API.GraphDetailVODataSourceConfig[]; } export class advancedConfigService extends Model { loading = false; config: GlobalConfig = { maxParallelism: 1, + dataSourceConfig: [], + }; + + nodeDataSourceOptionsMap: Record = {}; + + queryAllNodeDataSources = async (projectId: string) => { + this.loading = true; + const { status, data } = await projectGraphDomainDataSourceList({ + projectId, + }); + this.loading = false; + if (status && status.code === 0 && data) { + const obj: Record = {}; + data.forEach((item) => { + const dataSources = item.dataSources?.map((source) => ({ + label: source.dataSourceName, + value: source.dataSourceId, + })); + if (item.nodeId) { + obj[item.nodeId] = dataSources || []; + } + }); + this.nodeDataSourceOptionsMap = obj; + } }; getSetting = async (graphId: string, projectId: string) => { if (!graphId || !projectId) return; this.loading = true; - const { status, data } = await API.GraphController.getGraphDetail({ + const { status, data } = await getGraphDetail({ graphId, projectId, }); this.loading = false; if (status && status.code === 0) { this.config.maxParallelism = data?.maxParallelism || 1; + this.config.dataSourceConfig = data?.dataSourceConfig || []; } }; setting = async (params: API.FullUpdateGraphRequest) => { - const { status } = await API.GraphController.fullUpdateGraph(params); + const { status } = await fullUpdateGraph(params); if (status && status.code === 0) { message.success('配置成功'); } else { diff --git a/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-view.tsx b/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-view.tsx index d703aaf..d6889dc 100644 --- a/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-view.tsx +++ b/apps/platform/src/modules/advanced-config/advanced-config-drawer/advanced-config-view.tsx @@ -1,18 +1,24 @@ -import { getModel, useModel } from '@/util/valtio-helper'; -import { DefaultModalManager } from '@/modules/dag-modal-manager'; -import { advancedConfigService } from './advanced-config-service'; -import { Button, Drawer, Form, InputNumber, Space, Spin } from 'antd'; +import { CloseOutlined } from '@ant-design/icons'; +import { Button, Drawer, Form, InputNumber, Select, Space, Spin } from 'antd'; +import { parse } from 'query-string'; import { useCallback, useEffect } from 'react'; +import { useLocation } from 'umi'; + +import { Platform } from '@/components/platform-wrapper'; +import { DefaultModalManager } from '@/modules/dag-modal-manager'; import dagLayoutStyle from '@/modules/layout/dag-layout/index.less'; +import { LoginService } from '@/modules/login/login.service'; +import { getModel, useModel } from '@/util/valtio-helper'; +import { advancedConfigService } from './advanced-config-service'; import styles from './index.less'; -import { CloseOutlined } from '@ant-design/icons'; -import { useLocation } from 'umi'; -import { parse } from 'query-string'; export const AdvancedConfig = () => { const modalManager = useModel(DefaultModalManager); const service = useModel(advancedConfigService); + const loginService = useModel(LoginService); + + const { nodeDataSourceOptionsMap, config } = service; const { search } = useLocation(); const { dagId, projectId } = parse(search); @@ -30,8 +36,13 @@ export const AdvancedConfig = () => { await service.getSetting(dagId as string, projectId as string); }, [dagId]); + const getAllNodeDataSources = useCallback(async () => { + await service.queryAllNodeDataSources(projectId as string); + }, [projectId]); + useEffect(() => { if (visible) { + getAllNodeDataSources(); getConfig(); } }, [visible]); @@ -39,10 +50,42 @@ export const AdvancedConfig = () => { useEffect(() => { if (visible) { form.setFieldsValue({ - maxParallelism: service.config?.maxParallelism, + maxParallelism: config?.maxParallelism, + dataSourceConfig: config?.dataSourceConfig, }); } - }, [service.config.maxParallelism, visible]); + }, [config.maxParallelism, visible, config?.dataSourceConfig]); + + const getDataSourceOptions = (key: number) => { + const currentNodeConfig = config.dataSourceConfig[key]; + return nodeDataSourceOptionsMap[currentNodeConfig?.nodeId]; + }; + + const handleDisabled = (key: number) => { + /** + * editAllowed 为 true 才可以编辑, 并且要满足下面的条件 + * 1. center 模式下 + * - center 账号可以全部编辑 + * - - edge 账号只能编辑自己节点以及内置节点 + * 2. p2p 模式下只能编辑当前节点 + */ + const editAllowed = service.config?.dataSourceConfig[key]?.editEnable; + const currentNodeId = service.config?.dataSourceConfig[key]?.nodeId; + const ownerId = loginService?.userInfo?.ownerId; + + if (loginService?.userInfo?.platformType === Platform.CENTER) { + if (loginService?.userInfo?.ownerType === 'EDGE') { + // editAllowed 服务端已经做了关于当前登陆用户是不是当前节点的判断了,所有直接返回 editAllowed + return editAllowed; + } else if (loginService?.userInfo?.ownerType === 'CENTER') { + return editAllowed; + } + } else if (loginService?.userInfo?.platformType === Platform.AUTONOMY) { + // p2p 模式下只能编辑当前节点 + return editAllowed && ownerId === currentNodeId; + } + return false; + }; const handleOk = () => { form.validateFields().then(async (value) => { @@ -50,6 +93,12 @@ export const AdvancedConfig = () => { maxParallelism: value.maxParallelism, graphId: dagId as string, projectId: projectId as string, + dataSourceConfig: value.dataSourceConfig + .filter((item: { editEnable: boolean }) => item.editEnable === true) + .map((item: { nodeId: string; dataSourceId: string }) => ({ + nodeId: item.nodeId, + dataSourceId: item.dataSourceId, + })), }; await service.setting(params); onClose(); @@ -80,7 +129,12 @@ export const AdvancedConfig = () => { } > -
+ 任务并发数} @@ -89,7 +143,6 @@ export const AdvancedConfig = () => { wrapperCol={{ offset: 4, span: 12 }} > { precision={0} /> + + {(fields) => { + return ( + <> + {fields.map((field) => ( +
+ {`节点${ + config?.dataSourceConfig[field.key]?.nodeName + }默认存储数据源`} + } + name={[field.name, 'dataSourceId']} + rules={[{ required: true, message: '请选择' }]} + labelCol={{ span: 24 }} + wrapperCol={{ span: 24 }} + extra="保存后将不可修改" + > + + + + {() => { + const quantilesString = form?.getFieldValue(quantilesStringFieldName); + + if (quantilesString) { + const rawList = quantilesString.split(','); + rawList.push(''); + + const quantilesList = rawList.map((item: string, index: number) => { + if (index === 0) { + return `(-∞, ${item}]`; + } + + if (!item) { + return `(${rawList[index - 1]}, +∞)`; + } + + return `(${rawList[index - 1]}, ${item}]`; + }); + + const quantiles = + quantilesString.split(',')?.map((quantile: string) => { + return Number(quantile); + }) || []; + + form?.setFieldValue(quantilesFieldName, quantiles); + + const prevReplacements = form?.getFieldValue(replacementsFieldName) || []; + const replacements = + quantilesList?.map((_: string, index: number) => { + if (prevReplacements[index] !== undefined) { + return prevReplacements[index]; + } else { + return true; + } + }) || []; + + form?.setFieldValue(replacementsFieldName, replacements); + + const prevWeights = form?.getFieldValue(weightsFieldName) || []; + const weights = + quantilesList?.map((_: string, index: number) => { + if (prevWeights[index] !== undefined) { + return prevWeights[index]; + } else { + return null; + } + }) || []; + + form?.setFieldValue(weightsFieldName, weights); + + return ( + + {quantilesList.map((item: string, index: number) => { + return ( +
+ {item} + + > = (config) => { component: item, form, type: node.type, + name: node.name, }); }; diff --git a/apps/platform/src/modules/component-config/config-item-render/default-render-template.tsx b/apps/platform/src/modules/component-config/config-item-render/default-render-template.tsx index d4fa8bd..9a4fdfb 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-render-template.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-render-template.tsx @@ -1,4 +1,4 @@ -import { Switch, InputNumber, Input, Select, Form } from 'antd'; +import { InputNumber, Input, Select, Form } from 'antd'; import { useState, useEffect } from 'react'; import type { AtomicConfigNode } from '../component-config-protocol'; @@ -8,10 +8,43 @@ import { typeListMap, getValueBound } from '../utils'; import type { RenderProp } from './config-render-protocol'; import { getComponentByRenderStrategy } from './helper'; +const BooleanSelect = ({ + // 强制保持受控逻辑 + value = false, + onChange, +}: { + value: boolean; + onChange: (value: boolean) => void; +}) => ( + > = (config) => { /> ); + + return getComponentByRenderStrategy({ + prefixes, + componentConfig, + component: item, + form, + type: node.type, + name: node.name, + }); }; export const DefaultSelect: React.FC> = (config) => { - const { node, onChange, value, defaultVal, translation } = config; + const { + node, + onChange, + value, + defaultVal, + translation, + componentConfig, + form, + attrConfig, + } = config; const { allowed_values: allowedValues, type, list_max_length_inclusive, list_min_length_inclusive, + prefixes, } = node; let isMultiple = false; + + const isNoWrap = !!attrConfig?.style?.noWrap; if (['AT_STRINGS', 'AT_INTS', 'AT_FLOATS', 'AT_BOOLS'].includes(type)) { isMultiple = true; @@ -196,13 +275,14 @@ export const DefaultSelect: React.FC> = (config) => { setSelectOptions(options || []); }, [allowedValues]); - return ( + const item = ( {translation[node.name] || node.name}
} + labelCol={isNoWrap ? { span: 12 } : undefined} name={ node.prefixes && node.prefixes.length > 0 ? node.prefixes.join('/') + '/' + node.name @@ -256,13 +336,22 @@ export const DefaultSelect: React.FC> = (config) => { />
); + + return getComponentByRenderStrategy({ + prefixes, + componentConfig, + component: item, + form, + type: node.type, + name: node.name, + }); }; export const DefaultUnion: React.FC> = (config) => { - const { node, translation, form, componentConfig } = config; + const { node, translation, form, componentConfig, attrConfig } = config; const name = translation[node.name] || node.name; const { prefixes, children, selectedName } = node; - + const isNoWrap = !!attrConfig?.style?.noWrap; const options = children .filter((child) => { return child.prefixes[child.prefixes.length - 1] === node.name; @@ -279,6 +368,7 @@ export const DefaultUnion: React.FC> = (config) => { ? node.prefixes.join('/') + '/' + node.name : node.name } + labelCol={isNoWrap ? { span: 12 } : undefined} rules={[ { required: node.isRequired, @@ -298,15 +388,45 @@ export const DefaultUnion: React.FC> = (config) => { component: item, form, type: node.type, + name: node.name, }); }; export const DefaultStruct: React.FC> = (config) => { + const { node, form, componentConfig, translation } = config; + const { prefixes, name } = node; + + const item = ( +
{translation[name] || name}
+ ); + + return getComponentByRenderStrategy({ + prefixes, + componentConfig, + component: item, + form, + type: node.type, + name, + }); +}; + +export const DefaultEmpty: React.FC> = (config) => { const { node, form, componentConfig } = config; - const { prefixes } = node; + const { prefixes, name } = node; - // 在目前的策略中,Struct 节点默认不渲染。 - const item = <>; + // 默认不渲染。 + const item = ( + 0 + ? node.prefixes.join('/') + '/' + node.name + : node.name + } + > + <> + + ); return getComponentByRenderStrategy({ prefixes, @@ -314,5 +434,6 @@ export const DefaultStruct: React.FC> = (config) => { component: item, form, type: node.type, + name, }); }; diff --git a/apps/platform/src/modules/component-config/config-item-render/helper.tsx b/apps/platform/src/modules/component-config/config-item-render/helper.tsx index afef545..47f2025 100644 --- a/apps/platform/src/modules/component-config/config-item-render/helper.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/helper.tsx @@ -1,17 +1,18 @@ import { Form, type FormInstance } from 'antd'; -import type { ConfigItem } from '../component-config-protocol'; +import type { ConfigItem, ConfigType } from '../component-config-protocol'; interface IParams { prefixes?: string[]; componentConfig: ConfigItem[]; component: React.ReactNode; form?: FormInstance; - type: string; + type: ConfigType; + name?: string; } export const getComponentByRenderStrategy = (params: IParams) => { - const { prefixes, componentConfig, component, form, type } = params; + const { prefixes, componentConfig, component, form, type, name } = params; const parentName = prefixes?.[prefixes.length - 1]; const parentType = componentConfig.find( @@ -23,6 +24,12 @@ export const getComponentByRenderStrategy = (params: IParams) => { (config: ConfigItem) => config.name === grandParentName, )?.type; + const dependency = Form.useWatch(prefixes?.join('/'), form); + + const _prefixes = [...(prefixes || [])]; + _prefixes?.pop(); + const grandDependency = Form.useWatch(_prefixes?.join('/'), form); + /** 节点没有 prefixes,直接渲染 */ if (!prefixes?.length) { return component; @@ -33,21 +40,24 @@ export const getComponentByRenderStrategy = (params: IParams) => { return <>; } - /** 该节点的父节点是 union,直接隐藏 */ - if (parentType === 'AT_UNION_GROUP') { + /** + * 该节点的父节点是 union,自己没有类型/struct,不渲染 + * 否则,那就根据那个父节点的 value 渲染 + */ + + if (parentType === 'AT_UNION_GROUP' && (!type || type === 'AT_STRUCT_GROUP')) { return <>; + } else if (parentType === 'AT_UNION_GROUP') { + return dependency === name ? component : <>; } /** * 节点有 prefixes,就要根据 prefixes 的内容,按需联动 = 隐藏/展示 * 假如说该节点的父节点的父节点是 union,那就根据那个父节点的 value 渲染 */ - if (grandParentType === 'AT_UNION_GROUP') { - const _prefixes = [...(prefixes || [])]; - _prefixes?.pop(); - const dependency = Form.useWatch(_prefixes?.join('/'), form); - if (dependency === prefixes?.[prefixes?.length - 1]) { + if (grandParentType === 'AT_UNION_GROUP') { + if (grandDependency === prefixes?.[prefixes?.length - 1]) { return component; } diff --git a/apps/platform/src/modules/component-config/index.less b/apps/platform/src/modules/component-config/index.less index 35c848c..79f889a 100644 --- a/apps/platform/src/modules/component-config/index.less +++ b/apps/platform/src/modules/component-config/index.less @@ -70,6 +70,15 @@ font-weight: 400; } +.configItemStructLabel { + padding: 0 0 8px; + border-bottom: 1px #e1e1e1 solid; + margin: 20px 0 0; + color: rgb(0 0 0 / 60%); + font-size: 12px; + font-weight: 600; +} + .defaultRender { :global { .ant-input-number { diff --git a/apps/platform/src/modules/component-tree/component-protocol.ts b/apps/platform/src/modules/component-tree/component-protocol.ts index c29fb3e..a1281d0 100644 --- a/apps/platform/src/modules/component-tree/component-protocol.ts +++ b/apps/platform/src/modules/component-tree/component-protocol.ts @@ -61,7 +61,10 @@ export type AtomicParameterList = { export type AtomicParameter = AtomicParameterSingle & AtomicParameterList; // The value of an attribute -export type Attribute = OneOf & { is_na?: boolean }; +export type Attribute = OneOf & { + is_na?: boolean; + custom_protobuf_cls?: string; +}; export type AtomicParameterDef = { // If True, when Atomic Attr is not provided or is_na, default_value would @@ -166,4 +169,9 @@ export type ComponentTreeItem = { category: string; }; -export type ComputeMode = 'MPC' | 'TEE'; +export enum ComputeModeEnum { + MPC = 'MPC', + TEE = 'TEE', +} + +export type ComputeMode = keyof typeof ComputeModeEnum; diff --git a/apps/platform/src/modules/component-tree/component-tree-service.ts b/apps/platform/src/modules/component-tree/component-tree-service.ts index 88009e0..e624d03 100644 --- a/apps/platform/src/modules/component-tree/component-tree-service.ts +++ b/apps/platform/src/modules/component-tree/component-tree-service.ts @@ -129,8 +129,9 @@ export class DefaultComponentTreeService extends Model { convertToTree(mode: ComputeMode): ComponentTreeItem[] { const mergedDomainMap: { [key: string]: string } = { - feature: '特征预处理', - preprocessing: '特征预处理', + feature: '特征处理', + preprocessing: '特征处理', + postprocessing: '特征处理', read_data: '数据准备', data_prep: '数据准备', }; @@ -179,7 +180,7 @@ export class DefaultComponentTreeService extends Model { const domainOrder = [ '数据准备', 'data_filter', - '特征预处理', + '特征处理', 'stats', 'ml.train', 'ml.predict', @@ -188,7 +189,7 @@ export class DefaultComponentTreeService extends Model { const childrenOrder: { [key: string]: string[] } = { 数据准备: ['datatable', 'psi', 'train_test_split'], - 特征预处理: [ + 特征处理: [ 'binary_op', 'case_when', 'feature_calculate', diff --git a/apps/platform/src/modules/create-project/create-project.view.tsx b/apps/platform/src/modules/create-project/create-project.view.tsx index ff79de6..ace9f47 100644 --- a/apps/platform/src/modules/create-project/create-project.view.tsx +++ b/apps/platform/src/modules/create-project/create-project.view.tsx @@ -80,6 +80,10 @@ export const CreateProjectModal = ({ visible, data, close }: ICreateProjectModal }); }, []); + const handleComputeModeChange = () => { + form.setFieldsValue({ templateId: undefined }); + }; + return ( - + = ({ items }) => { const viewInstance = useModel(DagLogService); + const { search } = window.location; + const { dagId } = parse(search); const { unfold, logMainHeight, setLogMainMax, setLogMainMin } = viewInstance; - React.useEffect(() => { + const [activeKey, setActiveKey] = React.useState(items[0]?.key); + + useEffect(() => { + setActiveKey(items[0]?.key); return () => { viewInstance.setLogMainMin(); }; - }, []); - - const [activeKey, setActiveKey] = React.useState(items[0].key); + }, [dagId]); return (
diff --git a/apps/platform/src/modules/dag-log/sls-service.ts b/apps/platform/src/modules/dag-log/sls-service.ts index b5984b6..6a4c96b 100644 --- a/apps/platform/src/modules/dag-log/sls-service.ts +++ b/apps/platform/src/modules/dag-log/sls-service.ts @@ -1,6 +1,7 @@ import type { GraphNode } from '@secretflow/dag'; import { parse } from 'query-string'; +import { PadMode } from '@/components/platform-wrapper'; import API from '@/services/secretpad'; import { Model } from '@/util/valtio-helper'; @@ -58,6 +59,9 @@ export class SlsService extends Model { from: 'record' | 'pipeline', currentNodePartiesId: string | undefined, ) => { + const { search } = window.location; + const { mode } = parse(search); + if (mode === PadMode.TEE) return; if (!this.slsLogIsConfig) return; if (!currentNodePartiesId) return; const { label, id } = data; diff --git a/apps/platform/src/modules/dag-model-submission/useFormValidateOnly.tsx b/apps/platform/src/modules/dag-model-submission/hooks.ts similarity index 91% rename from apps/platform/src/modules/dag-model-submission/useFormValidateOnly.tsx rename to apps/platform/src/modules/dag-model-submission/hooks.ts index f43f0b3..de27372 100644 --- a/apps/platform/src/modules/dag-model-submission/useFormValidateOnly.tsx +++ b/apps/platform/src/modules/dag-model-submission/hooks.ts @@ -1,5 +1,5 @@ import { Form } from 'antd'; -import { FormInstance } from 'antd/lib'; +import type { FormInstance } from 'antd/lib'; import { useEffect, useState } from 'react'; export const useFormValidateOnly = (form: FormInstance) => { diff --git a/apps/platform/src/modules/dag-model-submission/node-address/index.tsx b/apps/platform/src/modules/dag-model-submission/node-address/index.tsx index 37b02e0..d333395 100644 --- a/apps/platform/src/modules/dag-model-submission/node-address/index.tsx +++ b/apps/platform/src/modules/dag-model-submission/node-address/index.tsx @@ -1,41 +1,116 @@ -import { Col, Empty, Input, Row } from 'antd'; +import { Col, Empty, Form, Input, Row, Select, Spin } from 'antd'; +import { useEffect, useMemo } from 'react'; + +import { useModel } from '@/util/valtio-helper'; + +import { SubmissionDrawerService } from '../submission-service'; + import styles from './index.less'; -type IProps = { - addressList: API.ModelPartyPathResponse[]; -}; +export const NodeAddressList = () => { + const submissionDrawerService = useModel(SubmissionDrawerService); + const form = Form.useFormInstance(); + const { addressNodeList: addressList } = submissionDrawerService; + + useEffect(() => { + const newAddress = addressList.map((item) => ({ + nodeName: item.nodeName, + nodeId: item.nodeId, + })); + form.setFieldValue('storageAddress', newAddress); + }, [addressList, form]); + + const dataSourceOptions = useMemo(() => { + return addressList.map((item) => { + return item?.dataSources?.map( + (source: API.ProjectGraphDomainDataSourceVODataSource) => { + return { + label: source.dataSourceName, + value: source.dataSourceId, + }; + }, + ); + }); + }, [addressList]); -export const NodeAddress = (props: IProps) => { - const { addressList } = props; + // 数据源路径 先隐藏 + // const handleChange = (value: string, key: number) => { + // const path = addressList[key]?.dataSources?.find( + // (item) => item.dataSourceId === value, + // )?.path; + // form.setFields([ + // { + // name: ['storageAddress', key, 'dataSourcePath'], + // value: path, + // }, + // ]); + // }; return ( -
- {addressList.length === 0 ? ( - - ) : ( -
- {addressList.map((item, index) => { - return ( -
-
{'存储节点' + (index + 1)}
-
- - - - - - - - -
-
- ); - })} -
- )} -
+ +
+ {addressList.length === 0 ? ( + + ) : ( + + {(fields) => { + return ( + <> + {fields.map((field, index) => { + return ( +
+
{'存储节点' + (index + 1)}
+ + + + + + + + + + + + */} +
+ ); + })} + + ); + }} +
+ )} +
+
); }; diff --git a/apps/platform/src/modules/dag-model-submission/submission-drawer.tsx b/apps/platform/src/modules/dag-model-submission/submission-drawer.tsx index d731eab..26242de 100644 --- a/apps/platform/src/modules/dag-model-submission/submission-drawer.tsx +++ b/apps/platform/src/modules/dag-model-submission/submission-drawer.tsx @@ -5,7 +5,6 @@ import { Form, Input, Empty, - Spin, message, Tooltip, Alert, @@ -22,10 +21,10 @@ import submissionLayoutStyle from '@/modules/layout/model-submission-layout/inde import { getModel, useModel } from '@/util/valtio-helper'; import styles from './index.less'; -import { NodeAddress } from './node-address'; +import { NodeAddressList } from './node-address'; import { PreviewSubmitNode } from './preview-submit-node'; import { SubmissionDrawerService } from './submission-service'; -import { useFormValidateOnly } from './useFormValidateOnly'; +import { useFormValidateOnly } from './hooks'; const WIDTH = 560; @@ -91,8 +90,8 @@ export const SubmissionDrawer = () => { }; const getAddress = useCallback( - async (modelId: string) => { - await getModelNodesAddress(modelId); + async (id: string) => { + await getModelNodesAddress(id); }, [modelId], ); @@ -138,9 +137,10 @@ export const SubmissionDrawer = () => { graphNodeOutPutId: modelId, modelName: value.name, modelDesc: value.desc, - modelPartyConfig: addressNodeList.map((item) => ({ + modelPartyConfig: value.storageAddress.map((item) => ({ modelParty: item.nodeId, - modelDataSource: item.dataSourcePath, + modelDataSource: item.dataSource, + modelDataPath: item.dataSourcePath, modelDataName: item.nodeName, })), modelComponent: submitNodes.map((item) => ({ @@ -294,9 +294,7 @@ export const SubmissionDrawer = () => { } required > - - - + ) => { const { rows } = meta; const resultManagerService = getModel(ResultManagerService); const { pathname } = useLocation(); + const [downloadBtnDisabled, setDownloadBtnDisabled] = useState(false); + const [downloadPath, setDownloadPath] = useState(''); + + useEffect(() => { + rows.forEach((r) => { + const { datasourceType, path } = r; + + if (datasourceType === DataSourceType.OSS) { + setDownloadBtnDisabled(true); + setDownloadPath(path); + } + }); + }, [rows]); return (
@@ -79,15 +94,24 @@ export const ResultModelComponent = (props: ResultComponentProps<'model'>) => { )} {/* p2p 模式下不用申请,直接下载 */} {hasAccess({ type: [Platform.AUTONOMY] }) && ( - + + )}
); diff --git a/apps/platform/src/modules/dag-result/result-report.tsx b/apps/platform/src/modules/dag-result/result-report.tsx index 832d7cd..ed52c06 100644 --- a/apps/platform/src/modules/dag-result/result-report.tsx +++ b/apps/platform/src/modules/dag-result/result-report.tsx @@ -52,6 +52,7 @@ export const getVisComponents = ( data: Tab, id: string, showFullscreen?: boolean, + tabs?: Tab[], ) => { const ComponentVisMap: Record = { // pva @@ -72,7 +73,15 @@ export const getVisComponents = ( return ComponentVisMap[key]; } - return ; + return ( + + ); }; export const getVisTabsContent = (codeName: string, tabs: Tab[], id: string) => { @@ -82,20 +91,31 @@ export const getVisTabsContent = (codeName: string, tabs: Tab[], id: string) => // 回归模型评估 'ml.eval/regression_eval': , }; + if (ComponentVisMap[codeName]) { return ComponentVisMap[codeName]; } + return ( - { - return { - label: getTabsName(item.name, index), - key: `${index}`, - children:
{getVisComponents(codeName, item, id)}
, - }; - })} - /> +
+ + document.getElementById('reportTabsContent') as HTMLDivElement + } + className={styles.tabsTable} + defaultActiveKey="0" + items={(tabs || []).map((item, index) => { + return { + label: getTabsName(item.name, index), + key: `${index}`, + children: ( +
+ {getVisComponents(codeName, item, id, undefined, tabs)} +
+ ), + }; + })} + /> +
); }; diff --git a/apps/platform/src/modules/dag-result/result-rules.tsx b/apps/platform/src/modules/dag-result/result-rules.tsx index 2d1c7e7..74a2590 100644 --- a/apps/platform/src/modules/dag-result/result-rules.tsx +++ b/apps/platform/src/modules/dag-result/result-rules.tsx @@ -1,5 +1,6 @@ -import { Button, Tag, Typography } from 'antd'; +import { Button, Tag, Tooltip, Typography } from 'antd'; import { parse } from 'query-string'; +import { useEffect, useState } from 'react'; import { useLocation } from 'umi'; import { Platform, hasAccess } from '@/components/platform-wrapper'; @@ -7,6 +8,7 @@ import { Download } from '@/modules/dag-result/apply-download'; import { openNewTab } from '@/util/path'; import { getModel } from '@/util/valtio-helper'; +import { DataSourceType } from '../data-source-list/type'; import { ResultManagerService } from '../result-manager/result-manager.service'; import style from './index.less'; @@ -23,6 +25,20 @@ export const ResultRuleComponent = (props: ResultComponentProps<'rule'>) => { const resultManagerService = getModel(ResultManagerService); const { pathname } = useLocation(); + const [downloadBtnDisabled, setDownloadBtnDisabled] = useState(false); + const [downloadPath, setDownloadPath] = useState(''); + + useEffect(() => { + rows.forEach((r) => { + const { datasourceType, path } = r; + + if (datasourceType === DataSourceType.OSS) { + setDownloadBtnDisabled(true); + setDownloadPath(path); + } + }); + }, [rows]); + return (
@@ -81,15 +97,24 @@ export const ResultRuleComponent = (props: ResultComponentProps<'rule'>) => { )} {/* p2p 模式下不用申请,直接下载 */} {hasAccess({ type: [Platform.AUTONOMY] }) && ( - + + )}
); diff --git a/apps/platform/src/modules/dag-result/result-table.tsx b/apps/platform/src/modules/dag-result/result-table.tsx index ed3ebf6..f17e637 100644 --- a/apps/platform/src/modules/dag-result/result-table.tsx +++ b/apps/platform/src/modules/dag-result/result-table.tsx @@ -6,7 +6,7 @@ import { } from '@ant-design/icons'; import { useFullscreen } from 'ahooks'; import type { InputRef } from 'antd'; -import { Button, Input, Space, Table, Tag } from 'antd'; +import { Button, Input, Space, Table, Tag, Tooltip } from 'antd'; import type { ColumnType } from 'antd/es/table'; import type { FilterConfirmProps } from 'antd/es/table/interface'; import classNames from 'classnames'; @@ -14,24 +14,28 @@ import classnames from 'classnames'; import { groupBy, map as lodashMap } from 'lodash'; import { parse } from 'query-string'; import type { Key } from 'react'; -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { CSVLink } from 'react-csv'; +import { useLocation } from 'umi'; import { Platform, hasAccess } from '@/components/platform-wrapper'; import { Download } from '@/modules/dag-result/apply-download'; +import { openNewTab } from '@/util/path'; import { getModel } from '@/util/valtio-helper'; +import { DataSourceType } from '../data-source-list/type'; import { ResultManagerService } from '../result-manager/result-manager.service'; import styles from './index.less'; import type { DataType, ResultComponentProps } from './types'; import { formatTimestamp } from './utils'; -import { useLocation } from 'umi'; -import { openNewTab } from '@/util/path'; export const ResultTableComponent = (props: ResultComponentProps<'table'>) => { const [searchText, setSearchText] = useState(''); const [searchedColumn, setSearchedColumn] = useState(''); + + const [downloadBtnDisabled, setDownloadBtnDisabled] = useState(false); + const [downloadPath, setDownloadPath] = useState(''); const searchInput = useRef(null); const { pathname } = useLocation(); const { mode, projectId } = parse(window.location.search); @@ -127,11 +131,24 @@ export const ResultTableComponent = (props: ResultComponentProps<'table'>) => { const metaInfo: { path: string; nodeId: string; tableId: string; type: string }[] = []; + useEffect(() => { + rows.forEach((r) => { + const { datasourceType, path } = r; + + if (datasourceType === DataSourceType.OSS) { + setDownloadBtnDisabled(true); + setDownloadPath(path); + } + }); + }, [rows]); + rows.forEach((r) => { const { path, nodeId, fields, fieldTypes, tableId, type } = r; + if (fields === '') { return; } + metaInfo.push({ path, nodeId, tableId, type }); const fieldList = fields.split(','); const fieldTypeList = fieldTypes.split(','); @@ -191,6 +208,7 @@ export const ResultTableComponent = (props: ResultComponentProps<'table'>) => { return item; }); }; + return (
@@ -243,17 +261,26 @@ export const ResultTableComponent = (props: ResultComponentProps<'table'>) => { {/* p2p 模式下不用申请,直接下载 */} {hasAccess({ type: [Platform.AUTONOMY] }) && props.codeName !== 'read_data/datatable' && ( - + + )}
diff --git a/apps/platform/src/modules/dag-result/types.ts b/apps/platform/src/modules/dag-result/types.ts index 848f6ea..1885c4a 100644 --- a/apps/platform/src/modules/dag-result/types.ts +++ b/apps/platform/src/modules/dag-result/types.ts @@ -1,3 +1,5 @@ +import type { DataSourceType } from '../data-source-list/type'; + import type { Tab } from './result-report-types'; export type ResultComponentProps = { @@ -10,6 +12,7 @@ export type ResultRuleRow = { nodeId: string; tableId: string; type: string; + datasourceType: DataSourceType; }; export type ResultModelRow = ResultRuleRow; @@ -20,6 +23,7 @@ type ResultTableRow = { tableId: string; dsId: string; type: string; + datasourceType: DataSourceType; } & ResultRuleRow; export type ResultTableData = { diff --git a/apps/platform/src/modules/dag-result/vis/output-table/description-table.tsx b/apps/platform/src/modules/dag-result/vis/output-table/description-table.tsx index 14aead1..f0a034f 100644 --- a/apps/platform/src/modules/dag-result/vis/output-table/description-table.tsx +++ b/apps/platform/src/modules/dag-result/vis/output-table/description-table.tsx @@ -3,10 +3,50 @@ import { Button, Table } from 'antd'; import { useRef } from 'react'; import { CSVLink } from 'react-csv'; +import type { Tab } from '../../result-report-types'; +import { modifyDataStructure } from '../utils'; + import styles from './index.less'; +const SAMPLE = 'data_filter/sample'; + +const getFullCsvDataForSample = (allTableInfo?: Tab[]) => { + const allTableInfoList = + allTableInfo?.map((info) => ({ + name: info.name, + ...modifyDataStructure(info), + })) || []; + + const csvFullData = [ + ['sample_name', 'num_before_sample', 'num_after_sample', 'sample_rate'], + ]; + + allTableInfoList.forEach((record) => { + const num_before_sample = record?.records?.find( + (item) => item?.name === 'num_before_sample', + )?.value; + + const num_after_sample = record?.records?.find( + (item) => item.name === 'num_after_sample', + )?.value; + + const sample_rate = record?.records?.find( + (item) => item.name === 'sample_rate', + )?.value; + + csvFullData.push([record.name, num_before_sample, num_after_sample, sample_rate]); + }); + + return csvFullData; +}; + export const DescriptionTable = (props: IProps) => { - const { data = [], tableName = '' } = props; + const { data = [], tableName = '', allTableInfo, componentName } = props; + + let fullCavData; + if (componentName && componentName === SAMPLE) { + fullCavData = getFullCsvDataForSample(allTableInfo); + } const csvRef = useRef<{ link: HTMLLinkElement; @@ -45,7 +85,7 @@ export const DescriptionTable = (props: IProps) => { 导出数据
- + { export interface IProps { data?: DataProps[]; tableName?: string; + allTableInfo?: Tab[]; + componentName?: string; } export interface DataProps { diff --git a/apps/platform/src/modules/dag-result/vis/output-table/index.tsx b/apps/platform/src/modules/dag-result/vis/output-table/index.tsx index bd3257a..aa89543 100644 --- a/apps/platform/src/modules/dag-result/vis/output-table/index.tsx +++ b/apps/platform/src/modules/dag-result/vis/output-table/index.tsx @@ -9,58 +9,74 @@ import classNames from 'classnames'; import React, { useEffect, useRef } from 'react'; import { CSVLink } from 'react-csv'; +import type { Tab } from '../../result-report-types'; import type { DescriptionList, ResultOriginData } from '../typing'; import { lexicographicalOrder, modifyDataStructure } from '../utils'; import { DescriptionTable } from './description-table'; import './index.less'; +const STATS_PSI = 'stats/stats_psi'; + +const getFullCsvDataForStatsPSI = (allTableInfo: Tab[]) => { + const tableInfoMap = {}; + const _allTableInfo = [...allTableInfo]; + _allTableInfo.shift(); + _allTableInfo.forEach((info) => { + tableInfoMap[info.name] = modifyDataStructure(info); + }); + + const csvFullData = [ + [ + 'name (feature)', + 'feature', + 'PSI (feature)', + 'name (Label)', + 'Label', + 'PSI (Label)', + 'Base Ratio', + 'Test Ratio', + ], + ]; + + const summaryInfo = modifyDataStructure(allTableInfo[0]); + + (summaryInfo?.records as string[][]).forEach((record) => { + csvFullData.push(record); + + const key = record[1] as string; + const processedRecord = tableInfoMap[key].records.map((item) => { + (item as string[]).unshift(...['', '', '']); + return item; + }) as string[][]; + csvFullData.push(...processedRecord); + }); + + return csvFullData; +}; + export const OutputTable: React.FC = (props) => { const { name: tableName, tableInfo: requestTableInfo, showFullscreen = false, + allTableInfo, + componentName, } = props; const fullScreenRef = React.useRef(null); const [isFullscreen, { enterFullscreen, exitFullscreen }] = useFullscreen(fullScreenRef); + const tableInfo = modifyDataStructure(requestTableInfo); - const csvRef = useRef<{ - link: HTMLLinkElement; - }>(null); - const downloadData = () => { - if (csvRef && csvRef.current) { - csvRef.current.link.click(); - } + // 去除导出数据时手动加入的key + const convertDownDataSource = (dataList: { key?: number }[] = []) => { + return dataList.map((item) => { + delete item.key; + return item; + }); }; - const [isEllipsis, setEllipsis] = React.useState(false); - - useEffect(() => { - if (tableInfo.schema.length > 3) { - setEllipsis(true); - } else { - setEllipsis(false); - } - }, [tableInfo.schema.length]); - - if (!tableInfo) { - return ( -
- -
- ); - } - - if (tableInfo.type === 'descriptions') { - return ( - - ); - } const columnsList: Columns[] = []; tableInfo.schema.forEach(({ name, type }) => { if (name !== 'name') { @@ -108,6 +124,7 @@ export const OutputTable: React.FC = (props) => { }); } }); + const dataSource = ((tableInfo.records as (string | number | boolean)[][]) || []).map( (record, index) => { const res: { key: number; [propName: string]: number | string | boolean } = { @@ -124,14 +141,53 @@ export const OutputTable: React.FC = (props) => { return res; }, ); - // 去除导出数据时手动加入的key - const convertDownDataSource = (dataList: { key?: number }[] = []) => { - return dataList.map((item) => { - delete item.key; - return item; - }); + + let csvData; + if (componentName && componentName === STATS_PSI && allTableInfo) { + csvData = getFullCsvDataForStatsPSI(allTableInfo); + } else { + csvData = convertDownDataSource(dataSource); + } + + const csvRef = useRef<{ + link: HTMLLinkElement; + }>(null); + + const downloadData = () => { + if (csvRef && csvRef.current) { + csvRef.current.link.click(); + } }; + const [isEllipsis, setEllipsis] = React.useState(false); + + useEffect(() => { + if (tableInfo.schema.length > 3) { + setEllipsis(true); + } else { + setEllipsis(false); + } + }, [tableInfo.schema.length]); + + if (!tableInfo) { + return ( +
+ +
+ ); + } + + if (tableInfo.type === 'descriptions') { + return ( + + ); + } + return (
@@ -149,11 +205,7 @@ export const OutputTable: React.FC = (props) => { )}
- +
{ - const { graphNodeId, status, codeName, nodeDef = {}, ...options } = n; + const { + graphNodeId, + status, + codeName, + nodeDef = {}, + parties = [], + ...options + } = n; // 强行断言:后端没有定义 UNFINISHED 状态 let graphNodeStatus = nodeStatus[status || 'STAGING'] as unknown as NodeStatus; if (graphNodeStatus === NodeStatus.default) { @@ -57,14 +63,16 @@ export class GraphSubmitRequestService extends GraphRequestService { * 初始化可以点击提交的算子 * 1. 成功的模型训练并且有输入边 * 2. 成功的模型预测并且有输入边并且上游还有线性模型参数修改算子 + * 3. 成功的模型预测并且有输入边并且下游还有后处理算子 * 注意:目前模型提交逻辑,能提交 模型训练算子和 模型预测算子 * 提交模型训练算子,自动勾选上游预处理组件 - * 提交模型预测算子,自动勾选上游的线性模型参数修改算子,以及模型训练算子上游的预处理组件算子 - * 目前暂无 模型预测后面连接的后处理算子 + * 提交模型预测算子,自动勾选上游的线性模型参数修改算子,以及模型训练算子上游的预处理组件算子, 和模型预测算子下游的后处理算子 + * 目前(评分卡转换) 属于模型预测后面连接的后处理算子 * */ styles: { isOpaque: !nodeCanOpaque(n, nodes), isHighlighted: false, + nodeParties: parties, }, nodeDef, }; @@ -98,7 +106,7 @@ const getProjectId = () => { }; const nodeCanOpaque = (node: API.GraphNodeDetail, nodes: API.GraphNode[]) => { - const { inputs = [], nodeDef = {}, status } = node; + const { inputs = [], outputs = [], nodeDef = {}, status } = node; const { domain } = nodeDef; if (status !== 'SUCCEED' || inputs.length === 0) return false; @@ -117,6 +125,17 @@ const nodeCanOpaque = (node: API.GraphNodeDetail, nodes: API.GraphNode[]) => { ) { return true; } + + // 判断下游有没有后处理算子 + const outputSlot = outputs[0]; + const postNode = nodes.find((item: API.GraphNodeDetail) => + (item?.inputs || []).includes(outputSlot), + ) as API.GraphNodeDetail; + if (!postNode) return false; + const { status: postNodeStatus, nodeDef: postNodeNodeDef } = postNode; + if (postNodeStatus === 'SUCCEED' && postNodeNodeDef?.domain === 'postprocessing') { + return true; + } return false; } return domain === 'ml.train'; diff --git a/apps/platform/src/modules/dag-submit/graph-service.ts b/apps/platform/src/modules/dag-submit/graph-service.ts index 03f05cf..62ce0bb 100644 --- a/apps/platform/src/modules/dag-submit/graph-service.ts +++ b/apps/platform/src/modules/dag-submit/graph-service.ts @@ -64,11 +64,12 @@ export class SubmitGraphService extends Model implements GraphEventHandlerProtoc const graph = mainDag.graphManager.getGraphInstance(); if (!graph) return; const { styles = {} } = data; - const { isOpaque = false } = styles; + const { isOpaque = false, nodeParties = [] } = styles; if (isOpaque !== true) { this.modalManager.openModal(dagLogDrawer.id, { nodeData: data, from: 'pipeline', + nodeParties, }); this.autoSelectModel(node); this.resetSelectionEdge(); @@ -94,11 +95,11 @@ export class SubmitGraphService extends Model implements GraphEventHandlerProtoc this.onCenterNode(node.getData().id); const sameBrachNodes = getModelSameBranchNodes(node); if (sameBrachNodes) { - // 需要打包训练算子,不需要打包预测算子 + // 需要打包训练算子,不需要打包预测算子, 也不需要打包后处理算子 const newSameBrachNodes = { modelNode: sameBrachNodes?.modelNode, preNodes: sameBrachNodes?.preNodes, - nextNodes: sameBrachNodes?.nextNodes, + // nextNodes: sameBrachNodes?.nextNodes, }; updateGraphNodesStyles(Object.values(newSameBrachNodes).flat(), { isOpaque: false, @@ -106,7 +107,7 @@ export class SubmitGraphService extends Model implements GraphEventHandlerProtoc this.selectNodeIdsObj = { modelNodeIds: sameBrachNodes.modelNode.map((item) => item.id), preNodesIds: sameBrachNodes.preNodes.map((item) => item.id), - nextNodesIds: sameBrachNodes.nextNodes.map((item) => item.id), + nextNodesIds: [], predictNodesIds: [], }; highlightSelectionByIds(this.getSelectIds()); diff --git a/apps/platform/src/modules/dag-submit/util.ts b/apps/platform/src/modules/dag-submit/util.ts index 7af1caf..cbdf806 100644 --- a/apps/platform/src/modules/dag-submit/util.ts +++ b/apps/platform/src/modules/dag-submit/util.ts @@ -74,7 +74,10 @@ export const highlightSelectionByIds = (ids: string[]) => { /** * 重置画布展示样式 - * 只有成功的模型训练算子以及(模型预测算子并且上游有线性模型参数修改算子)才可点击 + * 可点击的高亮算子 + * 1. 成功的模型训练算子 + * 2. 成功的模型预测算子并且上游有线性模型参数修改算子 + * 3. 成功的模型预测算子并且下游有成功的后处理算子 * */ export const resetGraphStyles = () => { const graph = mainDag.graphManager.getGraphInstance(); @@ -96,6 +99,7 @@ export const resetGraphStyles = () => { /** * 成功的模型训练算子高亮 * 成功的模型预测算子并且上游有线性模型参数修改算子高亮 + * 成功的模型预测算子并且下游有后处理算子高亮 */ export const nodeCanOpaque = (node: Node) => { const graph = mainDag.graphManager.getGraphInstance(); @@ -113,7 +117,11 @@ export const nodeCanOpaque = (node: Node) => { item.getData().nodeDef?.name === 'model_param_modifications' && item.getData().status === 0, ); - return !!modelParamsModificationNode; + + // 获取后处理算子 + const postNodesResult = getPostNodes(node); + + return !!modelParamsModificationNode || postNodesResult.length !== 0; } return domain === 'ml.train'; }; diff --git a/apps/platform/src/modules/data-manager/data-manager.service.ts b/apps/platform/src/modules/data-manager/data-manager.service.ts index 766d1c2..2054ea2 100644 --- a/apps/platform/src/modules/data-manager/data-manager.service.ts +++ b/apps/platform/src/modules/data-manager/data-manager.service.ts @@ -31,4 +31,12 @@ export enum UploadStatus { export enum DataSheetType { 'CSV' = 'CSV', 'HTTP' = 'HTTP', + 'OSS' = 'OSS', +} + +export enum DataSourceType { + OSS = 'OSS', + HTTP = 'HTTP', + ODPS = 'ODPS', + LOCAL = 'LOCAL', } diff --git a/apps/platform/src/modules/data-manager/data-manager.view.tsx b/apps/platform/src/modules/data-manager/data-manager.view.tsx index b1cad9b..6c6aab7 100644 --- a/apps/platform/src/modules/data-manager/data-manager.view.tsx +++ b/apps/platform/src/modules/data-manager/data-manager.view.tsx @@ -1,11 +1,6 @@ -import { - SearchOutlined, - InfoCircleOutlined, - DownOutlined, - ReloadOutlined, -} from '@ant-design/icons'; -import type { MenuProps, RadioChangeEvent, TourProps } from 'antd'; -import { Dropdown, message, Tag } from 'antd'; +import { SearchOutlined, InfoCircleOutlined, ReloadOutlined } from '@ant-design/icons'; +import type { RadioChangeEvent, TourProps } from 'antd'; +import { message, Tag } from 'antd'; import { Button, Radio, @@ -28,8 +23,7 @@ import React, { useEffect, useRef } from 'react'; import { confirmDelete } from '@/components/comfirm-delete'; import { EdgeAuthWrapper } from '@/components/edge-wrapper-auth'; import { Platform, hasAccess } from '@/components/platform-wrapper'; -import { HttpDataAddDrawer } from '@/modules/data-table-add/add-http-data/http-data-add.view'; -import { DataTableAddContent } from '@/modules/data-table-add/data-table-add.view'; +import { DataAddDrawer } from '@/modules/data-table-add/add-data/add-data.view'; import { DatatableInfoService } from '@/modules/data-table-info/component/data-table-auth/data-table-auth.service'; import { DataTableAuth } from '@/modules/data-table-info/data-table-auth-drawer'; import { DataTableInfoDrawer } from '@/modules/data-table-info/data-table-info.view'; @@ -49,7 +43,7 @@ import { LoginService } from '../login/login.service'; import { DataManagerService, - DataSheetType, + DataSourceType, UploadStatus, } from './data-manager.service'; import styles from './index.less'; @@ -87,13 +81,14 @@ export const DataManagerComponent: React.FC = () => { ), }, { - title: '表类型', - dataIndex: 'type', - key: 'type', + title: '数据源类型', + dataIndex: 'datasourceType', + key: 'datasourceType', width: '10%', filters: [ - { text: 'CSV', value: DataSheetType.CSV }, - { text: 'HTTP', value: DataSheetType.HTTP }, + { text: 'OSS', value: DataSourceType.OSS }, + { text: 'HTTP', value: DataSourceType.HTTP }, + { text: 'LOCAL', value: DataSourceType.LOCAL }, ], }, { @@ -138,7 +133,14 @@ export const DataManagerComponent: React.FC = () => { }, }, { - title: '状态', + title: ( + +
状态
+ + + +
+ ), dataIndex: 'status', key: 'status', width: '14%', @@ -176,7 +178,11 @@ export const DataManagerComponent: React.FC = () => { dataIndex: 'pushToTeeStatus', width: '15%', render: (status: string, record: API.DatatableVO) => { - if (record.type === DataSheetType.HTTP) return '-'; + if ( + record.datasourceType === DataSourceType.HTTP || + record.datasourceType === DataSourceType.OSS + ) + return '-'; if (!status || status === '') { return ( - +
@@ -342,7 +335,7 @@ export const DataManagerComponent: React.FC = () => { } loading={viewInstance.tableLoading} onChange={(pagination, filters, sorter) => { - viewInstance.typeFilters = filters?.type as FilterValue; + viewInstance.typeFilters = filters?.datasourceType as FilterValue; viewInstance.getTableList(); }} pagination={{ @@ -385,16 +378,6 @@ export const DataManagerComponent: React.FC = () => { /> )} - {viewInstance.showAddDataDrawer && ( - { - viewInstance.getTableList(); - viewInstance.showAddDataDrawer = false; - }} - visible={viewInstance.showAddDataDrawer} - /> - )} - {viewInstance.showDatatableInfoDrawer && ( { @@ -408,13 +391,13 @@ export const DataManagerComponent: React.FC = () => { /> )} - {viewInstance.showHttpDataAddDrawer && ( - { viewInstance.getTableList(); - viewInstance.showHttpDataAddDrawer = false; + viewInstance.showDataAddDrawer = false; }} - visible={viewInstance.showHttpDataAddDrawer} + visible={viewInstance.showDataAddDrawer} /> )} {contextHolder} @@ -449,11 +432,9 @@ export class DataManagerView extends Model { tableInfo: API.DatatableVO = {}; - showAddDataDrawer = false; - showDatatableInfoDrawer = false; - showHttpDataAddDrawer = false; + showDataAddDrawer = false; currentNode: API.NodeVO = {}; @@ -505,12 +486,8 @@ export class DataManagerView extends Model { this.displayTableList = this.tablesList; } - addData() { - this.showAddDataDrawer = true; - } - - addHttpData = () => { - this.showHttpDataAddDrawer = true; + addData = () => { + this.showDataAddDrawer = true; }; openDataInfo(tableInfo: API.DatatableVO) { @@ -586,14 +563,26 @@ export class DataManagerView extends Model { refreshTableStatus = async (record: API.DatatableVO) => { try { - const { status } = await getDatatable({ + const { status, data } = await getDatatable({ datatableId: record.datatableId, nodeId: this.currentNode.nodeId, type: record.type, }); if (status?.code === 0) { message.success('数据状态刷新成功'); - this.getTableList(); + // TODO: 这里服务端列表状态和这个状态暂时做不到同步,需要手动修改列表状态 + // this.getTableList(); + const newStatus = data?.status; + const newList = this.displayTableList.map((item) => { + if (item.datatableId === record.datatableId) { + return { + ...item, + status: newStatus, + }; + } + return item; + }); + this.displayTableList = newList; } else { message.error('数据状态刷新失败'); } diff --git a/apps/platform/src/modules/data-source-list/components/create-data-source/index.less b/apps/platform/src/modules/data-source-list/components/create-data-source/index.less new file mode 100644 index 0000000..0e8c30c --- /dev/null +++ b/apps/platform/src/modules/data-source-list/components/create-data-source/index.less @@ -0,0 +1,63 @@ +.actions { + display: flex; + align-items: center; + justify-content: end; +} + +.nodeTitle { + color: #000000e0; + font-size: 14px; + font-weight: 500; + line-height: 22px; +} + +.nodeContent { + display: flex; + align-items: center; + padding: 12px; + border-radius: 2px; + margin-top: 8px; + background: #00000005; + + .nodeName { + margin-right: 3px; + } + + .check { + padding: 0; + margin-left: 6px; + } + + .testing { + color: rgb(0 104 250 / 50%); + + &:hover { + color: rgb(0 104 250 / 50%) !important; + } + } + + .nodeStatusTag { + margin-left: 6px; + + :global { + .ant-btn { + padding: 0; + margin-left: 0; + } + + .ant-btn:not(.ant-btn-icon-only) > .ant-btn-icon:not(:last-child) { + margin-inline-end: 4px; + } + } + } + + :global { + .ant-form-item { + margin-bottom: 0; + } + + .ant-select { + width: 160px; + } + } +} diff --git a/apps/platform/src/modules/data-source-list/components/create-data-source/index.tsx b/apps/platform/src/modules/data-source-list/components/create-data-source/index.tsx new file mode 100644 index 0000000..2ae0220 --- /dev/null +++ b/apps/platform/src/modules/data-source-list/components/create-data-source/index.tsx @@ -0,0 +1,291 @@ +import { ExclamationCircleOutlined } from '@ant-design/icons'; +import { + Button, + Drawer, + Form, + Input, + Radio, + Select, + Space, + Switch, + Tooltip, + message, +} from 'antd'; +import React, { useEffect, useState } from 'react'; + +import { + DataSourceService, + DataSourceType, +} from '@/modules/data-source-list/data-source-list.service'; +import { NodeService } from '@/modules/node'; +import { useModel } from '@/util/valtio-helper'; + +import styles from './index.less'; + +export const CreateDataSourceModal: React.FC<{ + onClose: () => void; + visible: boolean; +}> = ({ visible, onClose }) => { + const dataSourceService = useModel(DataSourceService); + const nodeService = useModel(NodeService); + + const [form] = Form.useForm(); + const values = Form.useWatch([], form); + + const [disabled, setDisabled] = useState(true); + // const [nodeStatus, setNodeStatus] = useState('pending'); + + const closeHandler = () => { + onClose(); + }; + + const handleOk = async () => { + await form.validateFields().then(async (value) => { + const { status } = await dataSourceService.addDataSource(value); + if (status && status.code === 0) { + message.success(`「${value.name}」注册成功」`); + } else { + message.error(status?.msg || '注册失败'); + } + }); + + onClose(); + }; + + useEffect(() => { + if (visible) { + form.validateFields({ validateOnly: true }).then( + () => { + setDisabled(false); + }, + () => { + setDisabled(true); + }, + ); + } + }, [values, visible]); + + // const testNode = () => { + // const nodeId = form.getFieldValue('nodeId'); + // todo 接口调用 + // 点击之后,变成loading + // setNodeStatus('testing'); + // 接口返回之后 变成成功或者失败 + // setNodeStatus('succeed'); + // setNodeStatus('failed'); + // }; + + // const nodeTestContent = { + // pending: ( + // + // ), + // testing: ( + // + // ), + // failed: ( + // + // 校验失败 + // + // ), + // succeed: ( + // + // 校验成功 + // + // ), + // }; + + return ( + + + + + +
+ } + > + + + + +
+ OSS + + OSS存储支持CSV文件类型的数据资产 +
+ } + > + + + +
+ {/* + HTTP + + + ODPS + */} +
+
+ + {({ getFieldValue }) => { + return getFieldValue('type') === DataSourceType.OSS ? ( + <> + + + + + + + + + + + + + + + + + + + + + + + ) : getFieldValue('type') === DataSourceType.HTTP ? ( + + + + ) : ( + + + + ); + }} + +
节点连接配置
+
+ 节点1: + + { + viewInstance.searchTable(e); + }} + suffix={ + + } + /> +
+
+ { + viewInstance.dataFilter(e); + }} + > + 全部 + 可用 + 不可用 + +
+
+ +
+ +
+
{ + viewInstance.typeFilters = + filters?.type === null + ? ([DataSourceType.OSS, DataSourceType.HTTP] as FilterValue) + : filters?.type; + viewInstance.getDataSourceList(); + }} + pagination={{ + total: viewInstance.totalNum || 1, + current: viewInstance.pageNumber, + pageSize: viewInstance.pageSize, + showSizeChanger: true, + onChange: (page, pageSize) => { + viewInstance.pageNumber = page; + viewInstance.pageSize = pageSize; + }, + size: 'default', + }} + rowKey={(record) => record.datasourceId as string} + size="small" + /> + + + {viewInstance.showAddDataSourceDrawer && ( + { + viewInstance.getDataSourceList(); + viewInstance.showAddDataSourceDrawer = false; + }} + visible={viewInstance.showAddDataSourceDrawer} + /> + )} + {viewInstance.showDataSourceInfoDrawer && ( + { + viewInstance.getDataSourceList(); + viewInstance.showDataSourceInfoDrawer = false; + }} + visible={viewInstance.showDataSourceInfoDrawer} + data={viewInstance.DataSourceData} + /> + )} + + ); +}; + +export class DataSourceView extends Model { + dataSourceList: API.DatasourceListInfo[] = []; + + pageNumber = 1; + + pageSize = 10; + + totalNum = 1; + + statusFilter = ''; + + typeFilters: FilterValue | null = [DataSourceType.OSS, DataSourceType.HTTP]; + + search = ''; + + tableLoading = false; + + typesFilter: string[] = []; + + searchDebounce: number | undefined = undefined; + + showAddDataSourceDrawer = false; + + showDataSourceInfoDrawer = false; + + DataSourceData: API.DatasourceListInfo = {}; + + currentNode: API.NodeVO = {}; + + nodeService = getModel(NodeService); + dataSourceService = getModel(DataSourceService); + + onViewMount() { + if (this.nodeService.currentNode) { + this.getDataSourceList(); + } + this.nodeService.eventEmitter.on((currentNode) => { + this.getDataSourceList(); + this.currentNode = currentNode; + }); + } + + async getDataSourceList(isUpload?: boolean) { + if (isUpload) { + this.tableLoading = false; + } else { + this.tableLoading = true; + } + const nodeId = this.nodeService.currentNode?.nodeId; + const listData = await list({ + nodeId: nodeId || '', + page: this.pageNumber, + size: this.pageSize, + status: this.statusFilter, + name: this.search, + types: this.typeFilters as string[], + }); + + this.tableLoading = false; + this.totalNum = listData?.data?.total || 1; + this.dataSourceList = listData?.data?.infos || []; + } + + searchTable(e: ChangeEvent) { + this.search = e.target.value; + clearTimeout(this.searchDebounce); + this.searchDebounce = setTimeout(() => { + this.getDataSourceList(); + }, 300) as unknown as number; + } + + dataFilter(e: RadioChangeEvent) { + if (e.target.value === 'all') { + this.statusFilter = ''; + } else if (e.target.value === 'Available') { + this.statusFilter = 'Available'; + } else { + this.statusFilter = 'UnAvailable'; + } + this.getDataSourceList(true); + } + + addDataSource() { + this.showAddDataSourceDrawer = true; + } + + showDataSourceInfo(data: API.DatasourceListInfo) { + this.showDataSourceInfoDrawer = true; + this.DataSourceData = data; + } +} diff --git a/apps/platform/src/modules/data-source-list/type.ts b/apps/platform/src/modules/data-source-list/type.ts new file mode 100644 index 0000000..5e838f8 --- /dev/null +++ b/apps/platform/src/modules/data-source-list/type.ts @@ -0,0 +1,6 @@ +export enum DataSourceType { + 'OSS' = 'OSS', + 'HTTP' = 'HTTP', + 'ODPS' = 'ODPS', // 未支持 + 'LOCAL' = 'LOCAL', +} diff --git a/apps/platform/src/modules/data-table-add/add-data/add-data-service.ts b/apps/platform/src/modules/data-table-add/add-data/add-data-service.ts new file mode 100644 index 0000000..6208754 --- /dev/null +++ b/apps/platform/src/modules/data-table-add/add-data/add-data-service.ts @@ -0,0 +1,139 @@ +import { parse } from 'query-string'; + +import { createFeatureDatasource } from '@/services/secretpad/FeatureDatasourceController'; +import { createDataTable } from '@/services/secretpad/DatatableController'; +import { list } from '@/services/secretpad/DataSourceController'; +import { Model } from '@/util/valtio-helper'; + +export enum DataSourceType { + OSS = 'OSS', + HTTP = 'HTTP', + ODPS = 'ODPS', + LOCAL = 'LOCAL', +} + +type OptionType = { + label: string; + value: string; +}; + +type Structure = { + featureName: string; + featureType: string; + featureDescription: string; +}; + +type AddDataSheetValue = { + dataSource: string; + address: string; + tableName: string; + tableDesc: string; + features: Structure[]; + odpsSheetName?: string; + datasourceType: string; + datasourceName: string; +}; + +type AddDataSheetParams = AddDataSheetValue & { + nodeId: string; + type: DataSourceType; +}; + +export class AddDataSheetService extends Model { + submitDisabled = true; + + /** 数据源列表 */ + dataSourceList: API.DatasourceListInfo[] = [ + { + name: '本地数据源', + datasourceId: 'default-data-source', + type: DataSourceType.LOCAL, + }, + ]; + + dataSourceOptions: OptionType[] = []; + + addDataSheetLoading = false; + + /** 获取数据源列表 */ + queryDataSourceList = async (nodeId: string) => { + const { data, status } = await list({ + nodeId, + types: [DataSourceType.OSS, DataSourceType.HTTP], + page: 1, + size: 1000, + status: '', + name: '', + }); + if (data && status && status.code === 0) { + this.dataSourceList = [ + { + name: '本地数据源', + datasourceId: 'default-data-source', + type: DataSourceType.LOCAL, + }, + // { + // name: 'oss数据源', + // datasourceId: 'alice-oss-datasource', + // type: 'OSS', + // }, + ...(data?.infos?.filter((item) => item.status === 'Available') || []), + ]; + } + this.dataSourceOptions = this.dataSourceList.map((item) => ({ + label: item.name!, + value: item.datasourceId!, + })); + }; + + addDataSheet = async (value: AddDataSheetValue, type: DataSourceType) => { + const params = { + ...value, + nodeId: parse(window.location.search)?.nodeId as string, + type, + }; + switch (type) { + case DataSourceType.HTTP: + return this.addHttpData(params); + case DataSourceType.OSS: + return this.addOssData(params); + default: + return { status: {} }; + } + }; + + addHttpData = async (value: AddDataSheetParams) => { + const params = { + featureTableName: value.tableName, + nodeId: value.nodeId, + type: value.type, + desc: value.tableDesc, + url: value.address, + columns: value.features.map((item: Structure) => ({ + colName: item.featureName, + colType: item.featureType, + colComment: item.featureDescription, + })), + }; + return await createFeatureDatasource(params); + }; + + addOssData = async (value: AddDataSheetParams) => { + const params = { + nodeId: value.nodeId, + datatableName: value.tableName, + datasourceId: value.dataSource, + type: value.type, + desc: value.tableDesc, + relativeUri: value.address, + columns: value.features.map((item: Structure) => ({ + colName: item.featureName, + colType: item.featureType, + colComment: item.featureDescription || '', + })), + datasourceType: value.datasourceType, + datasourceName: value.datasourceName, + }; + return await createDataTable(params); + }; +} diff --git a/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx b/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx new file mode 100644 index 0000000..5ee3aa4 --- /dev/null +++ b/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx @@ -0,0 +1,251 @@ +import { Button, Form, Input, Select, Space, message } from 'antd'; +import { Drawer } from 'antd'; +import { debounce } from 'lodash'; +import { parse } from 'query-string'; +import { useCallback, useEffect, useState } from 'react'; +import { useLocation } from 'umi'; + +import { useModel } from '@/util/valtio-helper'; + +import { + DataTableStructure, + DataTableStructureService, +} from '../component/dataTableStructure/data-table-structure.view'; +import { HttpQueryExample } from '../component/httpQueryExample'; +import { OdpsPartition } from '../component/odpsPartition/odps-partition.view'; +import { + UploadTable, + UploadTableView, +} from '../component/upload-table/upload-table.view'; + +import { AddDataSheetService, DataSourceType } from './add-data-service'; +import styles from './index.less'; + +export const DataAddDrawer = ({ + visible, + onClose, +}: { + visible: boolean; + onClose: () => void; +}) => { + const UploadInstance = useModel(UploadTableView); + const [disabled, setDisabled] = useState(false); + + const dataTableStructureService = useModel(DataTableStructureService); + + const addDataSheetService = useModel(AddDataSheetService); + const { + queryDataSourceList, + dataSourceList, + dataSourceOptions, + addDataSheetLoading, + addDataSheet, + } = addDataSheetService; + + const [form] = Form.useForm(); + const values = Form.useWatch([], form); + const dataSourceFormValue = Form.useWatch('dataSource', form); + const dataSourceFormValueType = dataSourceList.find( + (item) => item.datasourceId === dataSourceFormValue, + )?.type; + const { search } = useLocation(); + const { nodeId } = parse(search); + + useEffect(() => { + const hasValue = + dataSourceFormValueType === DataSourceType.ODPS + ? values?.odpsSheetName && values?.tableName + : values?.address && values?.tableName; + if (hasValue && values?.features?.length !== 0) { + addDataSheetService.submitDisabled = false; + } else { + addDataSheetService.submitDisabled = true; + } + }, [values]); + + const getDataSourceList = useCallback(async () => { + await queryDataSourceList(nodeId as string); + }, [nodeId]); + + useEffect(() => { + if (visible) { + getDataSourceList(); + form.setFieldValue('features', [{}]); + } + }, [visible]); + + const handleClose = () => { + onClose(); + form.resetFields(); + dataTableStructureService.featuresError = []; + }; + + const validateForm = async (options = {}) => { + try { + const validateRes = await form.validateFields(options); + return validateRes; + } catch (e: any) { + const { errorFields } = e; + const featuresError = errorFields.filter((i: any) => i.name[0] === 'features'); + dataTableStructureService.featuresError = featuresError; + throw e; + } + }; + + const handleOk = async () => { + const validateRes = await validateForm(); + dataTableStructureService.featuresError = []; + const currentDataSource = dataSourceList.find( + (item) => item.datasourceId === validateRes.dataSource, + ); + const formValues = { + ...validateRes, + datasourceName: currentDataSource?.name, + datasourceType: currentDataSource?.type, + }; + try { + addDataSheetService.addDataSheetLoading = true; + const { status } = await addDataSheet( + formValues, + currentDataSource?.type as DataSourceType, + ); + addDataSheetService.addDataSheetLoading = false; + if (status && status.code === 0) { + message.success('添加成功'); + onClose(); + } else { + message.error(status?.msg || '添加失败'); + } + } catch (error) { + addDataSheetService.addDataSheetLoading = false; + } + }; + + const handelDataSourceChange = (value: string) => { + if (dataSourceFormValueType === DataSourceType.LOCAL) { + UploadInstance.reset(); + } + dataTableStructureService.featuresError = []; + dataTableStructureService.showFeatureErrorChecked = false; + form.resetFields(); + form.setFieldsValue({ + features: [{}], + dataSource: value, + }); + }; + + return ( + + + + {dataSourceFormValueType && + dataSourceFormValueType === DataSourceType.LOCAL ? ( + + ) : ( + + )} + + + } + > + + + + + )} + {dataSourceFormValueType === DataSourceType.HTTP && ( + + + + )} + {dataSourceFormValueType === DataSourceType.ODPS && ( + + + + )} + + {dataSourceFormValueType && + dataSourceFormValueType !== DataSourceType.LOCAL && ( + <> + + + + {dataSourceFormValueType === DataSourceType.HTTP && } + + + + + {dataSourceFormValueType === DataSourceType.ODPS && } + + )} + {dataSourceFormValueType && + dataSourceFormValueType === DataSourceType.LOCAL && ( + + )} + + + ); +}; diff --git a/apps/platform/src/modules/data-table-add/add-data/index.less b/apps/platform/src/modules/data-table-add/add-data/index.less new file mode 100644 index 0000000..543e84f --- /dev/null +++ b/apps/platform/src/modules/data-table-add/add-data/index.less @@ -0,0 +1,12 @@ +.titleSheet { + margin-bottom: 16px; + color: rgb(0 0 0 / 88%); + font-size: 14px; + line-height: 22px; +} + +.actions { + display: flex; + align-items: center; + justify-content: end; +} diff --git a/apps/platform/src/modules/data-table-add/add-http-data/http-data-add.view.tsx b/apps/platform/src/modules/data-table-add/add-http-data/http-data-add.view.tsx deleted file mode 100644 index 1568656..0000000 --- a/apps/platform/src/modules/data-table-add/add-http-data/http-data-add.view.tsx +++ /dev/null @@ -1,429 +0,0 @@ -import { - DeleteOutlined, - DownloadOutlined, - PlusOutlined, - QuestionCircleOutlined, - UploadOutlined, -} from '@ant-design/icons'; -import { - Alert, - Button, - Checkbox, - Form, - Input, - Select, - Space, - Tooltip, - Upload, - message, -} from 'antd'; -import { Drawer } from 'antd'; -import { parse } from 'query-string'; -import { useEffect, useRef } from 'react'; -import { CSVLink } from 'react-csv'; - -import { createFeatureDatasource } from '@/services/secretpad/FeatureDatasourceController'; -import { Model, useModel } from '@/util/valtio-helper'; - -import { analysisCsv } from '../component/upload-table/util'; - -import styles from './index.less'; - -const downloadData = [ - { 特征名称: 'id1', 特征类型: 'string', 特征描述: '' }, - { 特征名称: 'x1', 特征类型: 'integer', 特征描述: '描述' }, - { 特征名称: 'x2', 特征类型: 'integer', 特征描述: '' }, - { 特征名称: 'x3', 特征类型: 'integer', 特征描述: '' }, - { 特征名称: 'x4', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x5', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x6', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x7', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x8', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x9', 特征类型: 'float', 特征描述: '' }, - { 特征名称: 'x10', 特征类型: 'float', 特征描述: '' }, -]; - -export const HttpDataAddDrawer = ({ - visible, - onClose, -}: { - visible: boolean; - onClose: () => void; -}) => { - const httpDataAddService = useModel(HttpDataAddService); - const [form] = Form.useForm(); - const values = Form.useWatch([], form); - - useEffect(() => { - if (values?.address && values?.tableName && values?.features?.length !== 0) { - httpDataAddService.submitDisabled = false; - } else { - httpDataAddService.submitDisabled = true; - } - }, [values]); - - const csvRef = useRef<{ - link: HTMLLinkElement; - }>(null); - - const triggerDownload = () => { - if (csvRef.current) { - csvRef.current.link.click(); - } - }; - - const featureTypeOptions = [ - { value: 'int', label: 'integer' }, - { value: 'float', label: 'float' }, - { value: 'str', label: 'string' }, - ]; - - useEffect(() => { - if (visible) { - form.setFieldValue('features', [{}]); - } - }, [visible]); - - const handleClose = () => { - onClose(); - form.resetFields(); - httpDataAddService.loading = false; - httpDataAddService.featuresError = []; - }; - - const checkColCsvFormat = (ary: any) => { - if (ary.length !== 3) { - return false; - } - for (const item of ary) { - if (!['特征名称', '特征类型', '特征描述'].includes(item) && item) { - return false; - } - } - return true; - }; - - const handleColCsvUpload = async (file: File, fileList: File[]) => { - form.resetFields(['features']); - try { - const csvData = await analysisCsv(file); - const { - meta: { fields }, - data, - } = csvData; - const checkResult = checkColCsvFormat(fields); - if (!checkResult) { - message.error('请检查CSV格式'); - return; - } - const cols = data.map((info: any) => { - return { - featureName: info['特征名称'] || undefined, - featureType: - featureTypeOptions.find((i) => i.label === info['特征类型'])?.value || - undefined, - featureDescription: info['特征描述'], - }; - }); - const uniqueFeatures = cols.reduce((acc, cur) => { - acc[cur.featureName] = acc[cur.featureName] || cur; - return acc; - }, {} as Record); - const resultCols = Object.values(uniqueFeatures); - form.setFieldValue('features', resultCols); - const repeatLength = cols.length - resultCols.length; - if (repeatLength !== 0) { - message.success( - `上传了${cols.length}个字段, 有${repeatLength}个重复字段已自动去重`, - ); - } else { - message.success(`上传了${cols.length}个字段`); - } - setTimeout(() => { - validateForm(); - }); - } catch (e) { - console.log(e); - message.error('请检查CSV格式'); - form.setFieldValue('features', [{}]); - } - }; - - const validateForm = async (options = {}) => { - try { - const validateRes = await form.validateFields(options); - return validateRes; - } catch (e: any) { - const { errorFields } = e; - const featuresError = errorFields.filter((i: any) => i.name[0] === 'features'); - httpDataAddService.featuresError = featuresError; - throw e; - } - }; - - const handleOk = async () => { - const validateRes = await validateForm(); - httpDataAddService.featuresError = []; - const values = validateRes; - try { - httpDataAddService.loading = true; - const { status } = await httpDataAddService.addHttpData(values); - httpDataAddService.loading = false; - if (status && status.code === 0) { - message.success('添加成功'); - onClose(); - } else { - message.error(status?.msg || '添加失败'); - } - } catch (error) { - httpDataAddService.loading = false; - } - }; - - const handelFeatureChange = async () => { - await validateForm({ dirty: true }); - httpDataAddService.featuresError = []; - }; - - return ( - - - - - } - > -
- 数据表类型 - - - - :http数据源 -
- -
- - - - - - - - - -
-
数据表结构
-
- - - handleColCsvUpload(file, fileList) - } - customRequest={() => { - return; - }} - > - - -
-
- - {httpDataAddService.featuresError.length > 0 && ( -
- - - {`有${httpDataAddService.featuresError.length}个字段错误请检查`} - - - - (httpDataAddService.showFeatureErrorChecked = e.target.checked) - } - > - 仅看错误 - - -
- } - type="error" - showIcon - /> - - )} - -
-
特征名称
-
类型
-
描述(可选)
-
操作
-
- - - {(fields, { add, remove }) => ( -
-
- -
- {fields.reverse().map((field, index) => { - let display = 'flex'; - // 如果有错误,并且勾选了仅看错误项 - if ( - httpDataAddService.featuresError.length > 0 && - httpDataAddService.showFeatureErrorChecked - ) { - if ( - httpDataAddService.featuresError.find( - (i: any) => i.name[1] == fields.length - 1 - index, - ) - ) { - display = 'flex'; - } else { - display = 'none'; - } - } - return ( - - { - const values = form.getFieldValue('features'); - const features = values.filter( - (i: any) => value && i?.featureName === value, - ); - if (features.length > 1) return Promise.reject(); - return Promise.resolve(); - }, - message: '存在重复特征', - }, - ]} - > - - - - - -
- { - remove(field.name); - setTimeout(() => { - validateForm(); - handelFeatureChange(); - }); - }} - /> -
-
- ); - })} -
- )} -
- -
- ); -}; - -export class HttpDataAddService extends Model { - loading = false; - - submitDisabled = true; - - featuresError = []; - - showFeatureErrorChecked = false; - - addHttpData = async (value: any) => { - const params = { - featureTableName: value.tableName, - nodeId: parse(window.location.search)?.nodeId, - type: 'HTTP', - desc: value.tableDesc, - url: value.address, - columns: value.features.map( - (item: { - featureName: string; - featureType: string; - featureDescription: string; - }) => ({ - colName: item.featureName, - colType: item.featureType, - colComment: item.featureDescription, - }), - ), - }; - return await createFeatureDatasource(params); - }; -} diff --git a/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx b/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx new file mode 100644 index 0000000..53fe3d4 --- /dev/null +++ b/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx @@ -0,0 +1,300 @@ +import { + DeleteOutlined, + DownloadOutlined, + PlusOutlined, + UploadOutlined, +} from '@ant-design/icons'; +import { + Alert, + Button, + Checkbox, + Form, + Input, + message, + Select, + Space, + Upload, +} from 'antd'; +import { useRef } from 'react'; +import { CSVLink } from 'react-csv'; + +import { Model, useModel } from '@/util/valtio-helper'; + +import { analysisCsv } from '../upload-table/util'; + +import styles from './index.less'; + +const downloadData = [ + { 特征名称: 'id1', 特征类型: 'string', 特征描述: '' }, + { 特征名称: 'x1', 特征类型: 'integer', 特征描述: '描述' }, + { 特征名称: 'x2', 特征类型: 'integer', 特征描述: '' }, + { 特征名称: 'x3', 特征类型: 'integer', 特征描述: '' }, + { 特征名称: 'x4', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x5', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x6', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x7', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x8', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x9', 特征类型: 'float', 特征描述: '' }, + { 特征名称: 'x10', 特征类型: 'float', 特征描述: '' }, +]; + +const featureTypeOptions = [ + { value: 'int', label: 'integer' }, + { value: 'float', label: 'float' }, + { value: 'str', label: 'string' }, +]; + +export const DataTableStructure = () => { + const form = Form.useFormInstance(); + + const dataTableStructureService = useModel(DataTableStructureService); + + const csvRef = useRef<{ + link: HTMLLinkElement; + }>(null); + + const triggerDownload = () => { + if (csvRef.current) { + csvRef.current.link.click(); + } + }; + + const checkColCsvFormat = (ary: any) => { + if (ary.length !== 3) { + return false; + } + for (const item of ary) { + if (!['特征名称', '特征类型', '特征描述'].includes(item) && item) { + return false; + } + } + return true; + }; + + const validateForm = async (options = {}) => { + try { + const validateRes = await form.validateFields(options); + return validateRes; + } catch (e: any) { + const { errorFields } = e; + const featuresError = errorFields.filter((i: any) => i.name[0] === 'features'); + dataTableStructureService.featuresError = featuresError; + throw e; + } + }; + + const handelFeatureChange = async () => { + await validateForm({ dirty: true }); + dataTableStructureService.featuresError = []; + }; + + const handleColCsvUpload = async (file: File, fileList: File[]) => { + form.resetFields(['features']); + try { + const csvData = await analysisCsv(file); + const { + meta: { fields }, + data, + } = csvData; + const checkResult = checkColCsvFormat(fields); + if (!checkResult) { + message.error('请检查CSV格式'); + return; + } + const cols = data.map((info: any) => { + return { + featureName: info['特征名称'] || undefined, + featureType: + featureTypeOptions.find((i) => i.label === info['特征类型'])?.value || + undefined, + featureDescription: info['特征描述'], + }; + }); + const uniqueFeatures = cols.reduce((acc, cur) => { + acc[cur.featureName] = acc[cur.featureName] || cur; + return acc; + }, {} as Record); + const resultCols = Object.values(uniqueFeatures); + form.setFieldValue('features', resultCols); + const repeatLength = cols.length - resultCols.length; + if (repeatLength !== 0) { + message.success( + `上传了${cols.length}个字段, 有${repeatLength}个重复字段已自动去重`, + ); + } else { + message.success(`上传了${cols.length}个字段`); + } + setTimeout(() => { + handelFeatureChange(); + }); + } catch (e) { + console.log(e); + message.error('请检查CSV格式'); + form.setFieldValue('features', [{}]); + } + }; + + return ( + <> + +
+
数据表结构
+
+ + handleColCsvUpload(file, fileList)} + customRequest={() => { + return; + }} + > + + +
+
+ + {dataTableStructureService.featuresError.length > 0 && ( +
+ + + {`有${dataTableStructureService.featuresError.length}个字段错误请检查`} + + + + (dataTableStructureService.showFeatureErrorChecked = + e.target.checked) + } + > + 仅看错误 + + +
+ } + type="error" + showIcon + /> + + )} + +
+
特征名称
+
类型
+
描述(可选)
+
操作
+
+ + + {(fields, { add, remove }) => ( +
+
+ +
+ {fields.reverse().map((field, index) => { + let display = 'flex'; + // 如果有错误,并且勾选了仅看错误项 + if ( + dataTableStructureService.featuresError.length > 0 && + dataTableStructureService.showFeatureErrorChecked + ) { + if ( + dataTableStructureService.featuresError.find( + (i: any) => i.name[1] == fields.length - 1 - index, + ) + ) { + display = 'flex'; + } else { + display = 'none'; + } + } + return ( + + { + const values = form.getFieldValue('features'); + const features = values.filter( + (i: any) => value && i?.featureName === value, + ); + if (features.length > 1) return Promise.reject(); + return Promise.resolve(); + }, + message: '存在重复特征', + }, + ]} + > + + + + + +
+ { + remove(field.name); + setTimeout(() => { + validateForm(); + handelFeatureChange(); + }); + }} + /> +
+
+ ); + })} +
+ )} +
+ + ); +}; + +export class DataTableStructureService extends Model { + featuresError = []; + + showFeatureErrorChecked = false; +} diff --git a/apps/platform/src/modules/data-table-add/add-http-data/index.less b/apps/platform/src/modules/data-table-add/component/dataTableStructure/index.less similarity index 92% rename from apps/platform/src/modules/data-table-add/add-http-data/index.less rename to apps/platform/src/modules/data-table-add/component/dataTableStructure/index.less index 261b35a..4311191 100644 --- a/apps/platform/src/modules/data-table-add/add-http-data/index.less +++ b/apps/platform/src/modules/data-table-add/component/dataTableStructure/index.less @@ -1,10 +1,3 @@ -.titleSheet { - margin-bottom: 16px; - color: rgb(0 0 0 / 88%); - font-size: 14px; - line-height: 22px; -} - .dataSheetTitle { display: flex; align-items: center; diff --git a/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.less b/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.less new file mode 100644 index 0000000..10ec698 --- /dev/null +++ b/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.less @@ -0,0 +1,10 @@ +.content { + margin-bottom: 20px; + + .toggleBtn { + color: #0068fa; + cursor: pointer; + font-size: 14px; + line-height: 22px; + } +} diff --git a/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.tsx b/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.tsx new file mode 100644 index 0000000..c1b9d41 --- /dev/null +++ b/apps/platform/src/modules/data-table-add/component/httpQueryExample/index.tsx @@ -0,0 +1,45 @@ +import { DownOutlined } from '@ant-design/icons'; +import { Space, Typography } from 'antd'; +import { useState } from 'react'; + +import styles from './index.less'; + +// const CodeContent = ``; + +export const HttpQueryExample = () => { + const [toggle, setToggle] = useState(false); + + return ( +
+
+ + 请求示例 + setToggle(!toggle)}> + {toggle ? '收起' : '展开'} + + + +
+
+
+ + 点击查看官网文档 + +
+ {/* */} +
+
+ ); +}; diff --git a/apps/platform/src/modules/data-table-add/component/odpsPartition/odps-partition.view.tsx b/apps/platform/src/modules/data-table-add/component/odpsPartition/odps-partition.view.tsx new file mode 100644 index 0000000..41b565c --- /dev/null +++ b/apps/platform/src/modules/data-table-add/component/odpsPartition/odps-partition.view.tsx @@ -0,0 +1,39 @@ +import { Form, Row, Col, Input, Space } from 'antd'; + +export const OdpsPartition = () => { + return ( +
+ + + {(fields) => { + return ( + <> + {fields.map((field, index) => { + return ( + + +
+ + + + + + + + + + + + ); + })} + + ); + }} + + + + ); +}; diff --git a/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx b/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx index 6459adb..9683af2 100644 --- a/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx +++ b/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx @@ -158,7 +158,7 @@ export const UploadTable: React.FC = ({ setDisabled }) => { - 默认数据源 + 本地数据源 节点本地数据 @@ -482,7 +482,7 @@ export class UploadTableView extends Model { name: file.name, percent: 100, status: 'Error', - info: response?.status.msg || '', + info: response?.status?.msg || '', }; } }; @@ -629,6 +629,8 @@ export class UploadTableView extends Model { description: values.tbl_desc, datatableSchema: values.schema, realName: this.fileInfo?.realName, + datasourceType: 'LOCAL', + datasourceName: 'default-data-source', }); this.submitting = false; diff --git a/apps/platform/src/modules/data-table-info/data-table-auth/reademe.md b/apps/platform/src/modules/data-table-info/data-table-auth/reademe.md deleted file mode 100644 index 23205e6..0000000 --- a/apps/platform/src/modules/data-table-info/data-table-auth/reademe.md +++ /dev/null @@ -1,2 +0,0 @@ -这个文件夹下代码属于车险需求代码视觉: -https://done.alibaba-inc.com/file/TYrxgfwK25ys/MmpLFFqGSs01mIjw/preview?m=SPECS&aid=750E996B-28F6-483B-97AE-0CDB6DFBBC02&pageId=D8760893-E92B-437D-BF46-9C2383BD2A58 diff --git a/apps/platform/src/modules/data-table-info/data-table-info.view.tsx b/apps/platform/src/modules/data-table-info/data-table-info.view.tsx index 53d94d0..67b8ab7 100644 --- a/apps/platform/src/modules/data-table-info/data-table-info.view.tsx +++ b/apps/platform/src/modules/data-table-info/data-table-info.view.tsx @@ -3,6 +3,7 @@ import { Badge, Descriptions, message, Space, Tabs } from 'antd'; import { Drawer } from 'antd'; import type { TabsProps } from 'antd'; import React, { useEffect } from 'react'; + import { DataSheetType } from '@/modules/data-manager/data-manager.service'; import { getDatatable } from '@/services/secretpad/DatatableController'; import { Model, useModel } from '@/util/valtio-helper'; @@ -21,6 +22,12 @@ interface IProps { data: T; } +export const DataSheetText = { + [DataSheetType.CSV]: '节点本地数据', + [DataSheetType.HTTP]: 'HTTP数据', + [DataSheetType.OSS]: 'OSS数据', +}; + export const DataTableInfoDrawer: React.FC> = (props) => { const { visible, close, data } = props; const viewInstance = useModel(DataTableInfoDrawerView); @@ -73,11 +80,10 @@ export const DataTableInfoDrawer: React.FC> = (props) => { > - {/* {tableInfo.datasourceId} */} - 默认数据源 + {tableInfo.datasourceName} - {tableInfo.type === DataSheetType.CSV ? '节点本地数据' : 'HTTP数据'} + {tableInfo?.datasourceType} {tableInfo.relativeUri} diff --git a/apps/platform/src/modules/layout/dag-layout/index.tsx b/apps/platform/src/modules/layout/dag-layout/index.tsx index 3a1cfd0..67ffc58 100644 --- a/apps/platform/src/modules/layout/dag-layout/index.tsx +++ b/apps/platform/src/modules/layout/dag-layout/index.tsx @@ -3,10 +3,10 @@ import type { TabsProps } from 'antd'; import { Divider, Tabs, Space } from 'antd'; import classnames from 'classnames'; import { parse } from 'query-string'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { history } from 'umi'; -import { AccessWrapper, Platform } from '@/components/platform-wrapper'; +import { AccessWrapper, PadMode, Platform } from '@/components/platform-wrapper'; import { AdvancedConfigComponent } from '@/modules/advanced-config/advanced-config-entry'; import BinningResultDrawer from '@/modules/component-config/config-item-render/custom-render/binning-modification/drawer'; import { @@ -71,8 +71,7 @@ export const DagLayout = () => { const loginService = useModel(LoginService); const slsLogService = useModel(SlsService); - const { type = 'DAG', mode } = parse(window.location.search); - + const { type = 'DAG', mode, projectId } = parse(window.location.search); const goBack = async () => { const userInfo = await loginService.getUserInfo(); if (userInfo.platformType === Platform.AUTONOMY) { @@ -80,6 +79,7 @@ export const DagLayout = () => { history.push(`/edge?nodeId=${userInfo.ownerId}&tab=${origin || 'my-project'}`); } else { history.push('/home?tab=project-management'); + viewInstance.setInitActiveMenu(''); } }; @@ -123,17 +123,18 @@ export const DagLayout = () => { ALL: [], }; - useEffect(() => { + /** 初始化active menu */ + const setInitActiveMenu = useCallback(() => { const currentMenu = P2pMenuList[type as keyof typeof P2pMenuList]?.find( (item) => item.isInit, ); viewInstance.setActiveMenu(currentMenu?.id || DagLayoutMenu.PROJECTDATA); currentMenu?.callBack && currentMenu.callBack(); - }, [type, mode]); + }, [type, mode, projectId]); useEffect(() => { + const currentMenuList = P2pMenuList[type as keyof typeof P2pMenuList]; if (viewInstance.initActiveMenu) { - const currentMenuList = P2pMenuList[type as keyof typeof P2pMenuList]; const currentMenu = currentMenuList.find( (item) => item.id === viewInstance.initActiveMenu, ); @@ -141,8 +142,10 @@ export const DagLayout = () => { viewInstance.setActiveMenu(viewInstance.initActiveMenu); currentMenu?.callBack && currentMenu.callBack(); } + } else { + setInitActiveMenu(); } - }, [viewInstance.initActiveMenu]); + }, [viewInstance.initActiveMenu, type, mode, projectId]); const logItems = [ { @@ -151,13 +154,16 @@ export const DagLayout = () => { children: , disabled: false, }, - { + ]; + + if (mode === PadMode.MPC) { + logItems.push({ key: '2', label: , disabled: !slsLogService.slsLogIsConfig, children: , - }, - ]; + }); + } return (
diff --git a/apps/platform/src/modules/layout/model-submission-layout/index.tsx b/apps/platform/src/modules/layout/model-submission-layout/index.tsx index b1a96f2..26f89ca 100644 --- a/apps/platform/src/modules/layout/model-submission-layout/index.tsx +++ b/apps/platform/src/modules/layout/model-submission-layout/index.tsx @@ -1,11 +1,15 @@ import { ArrowLeftOutlined } from '@ant-design/icons'; import { Divider, Button, Alert, Tooltip, Breadcrumb, Popconfirm } from 'antd'; import classNames from 'classnames'; +import { parse } from 'query-string'; import { history } from 'umi'; +import { PadMode } from '@/components/platform-wrapper'; import { Log, LogLabel } from '@/modules/dag-log/log-viewer.view'; import { DagLogDrawer } from '@/modules/dag-log/log.drawer.layout'; import { DagLog } from '@/modules/dag-log/log.view'; +import { SlsLog, SlsLogLabel } from '@/modules/dag-log/sls-log-viewer.view'; +import { SlsService } from '@/modules/dag-log/sls-service'; import { DefaultModalManager } from '@/modules/dag-modal-manager'; import { ModalWidth } from '@/modules/dag-modal-manager/modal-manger-protocol'; import { @@ -13,17 +17,18 @@ import { SubmissionDrawer, } from '@/modules/dag-model-submission'; import { ModelSubmissionDrawerItem } from '@/modules/dag-model-submission/submission-drawer'; +import { SubmissionDrawerService } from '@/modules/dag-model-submission/submission-service'; import { SubmitGraphComponent } from '@/modules/dag-submit/graph'; import { ToolbuttonComponent } from '@/modules/dag-submit/toolbutton'; import { Model, useModel, getModel } from '@/util/valtio-helper'; import styles from './index.less'; -import { SubmissionDrawerService } from '@/modules/dag-model-submission/submission-service'; const RIGHT_DIST = 20; export const ModelSubmissionLayout = () => { const viewInstance = useModel(ModelSubmissionView); + const slsLogService = useModel(SlsService); const goBack = async () => { viewInstance.submissionDrawerService.cancelSubmitTimer(); @@ -33,6 +38,27 @@ export const ModelSubmissionLayout = () => { }); }; + const searchDagParams = window.location.search; + const { mode } = parse(searchDagParams); + + const logItems = [ + { + key: '1', + label: , + children: , + disabled: false, + }, + ]; + + if (mode === PadMode.MPC) { + logItems.push({ + key: '2', + label: , + disabled: !slsLogService.slsLogIsConfig, + children: , + }); + } + return (
@@ -104,16 +130,7 @@ export const ModelSubmissionLayout = () => {
- , - children: , - disabled: false, - }, - ]} - /> +
); diff --git a/apps/platform/src/modules/layout/record-layout/record-layout-view.tsx b/apps/platform/src/modules/layout/record-layout/record-layout-view.tsx index 3c0fb17..c98c6bf 100644 --- a/apps/platform/src/modules/layout/record-layout/record-layout-view.tsx +++ b/apps/platform/src/modules/layout/record-layout/record-layout-view.tsx @@ -3,7 +3,7 @@ import { Breadcrumb, Divider } from 'antd'; import { parse, stringify } from 'query-string'; import { history } from 'umi'; -import { Platform, hasAccess } from '@/components/platform-wrapper'; +import { PadMode, Platform, hasAccess } from '@/components/platform-wrapper'; import { ComponentConfigDrawer } from '@/modules/component-config/config-modal'; import { Log, LogLabel } from '@/modules/dag-log/log-viewer.view'; import { DagLogDrawer } from '@/modules/dag-log/log.drawer.layout'; @@ -32,9 +32,9 @@ export enum RecordArea { export const RecordLayout = () => { const slsLogService = useModel(SlsService); + const searchDagParams = window.location.search; + const { projectId, mode, type } = parse(searchDagParams); const goBack = async () => { - const searchDagParams = window.location.search; - const { projectId, mode, type } = parse(searchDagParams); const { pipelineName, pipelineId, origin } = (history.location.state || {}) as { pipelineId: string; pipelineName: string; @@ -60,6 +60,24 @@ export const RecordLayout = () => { ); }; + const logItems = [ + { + key: '1', + label: , + children: , + disabled: false, + }, + ]; + + if (mode === PadMode.MPC) { + logItems.push({ + key: '2', + label: , + disabled: !slsLogService.slsLogIsConfig, + children: , + }); + } + return (
@@ -103,22 +121,7 @@ export const RecordLayout = () => { - , - children: , - disabled: false, - }, - { - key: '2', - label: , - disabled: !slsLogService.slsLogIsConfig, - children: , - }, - ]} - /> +
diff --git a/apps/platform/src/modules/model-manager/model-release/model-release.view.tsx b/apps/platform/src/modules/model-manager/model-release/model-release.view.tsx index 2812f97..7b5c9d0 100644 --- a/apps/platform/src/modules/model-manager/model-release/model-release.view.tsx +++ b/apps/platform/src/modules/model-manager/model-release/model-release.view.tsx @@ -515,7 +515,7 @@ export const ModelReleaseModal = (props: ModelReleaseModalType) => { name={[`${nodeId}`, 'max_cpu']} dependencies={[[`${nodeId}`, 'min_cpu']]} label="最大 CPU" - initialValue={32} + initialValue={0} rules={[ { required: true, message: '请输入' }, { @@ -524,9 +524,6 @@ export const ModelReleaseModal = (props: ModelReleaseModalType) => { `${nodeId}`, 'min_cpu', ]); - if (Number(value) === 0) { - return Promise.reject(new Error(`取值应该大于0`)); - } if (min_cpu && value < min_cpu) return Promise.reject( new Error(`取值应该大于最小 CPU 值`), @@ -590,7 +587,7 @@ export const ModelReleaseModal = (props: ModelReleaseModalType) => { { `${nodeId}`, 'min_memory', ]); - if (Number(value) === 0) { - return Promise.reject(new Error(`取值应该大于0`)); - } if (min_memory && value < min_memory) return Promise.reject( new Error(`取值应该大于最小 Memory 值`), diff --git a/apps/platform/src/modules/result-details/index.less b/apps/platform/src/modules/result-details/index.less index 0c38687..8d63604 100644 --- a/apps/platform/src/modules/result-details/index.less +++ b/apps/platform/src/modules/result-details/index.less @@ -132,6 +132,7 @@ } :global(.ant-tabs-nav) { + margin-right: 135px; margin-bottom: 8px !important; } } diff --git a/apps/platform/src/modules/result-details/result-details-drawer.tsx b/apps/platform/src/modules/result-details/result-details-drawer.tsx index f0d16b9..e166386 100644 --- a/apps/platform/src/modules/result-details/result-details-drawer.tsx +++ b/apps/platform/src/modules/result-details/result-details-drawer.tsx @@ -1,8 +1,4 @@ -import { - CloseOutlined, - FullscreenExitOutlined, - FullscreenOutlined, -} from '@ant-design/icons'; +import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons'; import { useFullscreen } from 'ahooks'; import { Typography, @@ -13,6 +9,7 @@ import { Tabs, Space, Badge, + Tooltip, } from 'antd'; import classNames from 'classnames'; import React, { useEffect } from 'react'; @@ -31,6 +28,8 @@ import { import { getNodeResultDetail } from '@/services/secretpad/NodeController'; import { getModel, Model, useModel } from '@/util/valtio-helper'; +import { DataSourceType } from '../data-source-list/type'; + import { PreviewGraphComponents } from './graph'; import { FullscreenGraphModalComponent } from './graph-fullscreen-modal'; import style from './index.less'; @@ -104,26 +103,35 @@ export const ResultDetailsDrawer: React.FC = () => {
{(nodeResultsVO?.pullFromTeeStatus === ResultTableState.SUCCESS || nodeResultsVO?.pullFromTeeStatus === '') && ( - // - - // + + )} {nodeResultsVO.pullFromTeeStatus === ResultTableState.FAILED && ( - + + + )}
) : ( diff --git a/apps/platform/src/modules/result-manager/result-manager.view.tsx b/apps/platform/src/modules/result-manager/result-manager.view.tsx index 773b1cb..a1dd87e 100644 --- a/apps/platform/src/modules/result-manager/result-manager.view.tsx +++ b/apps/platform/src/modules/result-manager/result-manager.view.tsx @@ -1,5 +1,5 @@ import { SearchOutlined } from '@ant-design/icons'; -import { Input, Table, Typography, Button, Badge, Space } from 'antd'; +import { Input, Table, Typography, Button, Badge, Space, Tooltip } from 'antd'; import type { FilterValue, SorterResult } from 'antd/es/table/interface'; import { parse } from 'query-string'; import { useEffect, type ChangeEvent } from 'react'; @@ -16,6 +16,8 @@ import { } from '@/modules/result-details/result-details-drawer'; import { getModel, Model, useModel } from '@/util/valtio-helper'; +import { DataSourceType } from '../data-source-list/type'; + import { BatchDeleteModal } from './batch-delete/batch-delete.view'; import styles from './index.less'; import type { TableType } from './result-manager.protocol'; @@ -50,13 +52,22 @@ export const ResultManagerComponent = () => { )} */} - + + {/* + + ); } else if (record?.pullFromTeeStatus === ResultTableState.FAILED) { return ( @@ -172,7 +192,7 @@ export const ResultManagerComponent = () => { render: (gmtCreate: string) => ( {formatTimestamp(gmtCreate as string)} diff --git a/apps/platform/src/pages/edge.tsx b/apps/platform/src/pages/edge.tsx index bdb99d2..e2dc38b 100644 --- a/apps/platform/src/pages/edge.tsx +++ b/apps/platform/src/pages/edge.tsx @@ -3,6 +3,7 @@ import { parse } from 'query-string'; import { useEffect } from 'react'; import { useLocation } from 'umi'; +import { ReactComponent as DataSource } from '@/assets/data-source.svg'; import { ReactComponent as DataManager } from '@/assets/jiaochabiao.svg'; import { ReactComponent as CooperativeNode } from '@/assets/join-node.svg'; import { ReactComponent as projectManager } from '@/assets/project-manager.svg'; @@ -10,6 +11,7 @@ import { ReactComponent as ResultManager } from '@/assets/resultmanager.svg'; import { ReactComponent as Workbench } from '@/assets/workbench.svg'; import { CooperativeNodeListComponent } from '@/modules/cooperative-node-list'; import { DataManagerComponent } from '@/modules/data-manager/data-manager.view'; +import { DataSourceListComponent } from '@/modules/data-source-list'; import { HomeLayout } from '@/modules/layout/home-layout'; import { HomeLayoutService } from '@/modules/layout/home-layout/home-layout.service'; import { ManagementLayoutComponent } from '@/modules/layout/management-layout'; @@ -32,6 +34,12 @@ const menuItems: { component: , key: 'workbench', }, + { + label: '数据源管理', + icon: , + component: , + key: 'data-source', + }, { label: '数据管理', icon: , diff --git a/apps/platform/src/pages/new-node.tsx b/apps/platform/src/pages/new-node.tsx index 4f5ef44..5908072 100644 --- a/apps/platform/src/pages/new-node.tsx +++ b/apps/platform/src/pages/new-node.tsx @@ -1,27 +1,38 @@ import Icon from '@ant-design/icons'; import { parse } from 'query-string'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useLocation } from 'umi'; +import { ReactComponent as DataSource } from '@/assets/data-source.svg'; import { ReactComponent as DataManager } from '@/assets/jiaochabiao.svg'; import { ReactComponent as CooperativeNode } from '@/assets/join-node.svg'; import { ReactComponent as ResultManager } from '@/assets/resultmanager.svg'; import { CooperativeNodeListComponent } from '@/modules/cooperative-node-list'; import { DataManagerComponent } from '@/modules/data-manager/data-manager.view'; +import { DataSourceListComponent } from '@/modules/data-source-list'; import { HomeLayout } from '@/modules/layout/home-layout'; import { HomeLayoutService } from '@/modules/layout/home-layout/home-layout.service'; import { ManagementLayoutComponent } from '@/modules/layout/management-layout'; import { MessageService } from '@/modules/message-center/message.service'; +import { MyNodeService } from '@/modules/my-node/my-node.service'; import { NodeService } from '@/modules/node'; import { ResultManagerComponent } from '@/modules/result-manager/result-manager.view'; import { useModel } from '@/util/valtio-helper'; -const menuItems: { +type MenuItem = { label: string; icon: React.ReactNode; component: React.ReactNode; key: string; -}[] = [ +}; + +const defaultMenuItems: MenuItem[] = [ + { + label: '数据源管理', + icon: , + component: , + key: 'data-source', + }, { label: '数据管理', icon: , @@ -41,12 +52,17 @@ const menuItems: { key: 'result', }, ]; + const NodePage = () => { const { search } = useLocation(); const { nodeId } = parse(search); const homeLayoutService = useModel(HomeLayoutService); const messageService = useModel(MessageService); const nodeService = useModel(NodeService); + const myNodeService = useModel(MyNodeService); + + const [menuItems, setMenuItems] = useState(defaultMenuItems); + const [defaultTabKey, setDefaultTabKey] = useState('data-source'); useEffect(() => { const getNodeList = async () => { @@ -69,12 +85,32 @@ const NodePage = () => { // 获取未处理消息数量 getMessageTotal(); }, []); + + useEffect(() => { + const getNodeInfo = async () => { + await myNodeService.getNodeInfo(nodeId as string); + + /** + * 遇到 tee 节点,先屏蔽 数据源管理 + * 原因:tee 暂不支持 oss 数据源管理 + */ + if ( + myNodeService.nodeInfo.nodeId === 'tee' && + myNodeService.nodeInfo.type === 'embedded' + ) { + const items = menuItems.filter((item) => item.key !== 'data-source'); + + setDefaultTabKey('data-management'); + setMenuItems(items); + } + }; + + getNodeInfo(); + }, [nodeId]); + return ( - + ); }; diff --git a/apps/platform/src/services/secretpad/AuthController.ts b/apps/platform/src/services/secretpad/AuthController.ts index 7bee60a..a1936c0 100644 --- a/apps/platform/src/services/secretpad/AuthController.ts +++ b/apps/platform/src/services/secretpad/AuthController.ts @@ -23,21 +23,9 @@ export async function login(body?: API.LoginRequest, options?: { [key: string]: @author lihaixin @date 2023/12/15 POST /api/logout */ -export async function logout( - params: { - // query - /** http servlet request */ - request?: API.HttpServletRequest; - }, - options?: { [key: string]: any }, -) { +export async function logout(options?: { [key: string]: any }) { return request('/api/logout', { method: 'POST', - params: { - ...params, - request: undefined, - ...params['request'], - }, ...(options || {}), }); } diff --git a/apps/platform/src/services/secretpad/DataController.ts b/apps/platform/src/services/secretpad/DataController.ts index cb5cdd3..8d32730 100644 --- a/apps/platform/src/services/secretpad/DataController.ts +++ b/apps/platform/src/services/secretpad/DataController.ts @@ -25,11 +25,6 @@ export async function createData( @param request download data request POST /api/v1alpha1/data/download */ export async function download( - params: { - // query - /** http servlet response */ - response?: API.HttpServletResponse; - }, body?: API.DownloadDataRequest, options?: { [key: string]: any }, ) { @@ -38,11 +33,6 @@ export async function download( headers: { 'Content-Type': 'application/json', }, - params: { - ...params, - response: undefined, - ...params['response'], - }, data: body, ...(options || {}), }); diff --git a/apps/platform/src/services/secretpad/DataSourceController.ts b/apps/platform/src/services/secretpad/DataSourceController.ts new file mode 100644 index 0000000..3888ebb --- /dev/null +++ b/apps/platform/src/services/secretpad/DataSourceController.ts @@ -0,0 +1,72 @@ +/* eslint-disable */ +// 该文件由 OneAPI 自动生成,请勿手动修改! +import request from 'umi-request'; + +/** 此处后端没有提供注释 POST /api/v1alpha1/datasource/create */ +export async function create( + body?: API.CreateDatasourceRequest, + options?: { [key: string]: any }, +) { + return request( + '/api/v1alpha1/datasource/create', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} + +/** delete POST /api/v1alpha1/datasource/delete */ +export async function deleteUsingPOST( + body?: API.DeleteDatasourceRequest, + options?: { [key: string]: any }, +) { + return request('/api/v1alpha1/datasource/delete', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }); +} + +/** 此处后端没有提供注释 POST /api/v1alpha1/datasource/detail */ +export async function detail( + body?: API.DatasourceDetailRequest, + options?: { [key: string]: any }, +) { + return request( + '/api/v1alpha1/datasource/detail', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} + +/** 此处后端没有提供注释 POST /api/v1alpha1/datasource/list */ +export async function list( + body?: API.DatasourceListRequest, + options?: { [key: string]: any }, +) { + return request( + '/api/v1alpha1/datasource/list', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} diff --git a/apps/platform/src/services/secretpad/DatatableController.ts b/apps/platform/src/services/secretpad/DatatableController.ts index ae79d99..23a5990 100644 --- a/apps/platform/src/services/secretpad/DatatableController.ts +++ b/apps/platform/src/services/secretpad/DatatableController.ts @@ -2,6 +2,24 @@ // 该文件由 OneAPI 自动生成,请勿手动修改! import request from 'umi-request'; +/** Create datatable api +@param request create datatable request +@return + POST /api/v1alpha1/datatable/create */ +export async function createDataTable( + body?: API.CreateDatatableRequest, + options?: { [key: string]: any }, +) { + return request('/api/v1alpha1/datatable/create', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }); +} + /** Delete datable api @param request delete datatable request @return successful SecretPadResponse with null data diff --git a/apps/platform/src/services/secretpad/ProjectController.ts b/apps/platform/src/services/secretpad/ProjectController.ts index 0e277ba..1686632 100644 --- a/apps/platform/src/services/secretpad/ProjectController.ts +++ b/apps/platform/src/services/secretpad/ProjectController.ts @@ -277,6 +277,27 @@ export async function listProject(options?: { [key: string]: any }) { }); } +/** Update project schema api +@param request update project request +@return successful SecretPadResponse with null data + POST /api/v1alpha1/project/datasource/list */ +export async function projectGraphDomainDataSourceList( + body?: API.GetProjectGraphDomainDataSourceRequest, + options?: { [key: string]: any }, +) { + return request( + '/api/v1alpha1/project/datasource/list', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} + /** Stop project job api @param request stop project job task request @return successful SecretPadResponse with null data diff --git a/apps/platform/src/services/secretpad/index.ts b/apps/platform/src/services/secretpad/index.ts index 92c5dee..9bfa3f2 100644 --- a/apps/platform/src/services/secretpad/index.ts +++ b/apps/platform/src/services/secretpad/index.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -// API 更新时间:2024-04-29 18:01:44 -// API 唯一标识:662f7008734197dac4a0fb67 +// API 更新时间:2024-06-04 13:57:14 +// API 唯一标识:665eacba54bc56bcae52a03d // 该文件由 OneAPI 自动生成,请勿手动修改! import * as ApprovalController from './ApprovalController'; @@ -9,6 +9,7 @@ import * as CenterDataSyncController from './CenterDataSyncController'; import * as CloudLogController from './CloudLogController'; import * as ComponentVersionController from './ComponentVersionController'; import * as DataController from './DataController'; +import * as DataSourceController from './DataSourceController'; import * as DataSyncController from './DataSyncController'; import * as DatatableController from './DatatableController'; import * as FeatureDatasourceController from './FeatureDatasourceController'; @@ -33,6 +34,7 @@ export default { CloudLogController, ComponentVersionController, DataController, + DataSourceController, DataSyncController, DatatableController, FeatureDatasourceController, diff --git a/apps/platform/src/services/secretpad/typings.d.ts b/apps/platform/src/services/secretpad/typings.d.ts index e842a64..93186b3 100644 --- a/apps/platform/src/services/secretpad/typings.d.ts +++ b/apps/platform/src/services/secretpad/typings.d.ts @@ -84,6 +84,7 @@ declare namespace API { /** Task logs */ logs?: Array; config?: boolean; + nodeParties?: Array; } interface ComponentVersion { @@ -121,10 +122,42 @@ manipulate, derived from the value returned by the back end in the uplink mouth tableName?: string; /** Datatable description */ description?: string; + datasourceType?: string; + datasourceName?: string; /** Datatable schema */ datatableSchema?: Array; } + interface CreateDatasourceRequest { + nodeId?: string; + type?: string; + name?: string; + dataSourceInfo: DataSourceInfo; + } + + interface CreateDatasourceVO { + datasourceId?: string; + } + + interface CreateDatatableRequest { + /** node ID */ + nodeId?: string; + /** table name */ + datatableName?: string; + /** datasource id */ + datasourceId?: string; + /** datasource name */ + datasourceName?: string; + /** datatable type */ + datasourceType?: string; + /** table description */ + desc?: string; + /** table url */ + relativeUri?: string; + /** table columns */ + columns?: Array; + } + interface CreateFeatureDatasourceRequest { nodeId?: string; featureTableName?: string; @@ -132,6 +165,7 @@ manipulate, derived from the value returned by the back end in the uplink mouth desc?: string; url?: string; columns?: Array; + datasourceId?: string; } interface CreateGraphRequest { @@ -200,7 +234,80 @@ manipulate, derived from the value returned by the back end in the uplink mouth type DataResourceTypeEnum = 'NODE_ID' | 'PROJECT_ID'; - type DataSourceTypeEnum = 'HTTP' | 'CSV'; + type DataSource = Record; + + type DataSourceConfig = Record; + + type DataSourceInfo = Record; + + type DataSourceTypeEnum = 'HTTP' | 'OSS' | 'LOCAL' | 'MYSQL'; + + type DataTableTypeEnum = 'HTTP' | 'CSV'; + + interface DatasourceDetailRequest { + nodeId?: string; + datasourceId?: string; + type?: string; + } + + interface DatasourceDetailVO { + nodeId?: string; + datasourceId?: string; + name?: string; + type?: string; + status?: string; + info?: OssDatasourceInfo; + } + + type DatasourceErrorCode = + | 202012501 + | 'DATA_SOURCE_ENDPOINT_CONNECT_FAIL' + | 202012502 + | 'DATA_SOURCE_CREATE_FAIL' + | 202012503 + | 'DATA_SOURCE_BUCKET_NOT_EXIST' + | 202012504 + | 'DATA_SOURCE_CREDENTIALS_INVALID' + | 202012506 + | 'DATA_SOURCE_BUCKET_NOT_MATCH_ENDPOINT' + | 202012507 + | 'DATA_SOURCE_ENDPOINT_API_PORT_ERROR' + | 202012508 + | 'DATASOURCE_UNKNOWN_EXCEPTION' + | 202012509 + | 'DATA_SOURCE_DELETE_FAIL' + | 202012505 + | 'QUERY_DATASOURCE_FAILED'; + + interface DatasourceListInfo { + nodeId?: string; + datasourceId?: string; + name?: string; + type?: string; + status?: string; + relatedDatas?: Array; + } + + interface DatasourceListRequest { + /** page num default 1 */ + page?: number; + /** page size default 10 */ + size?: number; + /** sort,property,property(,ASC|DESC) "createdDate,desc" */ + sort?: Record; + nodeId?: string; + name?: string; + status?: string; + types?: Array; + } + + interface DatasourceListVO { + pageSize?: number; + pageNum?: number; + total?: number; + totalPage?: number; + infos?: Array; + } type DatatableErrorCode = | 202011301 @@ -249,6 +356,10 @@ manipulate, derived from the value returned by the back end in the uplink mouth pushToTeeErrMsg?: string; /** The data source id which it belongs to */ datasourceId?: string; + /** The data source type which it belongs to */ + datasourceType?: string; + /** The data source name which it belongs to */ + datasourceName?: string; /** Relative uri */ relativeUri?: string; /** Datatable type */ @@ -268,6 +379,12 @@ manipulate, derived from the value returned by the back end in the uplink mouth projectNodesInfo?: ProjectNodesInfo; } + interface DeleteDatasourceRequest { + nodeId?: string; + datasourceId?: string; + type?: string; + } + interface DeleteDatatableRequest { /** Node id */ nodeId?: string; @@ -347,8 +464,13 @@ manipulate, derived from the value returned by the back end in the uplink mouth edges?: Array; /** Graph max parallelism */ maxParallelism?: number; + dataSourceConfig?: Array; } + type FullUpdateGraphRequest$GraphDataSourceConfig = Record; + + type FullUpdateGraphRequestGraphDataSourceConfig = Record; + interface GetComponentRequest { /** app of the component, it can not be blank */ app?: string; @@ -396,6 +518,11 @@ manipulate, derived from the value returned by the back end in the uplink mouth type?: string; } + interface GetProjectGraphDomainDataSourceRequest { + /** Project id */ + projectId?: string; + } + interface GetProjectGraphRequest { /** Project id, it can not be blank */ projectId?: string; @@ -435,6 +562,8 @@ manipulate, derived from the value returned by the back end in the uplink mouth projectId?: string; } + type GraphDataSourceConfig = Record; + interface GraphDetailVO { /** Project id */ projectId?: string; @@ -448,8 +577,14 @@ manipulate, derived from the value returned by the back end in the uplink mouth edges?: Array; /** Graph max parallelism */ maxParallelism?: number; + /** data source configuration */ + dataSourceConfig?: Array; } + type GraphDetailVO$DataSourceConfig = Record; + + type GraphDetailVODataSourceConfig = Record; + interface GraphEdge { /** Edge id */ edgeId?: string; @@ -485,7 +620,11 @@ manipulate, derived from the value returned by the back end in the uplink mouth | 202011710 | 'GRAPH_DEPENDENT_NODE_NOT_RUN' | 202011711 - | 'GRAPH_NODE_ROUTE_NOT_EXISTS'; + | 'GRAPH_NODE_ROUTE_NOT_EXISTS' + | 202011712 + | 'GRAPH_NOT_OWNER_CANNOT_UPDATE' + | 202011713 + | 'NON_OUR_CREATION_CAN_VIEWED'; type GraphJobStatus = 'RUNNING' | 'STOPPED' | 'SUCCEED' | 'FAILED'; @@ -518,7 +657,16 @@ manipulate, derived from the value returned by the back end in the uplink mouth interface GraphNodeCloudLogsRequest { /** Project id, it can not be blank */ projectId?: string; + /** the graph nodeId */ graphNodeId?: string; + /** the graph node jobId */ + jobId?: string; + /** the graph node taskId */ + taskId?: string; + /** ture:query node running parties false:not query node running parties */ + queryParties?: boolean; + /** the requester nodeId */ + nodeId?: string; } interface GraphNodeDetail { @@ -546,6 +694,8 @@ manipulate, derived from the value returned by the back end in the uplink mouth taskId?: string; /** Project result base view object list */ results?: Array; + /** the graph node running parties */ + parties?: Array; } interface GraphNodeInfo { @@ -632,6 +782,8 @@ manipulate, derived from the value returned by the back end in the uplink mouth jobId?: string; /** Graph node task status */ status?: GraphNodeTaskStatus; + /** Graph node job parties */ + parties?: Array; } interface GraphNodeTaskLogsVO { @@ -672,7 +824,9 @@ manipulate, derived from the value returned by the back end in the uplink mouth | 202011904 | 'PROJECT_JOB_DELETE_ERROR' | 202011905 - | 'PROJECT_JOB_CLOUD_LOG_ERROR'; + | 'PROJECT_JOB_CLOUD_LOG_ERROR' + | 202011906 + | 'PROJECT_JOB_NODE_PERMISSION_ERROR'; type KusciaGrpcErrorCode = 202012101 | 'RPC_ERROR'; @@ -902,8 +1056,7 @@ waiting approved interface ModelPartyPathResponse { nodeId?: string; nodeName?: string; - dataSource?: string; - dataSourcePath?: string; + dataSources?: Array; } type ModelStatsEnum = @@ -1067,6 +1220,11 @@ waiting approved routeType?: string; } + interface NodeSimpleInfo { + nodeId?: string; + nodeName?: string; + } + interface NodeTokenRequest { /** nodeId */ nodeId?: string; @@ -1197,6 +1355,15 @@ result management list interface */ any >; + interface OssDatasourceInfo { + endpoint?: string; + bucket?: string; + prefix?: string; + ak?: string; + sk?: string; + storageType?: string; + } + interface P2pCreateNodeRequest { /** Node name, the value cannot be empty and can be the same */ name?: string; @@ -1334,7 +1501,19 @@ result management list interface */ | 202011515 | 'PROJECT_SERVING_NOT_DISCARD' | 202011516 - | 'PROJECT_FEATURE_TABLE_NOT_EXISTS'; + | 'PROJECT_FEATURE_TABLE_NOT_EXISTS' + | 202011517 + | 'NON_OUR_CREATION_CAN_VIEWED'; + + interface ProjectGraphDomainDataSourceVO { + nodeId?: string; + nodeName?: string; + dataSources?: Array; + } + + type ProjectGraphDomainDataSourceVO$DataSource = Record; + + type ProjectGraphDomainDataSourceVODataSource = Record; interface ProjectGraphOutputVO { /** graphId */ @@ -1602,6 +1781,11 @@ result management list interface */ data?: CloudGraphNodeTaskLogsVO; } + interface SecretPadResponse_CreateDatasourceVO_ { + status?: SecretPadResponseSecretPadResponseStatus; + data?: CreateDatasourceVO; + } + interface SecretPadResponse_CreateGraphVO_ { status?: SecretPadResponseSecretPadResponseStatus; data?: CreateGraphVO; @@ -1612,6 +1796,16 @@ result management list interface */ data?: CreateProjectVO; } + interface SecretPadResponse_DatasourceDetailVO_ { + status?: SecretPadResponseSecretPadResponseStatus; + data?: DatasourceDetailVO; + } + + interface SecretPadResponse_DatasourceListVO_ { + status?: SecretPadResponseSecretPadResponseStatus; + data?: DatasourceListVO; + } + interface SecretPadResponse_DatatableListVO_ { status?: SecretPadResponseSecretPadResponseStatus; data?: DatatableListVO; @@ -1787,6 +1981,11 @@ result management list interface */ data?: ServingDetailVO; } + interface SecretPadResponse_Set_ProjectGraphDomainDataSourceVO__ { + status?: SecretPadResponseSecretPadResponseStatus; + data?: Array; + } + interface SecretPadResponse_StartGraphVO_ { status?: SecretPadResponseSecretPadResponseStatus; data?: StartGraphVO; @@ -1831,6 +2030,8 @@ result management list interface */ graphId?: string; /** Graph node id list, it can not be empty */ nodes?: Array; + /** breakpoint resuming training flag true:yes,false:no */ + breakpoint?: boolean; } interface StartGraphVO { diff --git a/packages/dag/src/types/index.ts b/packages/dag/src/types/index.ts index b4cf59d..ae6e88f 100644 --- a/packages/dag/src/types/index.ts +++ b/packages/dag/src/types/index.ts @@ -31,6 +31,10 @@ export type GraphNode = { isOpaque?: boolean; isHighlighted?: boolean; isContinueRun?: boolean; + nodeParties?: { + nodeId: string; + nodeName: string; + }[]; }; };