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