| @@ -1,13 +1,47 @@ | |||
| .parameter-range { | |||
| width: 300px; | |||
| &__list { | |||
| width: 100%; | |||
| max-height: 300px; | |||
| overflow-x: visible; | |||
| overflow-y: auto; | |||
| width: 360px; | |||
| &__type { | |||
| margin-bottom: 10px; | |||
| color: @text-color-secondary; | |||
| font-size: @font-size-input; | |||
| &::before { | |||
| display: inline-block; | |||
| color: @error-color; | |||
| font-size: 14px; | |||
| font-family: SimSun, sans-serif; | |||
| line-height: 1; | |||
| content: '*'; | |||
| margin-inline-end: 4px; | |||
| } | |||
| } | |||
| &__desc { | |||
| margin-bottom: 20px; | |||
| padding: 4px 8px; | |||
| color: @text-color-tertiary; | |||
| font-size: 13px; | |||
| background: rgba(62, 96, 163, 0.05); | |||
| border-radius: 6px; | |||
| } | |||
| &__button { | |||
| margin-bottom: 0; | |||
| text-align: center; | |||
| &__form { | |||
| width: 100%; | |||
| &__list { | |||
| width: 100%; | |||
| max-height: 300px; | |||
| overflow-x: visible; | |||
| overflow-y: auto; | |||
| } | |||
| &__space { | |||
| flex: none; | |||
| width: 22px; | |||
| color: @text-color-tertiary; | |||
| font-size: @font-size-input; | |||
| line-height: 32px; | |||
| text-align: center; | |||
| } | |||
| &__button { | |||
| width: 100%; | |||
| margin-bottom: 0; | |||
| text-align: center; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,16 +1,16 @@ | |||
| import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; | |||
| import { Button, Flex, Form, Input, InputNumber } from 'antd'; | |||
| import { ParameterType, getFormOptions } from '../utils'; | |||
| import { ParameterType, getFormOptions, parameterTooltip } from '../utils'; | |||
| import styles from './index.less'; | |||
| type ParameterRangeProps = { | |||
| type?: ParameterType; | |||
| type: ParameterType; | |||
| value?: any[]; | |||
| onCancel?: () => void; | |||
| onConfirm?: (value: any[]) => void; | |||
| }; | |||
| function ParameterRange({ type, value, onCancel, onConfirm }: ParameterRangeProps) { | |||
| function ParameterRange({ type, value, onConfirm }: ParameterRangeProps) { | |||
| const [form] = Form.useForm(); | |||
| const isList = type === ParameterType.Choice || type === ParameterType.Grid; | |||
| const formOptions = getFormOptions(type, value); | |||
| @@ -33,108 +33,119 @@ function ParameterRange({ type, value, onCancel, onConfirm }: ParameterRangeProp | |||
| }; | |||
| return ( | |||
| <Form | |||
| labelCol={{ flex: '70px' }} | |||
| wrapperCol={{ flex: '1' }} | |||
| labelAlign="left" | |||
| form={form} | |||
| onFinish={handleFinish} | |||
| size="middle" | |||
| autoComplete="off" | |||
| scrollToFirstError | |||
| initialValues={initialValues} | |||
| className={styles['parameter-range']} | |||
| > | |||
| {isList ? ( | |||
| <div className={styles['parameter-range__list']}> | |||
| <Form.List name="list"> | |||
| {(fields, { add, remove }) => ( | |||
| <> | |||
| {fields.map(({ key, name, ...restField }, index) => ( | |||
| <Flex key={key} align="center"> | |||
| <Form.Item | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| {...restField} | |||
| name={[name, 'value']} | |||
| rules={[{ required: true, message: '必填' }]} | |||
| > | |||
| <Input placeholder="请输入" allowClear /> | |||
| </Form.Item> | |||
| <Flex | |||
| style={{ | |||
| marginLeft: '10px', | |||
| marginBottom: '20px', | |||
| flex: 'none', | |||
| width: '66px', | |||
| }} | |||
| align="center" | |||
| > | |||
| <Button | |||
| shape="circle" | |||
| size="middle" | |||
| type="text" | |||
| disabled={fields.length === 1} | |||
| icon={<MinusCircleOutlined />} | |||
| onClick={() => remove(name)} | |||
| ></Button> | |||
| {index === fields.length - 1 && ( | |||
| <div className={styles['parameter-range']}> | |||
| <div className={styles['parameter-range__type']}>{type}</div> | |||
| <div className={styles['parameter-range__desc']}>{parameterTooltip[type]}</div> | |||
| <Form | |||
| // labelCol={{ flex: '0' }} | |||
| // wrapperCol={{ flex: '1' }} | |||
| labelAlign="left" | |||
| form={form} | |||
| onFinish={handleFinish} | |||
| size="middle" | |||
| autoComplete="off" | |||
| scrollToFirstError | |||
| initialValues={initialValues} | |||
| layout={isList ? 'horizontal' : 'inline'} | |||
| className={styles['parameter-range__form']} | |||
| > | |||
| {isList ? ( | |||
| <div className={styles['parameter-range__form__list']}> | |||
| <Form.List name="list"> | |||
| {(fields, { add, remove }) => ( | |||
| <> | |||
| {fields.map(({ key, name, ...restField }, index) => ( | |||
| <Flex key={key} align="center"> | |||
| <Form.Item | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| {...restField} | |||
| name={[name, 'value']} | |||
| rules={[{ required: true, message: '必填' }]} | |||
| > | |||
| <Input placeholder="请输入" allowClear /> | |||
| </Form.Item> | |||
| <Flex | |||
| style={{ | |||
| marginLeft: '10px', | |||
| marginBottom: '20px', | |||
| flex: 'none', | |||
| width: '66px', | |||
| }} | |||
| align="center" | |||
| > | |||
| <Button | |||
| shape="circle" | |||
| size="middle" | |||
| type="text" | |||
| onClick={() => add()} | |||
| icon={<PlusCircleOutlined />} | |||
| disabled={fields.length === 1} | |||
| icon={<MinusCircleOutlined />} | |||
| onClick={() => remove(name)} | |||
| ></Button> | |||
| )} | |||
| {index === fields.length - 1 && ( | |||
| <Button | |||
| shape="circle" | |||
| size="middle" | |||
| type="text" | |||
| onClick={() => add()} | |||
| icon={<PlusCircleOutlined />} | |||
| ></Button> | |||
| )} | |||
| </Flex> | |||
| </Flex> | |||
| </Flex> | |||
| ))} | |||
| {fields.length === 0 && ( | |||
| <Form.Item className={styles['add-weight']}> | |||
| <Button | |||
| className={styles['add-weight__button']} | |||
| color="primary" | |||
| variant="dashed" | |||
| onClick={() => add()} | |||
| block | |||
| icon={<PlusCircleOutlined />} | |||
| > | |||
| 添加 | |||
| </Button> | |||
| ))} | |||
| {fields.length === 0 && ( | |||
| <Form.Item className={styles['add-weight']}> | |||
| <Button | |||
| className={styles['add-weight__button']} | |||
| color="primary" | |||
| variant="dashed" | |||
| onClick={() => add()} | |||
| block | |||
| icon={<PlusCircleOutlined />} | |||
| > | |||
| 添加 | |||
| </Button> | |||
| </Form.Item> | |||
| )} | |||
| </> | |||
| )} | |||
| </Form.List> | |||
| </div> | |||
| ) : ( | |||
| <Flex align="start" style={{ width: '100%', marginBottom: '20px' }}> | |||
| {formOptions.map((item, index) => { | |||
| return ( | |||
| <> | |||
| <Form.Item | |||
| key={item.name} | |||
| name={item.name} | |||
| style={{ flex: 1, marginInlineEnd: 0 }} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: `必填`, | |||
| }, | |||
| ]} | |||
| > | |||
| <InputNumber style={{ width: '100%' }} placeholder={item.name} /> | |||
| </Form.Item> | |||
| )} | |||
| </> | |||
| )} | |||
| </Form.List> | |||
| </div> | |||
| ) : ( | |||
| formOptions.map((item) => { | |||
| return ( | |||
| <Form.Item | |||
| key={item.name} | |||
| label={item.label} | |||
| name={item.name} | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: `请输入${item.label}`, | |||
| }, | |||
| ]} | |||
| > | |||
| <InputNumber style={{ width: '100%' }} placeholder={`请输入${item.label}`} /> | |||
| </Form.Item> | |||
| ); | |||
| }) | |||
| )} | |||
| <Form.Item className={styles['parameter-range__button']}> | |||
| <Button type="default" htmlType="button" onClick={onCancel}> | |||
| 取消 | |||
| </Button> | |||
| <Button type="primary" htmlType="submit" style={{ marginLeft: '20px' }}> | |||
| 确定 | |||
| </Button> | |||
| </Form.Item> | |||
| </Form> | |||
| {index !== formOptions.length - 1 && ( | |||
| <span className={styles['parameter-range__form__space']}> | |||
| {index === 0 ? '-' : ' '} | |||
| </span> | |||
| )} | |||
| </> | |||
| ); | |||
| })} | |||
| </Flex> | |||
| )} | |||
| <Form.Item layout="horizontal" className={styles['parameter-range__form__button']}> | |||
| <Button type="primary" htmlType="submit" style={{ width: '100%', height: '36px' }}> | |||
| 确定 | |||
| </Button> | |||
| </Form.Item> | |||
| </Form> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -1,14 +1,23 @@ | |||
| .parameter-range { | |||
| border-radius: 18px; | |||
| box-shadow: 0px 3px 10px rgba(22, 100, 255, 0.15); | |||
| :global { | |||
| .ant-popover-inner { | |||
| padding: 20px 20px 12px; | |||
| } | |||
| .ant-popconfirm-description { | |||
| padding-top: 20px; | |||
| } | |||
| .ant-popover-content { | |||
| .ant-popover-inner { | |||
| width: 400px; | |||
| padding: 20px 20px 12px; | |||
| background-image: url(@/assets/img/popover-bg.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top left; | |||
| background-size: 100% auto; | |||
| } | |||
| .ant-popconfirm-description { | |||
| margin-top: 20px; | |||
| } | |||
| .ant-popconfirm-buttons { | |||
| display: none; | |||
| .ant-popconfirm-buttons { | |||
| display: none; | |||
| } | |||
| } | |||
| } | |||
| @@ -58,3 +67,17 @@ | |||
| } | |||
| } | |||
| } | |||
| .parameter-range-title { | |||
| color: @text-color; | |||
| font-weight: 500; | |||
| font-size: @font-size-content; | |||
| } | |||
| .parameter-range-title-icon { | |||
| color: @text-color-secondary; | |||
| &:hover { | |||
| color: @text-color; | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { isEmpty } from '@/utils'; | |||
| import { Popconfirm, Typography } from 'antd'; | |||
| import { Flex, Popconfirm, Typography } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import ParameterRange from '../ParameterRange'; | |||
| @@ -8,7 +8,7 @@ import { ParameterType } from '../utils'; | |||
| import styles from './index.less'; | |||
| type ParameterRangeProps = { | |||
| type?: ParameterType; | |||
| type: ParameterType; | |||
| value?: any[]; | |||
| onChange?: (value: any[]) => void; | |||
| }; | |||
| @@ -58,18 +58,11 @@ function PopParameterRange({ type, value, onChange }: ParameterRangeProps) { | |||
| <div ref={popconfirmRef}> | |||
| <Popconfirm | |||
| id="pop-parameter" | |||
| title="参数范围" | |||
| title={<PopconfirmTitle title="参数范围" onClose={handleCancel} />} | |||
| disabled={disabled} | |||
| description={ | |||
| <ParameterRange | |||
| type={type} | |||
| value={value} | |||
| onCancel={handleCancel} | |||
| onConfirm={handleConfirm} | |||
| ></ParameterRange> | |||
| <ParameterRange type={type} value={value} onConfirm={handleConfirm}></ParameterRange> | |||
| } | |||
| okText="确定" | |||
| cancelText="取消" | |||
| overlayClassName={styles['parameter-range']} | |||
| icon={null} | |||
| open={open} | |||
| @@ -95,4 +88,18 @@ function PopParameterRange({ type, value, onChange }: ParameterRangeProps) { | |||
| ); | |||
| } | |||
| function PopconfirmTitle({ title, onClose }: { title: string; onClose: () => void }) { | |||
| return ( | |||
| <Flex align="center" justify="space-between"> | |||
| <span className={styles['parameter-range-title']}>{title}</span> | |||
| <KFIcon | |||
| className={styles['parameter-range-title-icon']} | |||
| type="icon-guanbi" | |||
| font={17} | |||
| onClick={onClose} | |||
| /> | |||
| </Flex> | |||
| ); | |||
| } | |||
| export default PopParameterRange; | |||
| @@ -41,7 +41,7 @@ | |||
| &::before { | |||
| display: inline-block; | |||
| color: #c73131; | |||
| color: @error-color; | |||
| font-size: 14px; | |||
| font-family: SimSun, sans-serif; | |||
| line-height: 1; | |||
| @@ -38,6 +38,27 @@ export const axParameterOptions = ['fixed', 'range', 'choice'].map((name) => ({ | |||
| value: name, | |||
| })); | |||
| export const parameterTooltip: Record<ParameterType, string> = { | |||
| [ParameterType.Uniform]: '在 low 和 high 之间均匀采样浮点数', | |||
| [ParameterType.QUniform]: '在 low 和 high 之间均匀采样浮点数,四舍五入到 q 的倍数', | |||
| [ParameterType.LogUniform]: '在 low 和 high 之间均匀采样浮点数,对数空间采样', | |||
| [ParameterType.QLogUniform]: | |||
| '在 low 和 high 之间均匀采样浮点数,对数空间采样并四舍五入到 q 的倍数', | |||
| [ParameterType.Randn]: '在均值为 m,方差为 s 的正态分布中进行随机浮点数抽样', | |||
| [ParameterType.QRandn]: | |||
| '在均值为 m,方差为 s 的正态分布中进行随机浮点数抽样,四舍五入到 q 的倍数', | |||
| [ParameterType.RandInt]: '在 low(包括)到 high(不包括)之间均匀采样整数', | |||
| [ParameterType.QRandInt]: | |||
| '在 low(包括)到 high(不包括)之间均匀采样整数,四舍五入到 q 的倍数(包括 high)', | |||
| [ParameterType.LogRandInt]: '在 low(包括)到 high(不包括)之间对数空间上均匀采样整数', | |||
| [ParameterType.QLogRandInt]: | |||
| '在 low(包括)到 high(不包括)之间对数空间上均匀采样整数,并四舍五入到 q 的倍数', | |||
| [ParameterType.Choice]: '从指定的选项中采样一个选项', | |||
| [ParameterType.Grid]: '对选项进行网格搜索,每个值都将被采样', | |||
| [ParameterType.Range]: '在 low 和 high 范围内采样取值', | |||
| [ParameterType.Fixed]: '固定取值', | |||
| }; | |||
| export type ParameterData = { | |||
| label: string; | |||
| name: string; | |||
| @@ -69,12 +90,12 @@ export const getFormOptions = (type?: ParameterType, value?: number[]): Paramete | |||
| case ParameterType.Range: | |||
| return [ | |||
| { | |||
| name: 'min', | |||
| name: 'low', | |||
| label: '最小值', | |||
| value: numbers?.[0], | |||
| }, | |||
| { | |||
| name: 'max', | |||
| name: 'high', | |||
| label: '最大值', | |||
| value: numbers?.[1], | |||
| }, | |||
| @@ -85,12 +106,12 @@ export const getFormOptions = (type?: ParameterType, value?: number[]): Paramete | |||
| case ParameterType.QLogRandInt: | |||
| return [ | |||
| { | |||
| name: 'min', | |||
| name: 'low', | |||
| label: '最小值', | |||
| value: numbers?.[0], | |||
| }, | |||
| { | |||
| name: 'max', | |||
| name: 'high', | |||
| label: '最大值', | |||
| value: numbers?.[1], | |||
| }, | |||
| @@ -103,12 +124,12 @@ export const getFormOptions = (type?: ParameterType, value?: number[]): Paramete | |||
| case ParameterType.Randn: | |||
| return [ | |||
| { | |||
| name: 'mean', | |||
| name: 'm', | |||
| label: '均值', | |||
| value: numbers?.[0], | |||
| }, | |||
| { | |||
| name: 'std', | |||
| name: 's', | |||
| label: '方差', | |||
| value: numbers?.[1], | |||
| }, | |||
| @@ -116,12 +137,12 @@ export const getFormOptions = (type?: ParameterType, value?: number[]): Paramete | |||
| case ParameterType.QRandn: | |||
| return [ | |||
| { | |||
| name: 'mean', | |||
| name: 'm', | |||
| label: '均值', | |||
| value: numbers?.[0], | |||
| }, | |||
| { | |||
| name: 'std', | |||
| name: 's', | |||
| label: '方差', | |||
| value: numbers?.[1], | |||
| }, | |||