| @@ -8,7 +8,7 @@ | |||||
| "build": "max build", | "build": "max build", | ||||
| "deploy": "npm run build && npm run gh-pages", | "deploy": "npm run build && npm run gh-pages", | ||||
| "dev": "npm run start:dev", | "dev": "npm run start:dev", | ||||
| "dev-no-sso": "NO_SSO=true npm run start:dev", | |||||
| "dev-no-sso": "cross-env NO_SSO=true npm run start:dev", | |||||
| "docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./", | "docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./", | ||||
| "docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build", | "docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build", | ||||
| "docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up", | "docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up", | ||||
| @@ -1,3 +1,4 @@ | |||||
| import { formatEnum } from '@/utils/format'; | |||||
| import { Typography } from 'antd'; | import { Typography } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import './index.less'; | import './index.less'; | ||||
| @@ -7,8 +8,12 @@ type FormInfoProps = { | |||||
| value?: any; | value?: any; | ||||
| /** 如果 `value` 是对象时,取对象的哪个属性作为值 */ | /** 如果 `value` 是对象时,取对象的哪个属性作为值 */ | ||||
| valuePropName?: string; | valuePropName?: string; | ||||
| /** 是否是多行 */ | |||||
| multiline?: boolean; | |||||
| /** 是否是多行文本 */ | |||||
| textArea?: boolean; | |||||
| /** 是否是下拉框 */ | |||||
| select?: boolean; | |||||
| /** 下拉框数据 */ | |||||
| options?: { label: string; value: any }[]; | |||||
| /** 自定义类名 */ | /** 自定义类名 */ | ||||
| className?: string; | className?: string; | ||||
| /** 自定义样式 */ | /** 自定义样式 */ | ||||
| @@ -18,20 +23,34 @@ type FormInfoProps = { | |||||
| /** | /** | ||||
| * 模拟禁用的输入框,但是内容超长时,hover 时显示所有内容 | * 模拟禁用的输入框,但是内容超长时,hover 时显示所有内容 | ||||
| */ | */ | ||||
| function FormInfo({ value, valuePropName, className, style, multiline = false }: FormInfoProps) { | |||||
| const data = value && typeof value === 'object' && valuePropName ? value[valuePropName] : value; | |||||
| function FormInfo({ | |||||
| value, | |||||
| valuePropName, | |||||
| className, | |||||
| select, | |||||
| options, | |||||
| style, | |||||
| textArea = false, | |||||
| }: FormInfoProps) { | |||||
| let data = value; | |||||
| if (value && typeof value === 'object' && valuePropName) { | |||||
| data = value[valuePropName]; | |||||
| } else if (select === true && options) { | |||||
| data = formatEnum(options)(value); | |||||
| } | |||||
| return ( | return ( | ||||
| <div | <div | ||||
| className={classNames( | className={classNames( | ||||
| 'form-info', | 'form-info', | ||||
| { | { | ||||
| 'form-info--multiline': multiline, | |||||
| 'form-info--multiline': textArea, | |||||
| }, | }, | ||||
| className, | className, | ||||
| )} | )} | ||||
| style={style} | style={style} | ||||
| > | > | ||||
| <Typography.Paragraph ellipsis={multiline ? false : { tooltip: data }}> | |||||
| <Typography.Paragraph ellipsis={textArea ? false : { tooltip: data }}> | |||||
| {data} | {data} | ||||
| </Typography.Paragraph> | </Typography.Paragraph> | ||||
| </div> | </div> | ||||
| @@ -15,11 +15,11 @@ import { useSnapshot } from 'umi'; | |||||
| // 获取资源规格 | // 获取资源规格 | ||||
| export function useComputingResource() { | export function useComputingResource() { | ||||
| const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); | ||||
| const computingResourceSnap = useSnapshot(computingResourceState); | |||||
| const snap = useSnapshot(computingResourceState); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (computingResourceSnap.computingResource.length > 0) { | |||||
| setResourceStandardList(computingResourceSnap.computingResource as ComputingResource[]); | |||||
| if (snap.computingResource.length > 0) { | |||||
| setResourceStandardList(snap.computingResource as ComputingResource[]); | |||||
| } else { | } else { | ||||
| getComputingResource(); | getComputingResource(); | ||||
| } | } | ||||
| @@ -83,7 +83,7 @@ function AutoMLInstance() { | |||||
| const setupSSE = (name: string, namespace: string) => { | const setupSSE = (name: string, namespace: string) => { | ||||
| let { origin } = location; | let { origin } = location; | ||||
| if (process.env.NODE_ENV === 'development') { | if (process.env.NODE_ENV === 'development') { | ||||
| origin = 'http://172.20.32.181:31213'; | |||||
| origin = 'http://172.20.32.197:31213'; | |||||
| } | } | ||||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | ||||
| const evtSource = new EventSource( | const evtSource = new EventSource( | ||||
| @@ -67,8 +67,8 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) { | |||||
| message: '请输入镜像Tag', | message: '请输入镜像Tag', | ||||
| }, | }, | ||||
| { | { | ||||
| pattern: /^[a-zA-Z0-9_-]*$/, | |||||
| message: '只支持字母、数字、下划线(_)、中横线(-)', | |||||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||||
| message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| @@ -135,7 +135,7 @@ function LogGroup({ | |||||
| const setupSockect = () => { | const setupSockect = () => { | ||||
| let { host } = location; | let { host } = location; | ||||
| if (process.env.NODE_ENV === 'development') { | if (process.env.NODE_ENV === 'development') { | ||||
| host = '172.20.32.181:31213'; | |||||
| host = '172.20.32.197:31213'; | |||||
| } | } | ||||
| const socket = new WebSocket( | const socket = new WebSocket( | ||||
| `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | ||||
| @@ -1,7 +1,7 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | ||||
| import LogList from '@/pages/Experiment/components/LogList'; | import LogList from '@/pages/Experiment/components/LogList'; | ||||
| import { getExperimentInsReq } from '@/services/autoML'; | |||||
| import { getRayInsReq } from '@/services/hyperParameter'; | |||||
| import { NodeStatus } from '@/types'; | import { NodeStatus } from '@/types'; | ||||
| import { parseJsonText } from '@/utils'; | import { parseJsonText } from '@/utils'; | ||||
| import { safeInvoke } from '@/utils/functional'; | import { safeInvoke } from '@/utils/functional'; | ||||
| @@ -22,12 +22,11 @@ enum TabKeys { | |||||
| History = 'history', | History = 'history', | ||||
| } | } | ||||
| function AutoMLInstance() { | |||||
| function HyperParameterInstance() { | |||||
| const [activeTab, setActiveTab] = useState<string>(TabKeys.Params); | const [activeTab, setActiveTab] = useState<string>(TabKeys.Params); | ||||
| const [autoMLInfo, setAutoMLInfo] = useState<HyperParameterData | undefined>(undefined); | |||||
| const [experimentInfo, setExperimentInfo] = useState<HyperParameterData | undefined>(undefined); | |||||
| const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined); | const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined); | ||||
| const params = useParams(); | const params = useParams(); | ||||
| // const autoMLId = safeInvoke(Number)(params.autoMLId); | |||||
| const instanceId = safeInvoke(Number)(params.id); | const instanceId = safeInvoke(Number)(params.id); | ||||
| const evtSourceRef = useRef<EventSource | null>(null); | const evtSourceRef = useRef<EventSource | null>(null); | ||||
| @@ -42,14 +41,14 @@ function AutoMLInstance() { | |||||
| // 获取实验实例详情 | // 获取实验实例详情 | ||||
| const getExperimentInsInfo = async (isStatusDetermined: boolean) => { | const getExperimentInsInfo = async (isStatusDetermined: boolean) => { | ||||
| const [res] = await to(getExperimentInsReq(instanceId)); | |||||
| const [res] = await to(getRayInsReq(instanceId)); | |||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const info = res.data as AutoMLInstanceData; | const info = res.data as AutoMLInstanceData; | ||||
| const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; | const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; | ||||
| // 解析配置参数 | // 解析配置参数 | ||||
| const paramJson = parseJsonText(param); | const paramJson = parseJsonText(param); | ||||
| if (paramJson) { | if (paramJson) { | ||||
| setAutoMLInfo(paramJson); | |||||
| setExperimentInfo(paramJson); | |||||
| } | } | ||||
| // 这个接口返回的状态有延时,SSE 返回的状态是最新的 | // 这个接口返回的状态有延时,SSE 返回的状态是最新的 | ||||
| @@ -83,7 +82,7 @@ function AutoMLInstance() { | |||||
| const setupSSE = (name: string, namespace: string) => { | const setupSSE = (name: string, namespace: string) => { | ||||
| let { origin } = location; | let { origin } = location; | ||||
| if (process.env.NODE_ENV === 'development') { | if (process.env.NODE_ENV === 'development') { | ||||
| origin = 'http://172.20.32.181:31213'; | |||||
| origin = 'http://172.20.32.197:31213'; | |||||
| } | } | ||||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | ||||
| const evtSource = new EventSource( | const evtSource = new EventSource( | ||||
| @@ -142,7 +141,7 @@ function AutoMLInstance() { | |||||
| children: ( | children: ( | ||||
| <HyperParameterBasic | <HyperParameterBasic | ||||
| className={styles['auto-ml-instance__basic']} | className={styles['auto-ml-instance__basic']} | ||||
| info={autoMLInfo} | |||||
| info={experimentInfo} | |||||
| runStatus={instanceInfo?.nodeStatus} | runStatus={instanceInfo?.nodeStatus} | ||||
| isInstance | isInstance | ||||
| /> | /> | ||||
| @@ -189,7 +188,7 @@ function AutoMLInstance() { | |||||
| children: ( | children: ( | ||||
| <ExperimentHistory | <ExperimentHistory | ||||
| fileUrl={instanceInfo?.run_history_path} | fileUrl={instanceInfo?.run_history_path} | ||||
| isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification} | |||||
| isClassification={experimentInfo?.task_type === AutoMLTaskType.Classification} | |||||
| /> | /> | ||||
| ), | ), | ||||
| }, | }, | ||||
| @@ -212,4 +211,4 @@ function AutoMLInstance() { | |||||
| ); | ); | ||||
| } | } | ||||
| export default AutoMLInstance; | |||||
| export default HyperParameterInstance; | |||||
| @@ -109,7 +109,7 @@ function ExecuteConfig() { | |||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | <Form.Item | ||||
| label="代码配置" | label="代码配置" | ||||
| name="code" | |||||
| name="code_config" | |||||
| rules={[ | rules={[ | ||||
| { | { | ||||
| validator: requiredValidator, | validator: requiredValidator, | ||||
| @@ -12,7 +12,7 @@ export enum OperationType { | |||||
| export type FormData = { | export type FormData = { | ||||
| name: string; // 实验名称 | name: string; // 实验名称 | ||||
| description: string; // 实验描述 | description: string; // 实验描述 | ||||
| code: ParameterInputObject; // 代码 | |||||
| code_config: ParameterInputObject; // 代码 | |||||
| dataset: ParameterInputObject; // 数据集 | dataset: ParameterInputObject; // 数据集 | ||||
| model: ParameterInputObject; // 模型 | model: ParameterInputObject; // 模型 | ||||
| image: ParameterInputObject; // 镜像 | image: ParameterInputObject; // 镜像 | ||||
| @@ -177,8 +177,8 @@ function MirrorCreate() { | |||||
| message: '请输入镜像Tag', | message: '请输入镜像Tag', | ||||
| }, | }, | ||||
| { | { | ||||
| pattern: /^[a-zA-Z0-9_-]*$/, | |||||
| message: '只支持字母、数字、下划线(_)、中横线(-)', | |||||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||||
| message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| @@ -247,7 +247,7 @@ function CreateServiceVersion() { | |||||
| }, | }, | ||||
| { | { | ||||
| pattern: /^[a-zA-Z0-9._-]+$/, | pattern: /^[a-zA-Z0-9._-]+$/, | ||||
| message: '版本只支持字母、数字、点、下划线、中横线', | |||||
| message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)', | |||||
| }, | }, | ||||
| ]} | ]} | ||||
| > | > | ||||
| @@ -1,6 +1,6 @@ | |||||
| import FormInfo from '@/components/FormInfo'; | import FormInfo from '@/components/FormInfo'; | ||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { Form, Input, Select, Typography } from 'antd'; | |||||
| import { Form, Input, Select } from 'antd'; | |||||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | ||||
| const meta = { | const meta = { | ||||
| @@ -24,6 +24,14 @@ export default meta; | |||||
| type Story = StoryObj<typeof meta>; | type Story = StoryObj<typeof meta>; | ||||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | ||||
| export const Primary: Story = { | |||||
| args: { | |||||
| style: { width: '200px' }, | |||||
| value: | |||||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||||
| }, | |||||
| }; | |||||
| export const InForm: Story = { | export const InForm: Story = { | ||||
| render: () => { | render: () => { | ||||
| return ( | return ( | ||||
| @@ -40,11 +48,10 @@ export const InForm: Story = { | |||||
| value: 1, | value: 1, | ||||
| showValue: '对象文本', | showValue: '对象文本', | ||||
| }, | }, | ||||
| input_text: | |||||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||||
| antd_select: 1, | |||||
| select_text: 1, | select_text: 1, | ||||
| select_large_text: 1, | |||||
| ant_input_text: | |||||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||||
| ant_select_text: 1, | |||||
| }} | }} | ||||
| > | > | ||||
| <Form.Item label="文本" name="text"> | <Form.Item label="文本" name="text"> | ||||
| @@ -54,7 +61,7 @@ export const InForm: Story = { | |||||
| <FormInfo /> | <FormInfo /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="多行文本" name="multiline_text"> | <Form.Item label="多行文本" name="multiline_text"> | ||||
| <FormInfo multiline /> | |||||
| <FormInfo textArea /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="对象" name="object_text"> | <Form.Item label="对象" name="object_text"> | ||||
| <FormInfo valuePropName="showValue" /> | <FormInfo valuePropName="showValue" /> | ||||
| @@ -62,12 +69,9 @@ export const InForm: Story = { | |||||
| <Form.Item label="无内容" name="empty_text"> | <Form.Item label="无内容" name="empty_text"> | ||||
| <FormInfo /> | <FormInfo /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="Input" name="input_text"> | |||||
| <Input disabled /> | |||||
| </Form.Item> | |||||
| <Form.Item label="Select" name="antd_select"> | |||||
| <Select | |||||
| disabled | |||||
| <Form.Item label="Select" name="select_text"> | |||||
| <FormInfo | |||||
| select | |||||
| options={[ | options={[ | ||||
| { | { | ||||
| label: | label: | ||||
| @@ -77,37 +81,11 @@ export const InForm: Story = { | |||||
| ]} | ]} | ||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="Select" name="select_text"> | |||||
| <Select | |||||
| labelRender={(props) => { | |||||
| return ( | |||||
| <div style={{ width: '100%', lineHeight: 'normal' }}> | |||||
| <Typography.Text ellipsis={{ tooltip: props.label }} style={{ margin: 0 }}> | |||||
| {props.label} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| }} | |||||
| disabled | |||||
| options={[ | |||||
| { | |||||
| label: '选择文本', | |||||
| value: 1, | |||||
| }, | |||||
| ]} | |||||
| /> | |||||
| <Form.Item label="Input" name="ant_input_text"> | |||||
| <Input disabled /> | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="Long Select" name="select_large_text"> | |||||
| <Form.Item label="Select" name="ant_select_text"> | |||||
| <Select | <Select | ||||
| labelRender={(props) => { | |||||
| return ( | |||||
| <div style={{ width: '100%', lineHeight: 'normal' }}> | |||||
| <Typography.Text ellipsis={{ tooltip: props.label }} style={{ margin: 0 }}> | |||||
| {props.label} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| }} | |||||
| disabled | disabled | ||||
| options={[ | options={[ | ||||
| { | { | ||||
| @@ -122,10 +122,12 @@ export const formatBoolean = (value: boolean): string => { | |||||
| return value ? '是' : '否'; | return value ? '是' : '否'; | ||||
| }; | }; | ||||
| type FormatEnum = (value: string | number) => string; | |||||
| type FormatEnumFunc = (value: string | number) => string; | |||||
| // 格式化枚举 | // 格式化枚举 | ||||
| export const formatEnum = (options: { value: string | number; label: string }[]): FormatEnum => { | |||||
| export const formatEnum = ( | |||||
| options: { value: string | number; label: string }[], | |||||
| ): FormatEnumFunc => { | |||||
| return (value: string | number) => { | return (value: string | number) => { | ||||
| const option = options.find((item) => item.value === value); | const option = options.find((item) => item.value === value); | ||||
| return option ? option.label : '--'; | return option ? option.label : '--'; | ||||