Browse Source

Merge pull request '合并dev-zw' (#189) from dev-zw into dev

dev-active_learn
cp3hnu 10 months ago
parent
commit
8fdbc6a347
35 changed files with 298 additions and 168 deletions
  1. +12
    -1
      react-ui/config/routes.ts
  2. +1
    -0
      react-ui/package.json
  3. +0
    -10
      react-ui/public/fonts/font.css
  4. +13
    -2
      react-ui/src/components/CodeConfigItem/index.tsx
  5. +0
    -3
      react-ui/src/components/IFramePage/index.tsx
  6. +1
    -1
      react-ui/src/components/ResourceSelect/index.tsx
  7. +1
    -1
      react-ui/src/pages/AutoML/Instance/index.tsx
  8. +14
    -10
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
  9. +1
    -1
      react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx
  10. +1
    -0
      react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
  11. +6
    -1
      react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx
  12. +21
    -6
      react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
  13. +21
    -3
      react-ui/src/pages/Dataset/components/AddModelModal/index.tsx
  14. +2
    -2
      react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx
  15. +2
    -2
      react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx
  16. +2
    -1
      react-ui/src/pages/Experiment/Info/index.jsx
  17. +8
    -8
      react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx
  18. +1
    -2
      react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less
  19. +17
    -2
      react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx
  20. +8
    -25
      react-ui/src/pages/Experiment/components/ViewParamsModal/index.less
  21. +41
    -9
      react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx
  22. +1
    -0
      react-ui/src/pages/Experiment/index.jsx
  23. +30
    -24
      react-ui/src/pages/Mirror/Create/index.tsx
  24. +12
    -5
      react-ui/src/pages/Mirror/Info/index.tsx
  25. +3
    -3
      react-ui/src/pages/Mirror/List/index.tsx
  26. +1
    -0
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.less
  27. +7
    -6
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  28. +29
    -25
      react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx
  29. +5
    -0
      react-ui/src/pages/Points/index.tsx
  30. +7
    -1
      react-ui/src/pages/Workspace/components/TotalStatistics/index.less
  31. +6
    -2
      react-ui/src/pages/Workspace/index.less
  32. +2
    -2
      react-ui/src/pages/Workspace/index.tsx
  33. +1
    -0
      react-ui/src/types.ts
  34. +4
    -1
      react-ui/src/utils/date.ts
  35. +17
    -9
      react-ui/src/utils/table.tsx

+ 12
- 1
react-ui/config/routes.ts View File

@@ -271,7 +271,18 @@ export default [
{
name: '镜像详情',
path: 'info/:id',
component: './Mirror/Info',
routes: [
{
name: '镜像详情',
path: '',
component: './Mirror/Info',
},
{
name: '新增镜像版本',
path: 'add-version',
component: './Mirror/Create',
},
],
},
{
name: '创建镜像',


+ 1
- 0
react-ui/package.json View File

@@ -67,6 +67,7 @@
"@types/crypto-js": "^4.2.2",
"@umijs/route-utils": "^4.0.1",
"antd": "~5.21.4",
"caniuse-lite": "~1.0.30001707",
"classnames": "^2.3.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0",


+ 0
- 10
react-ui/public/fonts/font.css View File

@@ -4,16 +4,6 @@
font-display: swap;
}

@font-face {
font-family: 'TaoBaoMaiCaiTi';
src: url('./TaoBaoMaiCaiTi-Regular.woff2') format('woff2'), /* 最优先使用 woff2 */
url('./TaoBaoMaiCaiTi-Regular.woff') format('woff'), /* 兼容性较好的 woff */
url('./TaoBaoMaiCaiTi-Regular.ttf') format('truetype'), /* ttf 作为备选 */
url('./TaoBaoMaiCaiTi-Regular.otf') format('opentype'); /* otf 作为最后选项 */
font-display: swap; /* 优化页面加载时的字体显示 */
}


@font-face {
font-family: 'DingTalk-JinBuTi';
src: url('./DingTalk-JinBuTi.woff2') format('woff2'), /* 最优先使用 woff2 */


+ 13
- 2
react-ui/src/components/CodeConfigItem/index.tsx View File

@@ -2,6 +2,7 @@ import { AvailableRange } from '@/enums';
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { Flex, Typography } from 'antd';
import classNames from 'classnames';
import { useState } from 'react';
import styles from './index.less';

type CodeConfigItemProps = {
@@ -10,6 +11,7 @@ type CodeConfigItemProps = {
};

function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
const [isEllipsis, setIsEllipsis] = useState(false);
return (
<div className={styles['code-config-item']} onClick={() => onClick?.(item)}>
<Flex justify="space-between" align="center" style={{ marginBottom: '15px' }}>
@@ -32,11 +34,20 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
</Flex>
<Typography.Paragraph
className={styles['code-config-item__url']}
ellipsis={{ rows: 2, tooltip: item.git_url }}
ellipsis={{
rows: 2,
tooltip: isEllipsis ? item.git_url : false, // 仅当省略时显示 tooltip
onEllipsis: (ellipsis) => setIsEllipsis(ellipsis),
}}
>
{item.git_url}
</Typography.Paragraph>
<div className={styles['code-config-item__branch']}>{item.git_branch}</div>
<Typography.Paragraph
className={styles['code-config-item__branch']}
ellipsis={{ tooltip: item.git_branch }}
>
{item.git_branch}
</Typography.Paragraph>
</div>
);
}


+ 0
- 3
react-ui/src/components/IFramePage/index.tsx View File

@@ -48,12 +48,9 @@ function IframePage({ type, className, style }: IframePageProps) {

useEffect(() => {
const requestIframeUrl = async () => {
setLoading(true);
const [res] = await to(getRequestAPI(type)());
if (res && res.data) {
setIframeUrl(res.data);
} else {
setLoading(false);
}
};



+ 1
- 1
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -6,7 +6,7 @@

import KFIcon from '@/components/KFIcon';
import ResourceSelectorModal, {
ResourceSelectorResponse,
type ResourceSelectorResponse,
ResourceSelectorType,
selectorTypeConfig,
} from '@/components/ResourceSelectorModal';


+ 1
- 1
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -28,7 +28,6 @@ function AutoMLInstance() {
const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | 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);

@@ -187,6 +186,7 @@ function AutoMLInstance() {
icon: <KFIcon type="icon-Trialliebiao" />,
children: (
<ExperimentHistory
calcMetrics={autoMLInfo?.scoring_functions}
fileUrl={instanceInfo?.run_history_path}
isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification}
/>


+ 14
- 10
react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx View File

@@ -8,8 +8,9 @@ import TrialStatusCell from '../TrialStatusCell';
import styles from './index.less';

type ExperimentHistoryProps = {
fileUrl?: string;
isClassification: boolean;
calcMetrics?: string; // 计算指标
fileUrl?: string; // 文件url
isClassification: boolean; // 是否是分类
};

type TableData = {
@@ -22,7 +23,7 @@ type TableData = {
althorithm?: string;
};

function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) {
function ExperimentHistory({ calcMetrics, fileUrl, isClassification }: ExperimentHistoryProps) {
const [tableData, setTableData] = useState<TableData[]>([]);
useEffect(() => {
// 获取实验运行历史记录
@@ -33,7 +34,7 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
const list: TableData[] = data.map((item) => {
return {
id: item[0]?.[0],
accuracy: item[1]?.[5]?.accuracy,
accuracy: calcMetrics ? item[1]?.[5]?.[calcMetrics] : undefined,
duration: item[1]?.[5]?.duration,
train_loss: item[1]?.[5]?.train_loss,
status: item[1]?.[2]?.['__enum__']?.split('.')?.[1],
@@ -64,12 +65,6 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
width: 80,
render: tableCellRender(false),
},
{
title: '准确率',
dataIndex: 'accuracy',
key: 'accuracy',
render: tableCellRender(true),
},
{
title: '耗时',
dataIndex: 'duration',
@@ -103,6 +98,15 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
},
];

if (calcMetrics) {
columns.splice(0, 0, {
title: `指标:${calcMetrics}`,
dataIndex: 'accuracy',
key: 'accuracy',
render: tableCellRender(true),
});
}

return (
<div className={styles['experiment-history']}>
<div className={styles['experiment-history__content']}>


+ 1
- 1
react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx View File

@@ -107,7 +107,7 @@ function ExperimentInstanceComponent({
};

if (!experimentInsList || experimentInsList.length === 0) {
return <div style={{ textAlign: 'center' }}>暂无实验实例</div>;
return <div style={{ textAlign: 'center' }}>暂无数据</div>;
}

return (


+ 1
- 0
react-ui/src/pages/AutoML/components/ExperimentList/index.tsx View File

@@ -188,6 +188,7 @@ function ExperimentList({ type }: ExperimentListProps) {
if (expanded) {
setExpandedRowKeys([record.id]);
getExperimentInsList(record.id, 0);
refreshExperimentList();
} else {
setExpandedRowKeys([]);
}


+ 6
- 1
react-ui/src/pages/CodeConfig/components/CodeConfigItem/index.tsx View File

@@ -70,7 +70,12 @@ function CodeConfigItem({ item, onClick, onEdit, onRemove }: CodeConfigItemProps
>
{item.git_url}
</Typography.Paragraph>
<div className={styles['code-config-item__branch']}>{item.git_branch}</div>
<Typography.Paragraph
className={styles['code-config-item__branch']}
ellipsis={{ tooltip: item.git_branch }}
>
{item.git_branch}
</Typography.Paragraph>
</div>
<Flex justify="space-between">
<div className={styles['code-config-item__user']}>


+ 21
- 6
react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx View File

@@ -159,27 +159,42 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
showSearch
/>
</Form.Item>
{/* <Form.Item label="集群版本" name="available_cluster">
<Select allowClear placeholder="请选择集群版本" options={clusterOptions} />
</Form.Item> */}
<Form.Item
label="数据集简介"
label="数据集描述"
name="description"
rules={[
{
required: true,
message: '请输入数据集简介',
message: '请输入数据集描述',
},
]}
>
<Input.TextArea
placeholder="请输入数据集简介"
placeholder="请输入数据集描述"
showCount
maxLength={200}
autoSize={{ minRows: 2, maxRows: 6 }}
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"


+ 21
- 3
react-ui/src/pages/Dataset/components/AddModelModal/index.tsx View File

@@ -143,23 +143,41 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
/>
</Form.Item>
<Form.Item
label="模型简介"
label="模型描述"
name="description"
rules={[
{
required: true,
message: '请输入模型简介',
message: '请输入模型描述',
},
]}
>
<Input.TextArea
placeholder="请输入模型简介"
placeholder="请输入模型描述"
maxLength={200}
autoSize={{ minRows: 2, maxRows: 6 }}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"


+ 2
- 2
react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx View File

@@ -21,7 +21,7 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [
value: data.name,
},
{
label: '版本',
label: '数据集版本',
value: data.version,
},
{
@@ -64,7 +64,7 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [
ellipsis: true,
},
{
label: '版本',
label: '模型版本',
value: data.version,
ellipsis: true,
},


+ 2
- 2
react-ui/src/pages/DevelopmentEnvironment/components/CreateMirrorModal/index.tsx View File

@@ -51,8 +51,8 @@ function CreateMirrorModal({ envId, onOk, ...rest }: CreateMirrorModalProps) {
message: '请输入镜像名称',
},
{
pattern: /^[a-z0-9/_-]*$/,
message: '只支持小写字母、数字、下划线(_)、中横线(-)、斜杠(/)',
pattern: /^[a-z0-9/._-]*$/,
message: '只支持小写字母、数字、点(.)、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>


+ 2
- 1
react-ui/src/pages/Experiment/Info/index.jsx View File

@@ -179,11 +179,12 @@ function ExperimentText() {
if (!statusNode) {
return;
}
const { finishedAt, startedAt, phase, id } = statusNode;
const { finishedAt, startedAt, phase, id, message } = statusNode;
workflowNode.experimentStartTime = startedAt;
workflowNode.experimentEndTime = finishedAt;
workflowNode.experimentStatus = phase;
workflowNode.workflowId = id;
workflowNode.message = message;
workflowNode.img = phase
? `${workflowNode.imgName}-${phase}.png`
: `${workflowNode.imgName}.png`;


+ 8
- 8
react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx View File

@@ -3,7 +3,7 @@ import editExperimentIcon from '@/assets/img/edit-experiment.png';
import KFModal from '@/components/KFModal';
import { type PipelineGlobalParam } from '@/types';
import { to } from '@/utils/promise';
import { Button, Form, Input, Radio, Select, type FormRule } from 'antd';
import { Button, Form, Input, Radio, Select, Typography, type FormRule } from 'antd';
import { useState } from 'react';
import styles from './index.less';

@@ -63,13 +63,14 @@ export const getParamRules = (paramType: number, required: boolean = false): For
};

// 根据参数设置 label
export const getParamType = (param: PipelineGlobalParam): string => {
export const getParamLabel = (param: PipelineGlobalParam): React.ReactNode => {
const paramTypes: Readonly<Record<number, string>> = {
1: '字符串',
2: '整型',
3: '布尔类型',
};
return param.param_name + `(${paramTypes[param.param_type]})`;
const label = param.param_name + `(${paramTypes[param.param_type]})`;
return <Typography.Text ellipsis={{ tooltip: label }}>{label}</Typography.Text>;
};

function AddExperimentModal({
@@ -99,8 +100,8 @@ function AddExperimentModal({
};

const paramLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};

// 除了流水线选择发生变化
@@ -157,7 +158,6 @@ function AddExperimentModal({
form={form}
{...layout}
labelAlign="left"
labelWrap
>
<Form.Item
label="实验名称"
@@ -215,9 +215,9 @@ function AddExperimentModal({
{...restField}
{...paramLayout}
key={key}
label={getParamType(globalParam[name])}
label={getParamLabel(globalParam[name])}
name={[name, 'param_value']}
rules={getParamRules(globalParam[name]['param_type'])}
rules={getParamRules(globalParam[name]['param_type'], true)}
>
{getParamComponent(
globalParam[name]['param_type'],


+ 1
- 2
react-ui/src/pages/Experiment/components/ExperimentDrawer/index.less View File

@@ -13,7 +13,6 @@
}

&__tabs {
height: calc(100% - 169px);
:global {
.ant-tabs-nav {
padding-left: 24px;
@@ -35,7 +34,7 @@
display: flex;
align-items: center;
margin-bottom: 15px;
padding-left: 24px;
padding: 0 24px;
color: @text-color;
font-size: 15px;
}


+ 17
- 2
react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx View File

@@ -3,7 +3,7 @@ import { experimentStatusInfo } from '@/pages/Experiment/status';
import { PipelineNodeModelSerialize } from '@/types';
import { elapsedTime, formatDate } from '@/utils/date';
import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons';
import { Drawer, Tabs } from 'antd';
import { Drawer, Tabs, Typography } from 'antd';
import { useMemo } from 'react';
import ExperimentParameter from '../ExperimentParameter';
import ExperimentResult from '../ExperimentResult';
@@ -129,6 +129,14 @@ const ExperimentDrawer = ({
'--'
)}
</div>
{instanceNodeData.message && (
<div className={styles['experiment-drawer__info']}>
<div style={{ flex: 'none' }}>消息:</div>
<Typography.Text ellipsis={{ tooltip: instanceNodeData.message }}>
{instanceNodeData.message ?? '--'}
</Typography.Text>
</div>
)}
<div className={styles['experiment-drawer__info']}>
启动时间:{formatDate(instanceNodeStartTime)}
</div>
@@ -137,7 +145,14 @@ const ExperimentDrawer = ({
{elapsedTime(instanceNodeStartTime, instanceNodeEndTime)}
</div>
</div>
<Tabs defaultActiveKey="1" items={items} className={styles['experiment-drawer__tabs']} />
<Tabs
defaultActiveKey="1"
items={items}
className={styles['experiment-drawer__tabs']}
style={{
height: instanceNodeData.message ? 'calc(100% - 169px - 39px)' : 'calc(100% - 169px)',
}}
/>
</Drawer>
);
};


+ 8
- 25
react-ui/src/pages/Experiment/components/ViewParamsModal/index.less View File

@@ -1,31 +1,14 @@
.params_container {
max-height: 230px;
padding: 15px 15px 0;
.params-container {
max-height: calc(100vh - 300px);
padding: 24px 24px 0;
overflow-y: auto;
border: 1px solid #e6e6e6;
border-radius: 8px;

&_line {
display: flex;
align-items: center;
margin-bottom: 15px;

&_label {
width: 180px;
color: @text-color;
font-size: 15px;
}
&_value {
flex: 1;
width: 100px;
margin-left: 15px;
padding: 10px 20px;
color: @text-color;
font-size: @font-size;
line-height: 20px;
background: #f6f6f6;
border: 1px solid #e0e0e1;
border-radius: 4px;
}
.params-empty {
:global {
.kf-empty__image {
width: 300px;
}
}
}

+ 41
- 9
react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx View File

@@ -4,9 +4,11 @@
* @Description: 查看实验使用的参数
*/
import parameterImg from '@/assets/img/modal-parameter.png';
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFModal from '@/components/KFModal';
import { type PipelineGlobalParam } from '@/types';
import { getParamType } from '../AddExperimentModal';
import { Form } from 'antd';
import { getParamComponent, getParamLabel } from '../AddExperimentModal';
import styles from './index.less';

type ParamsModalProps = {
@@ -26,14 +28,44 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) {
cancelButtonProps={{ style: { display: 'none' } }}
width={825}
>
<div className={styles.params_container}>
{globalParam?.map((item) => (
<div key={item.param_name} className={styles.params_container_line}>
<span className={styles.params_container_line_label}>{getParamType(item)}</span>
<span className={styles.params_container_line_value}>{item.param_value}</span>
</div>
))}
</div>
{Array.isArray(globalParam) && globalParam.length > 0 ? (
<div className={styles['params-container']}>
<Form
name="view_params_form"
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
initialValues={{ global_param: globalParam }}
labelAlign="left"
disabled
>
<Form.List name="global_param">
{(fields) =>
fields.map(({ key, name, ...restField }) => (
<Form.Item
{...restField}
key={key}
name={[name, 'param_value']}
label={getParamLabel(globalParam[name])}
>
{getParamComponent(
globalParam[name]['param_type'],
globalParam[name]['is_sensitive'],
)}
</Form.Item>
))
}
</Form.List>
</Form>
</div>
) : (
<KFEmpty
className={styles['params-empty']}
type={EmptyType.NoData}
title="暂无数据"
content="该流水线没有设置全局参数"
hasFooter={false}
/>
)}
</KFModal>
);
}


+ 1
- 0
react-ui/src/pages/Experiment/index.jsx View File

@@ -206,6 +206,7 @@ function Experiment() {
setExpandedRowKeys(null);
} else {
getQueryByExperiment(record.id, 0);
refreshExperimentList();
}
};



+ 30
- 24
react-ui/src/pages/Mirror/Create/index.tsx View File

@@ -44,7 +44,7 @@ const mirrorRadioItems: KFRadioItem[] = [
function MirrorCreate() {
const navigate = useNavigate();
const [form] = Form.useForm();
const [nameDisabled, setNameDisabled] = useState(false);
const [isAddVersion, setIsAddVersion] = useState(false); // 是制作镜像还是新增镜像版本
const { message } = App.useApp();

const uploadProps: UploadProps = {
@@ -60,7 +60,7 @@ function MirrorCreate() {
const name = SessionStorage.getItem(SessionStorage.mirrorNameKey);
if (name) {
form.setFieldValue('name', name);
setNameDisabled(true);
setIsAddVersion(true);
}
return () => {
SessionStorage.removeItem(SessionStorage.mirrorNameKey);
@@ -70,32 +70,36 @@ function MirrorCreate() {
// 创建公网、本地镜像
const createPublicMirror = async (formData: FormData) => {
const upload_type = formData['upload_type'];
let params;
if (upload_type === CommonTabKeys.Public) {
params = {
const params = {
...omit(formData, ['upload_type']),
upload_type: 0,
image_type: 0,
};
const [res] = await to(createMirrorReq(params));
if (res) {
message.success('创建成功');
navigate(-1);
}
} else {
const fileList = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const file = fileList[0];
params = {
const params = {
...omit(formData, ['fileList', 'upload_type']),
path: file.response.data.url,
file_size: file.response.data.fileSize,
upload_type: 1,
image_type: 0,
};
const [res] = await to(createMirrorReq(params));
if (res) {
message.success('创建成功');
navigate(-1);
}
}
}

const [res] = await to(createMirrorReq(params));
if (res) {
message.success('创建成功');
navigate(-1);
}
};

// 提交
@@ -118,14 +122,16 @@ function MirrorCreate() {
return true;
};

const descTitle = isAddVersion ? '版本描述' : '镜像描述';

return (
<div className={styles['mirror-create']}>
<PageTitle title="创建镜像"></PageTitle>
<PageTitle title={!isAddVersion ? '创建镜像' : '新增镜像版本'}></PageTitle>
<div className={styles['mirror-create__content']}>
<div>
<Form
name="mirror-create"
labelCol={{ flex: '130px' }}
labelCol={{ flex: '135px' }}
wrapperCol={{ flex: 1 }}
labelAlign="left"
form={form}
@@ -142,7 +148,7 @@ function MirrorCreate() {
<Row gutter={10}>
<Col span={10}>
<Form.Item
label="镜像名称及Tag"
label="镜像名称和版本"
name="name"
rules={[
{
@@ -150,15 +156,15 @@ function MirrorCreate() {
message: '请输入镜像名称',
},
{
pattern: /^[a-z0-9/_-]*$/,
message: '只支持小写字母、数字、下划线(_)、中横线(-)、斜杠(/)',
pattern: /^[a-z0-9/._-]*$/,
message: '只支持小写字母、数字、点(.)、下划线(_)、中横线(-)、斜杠(/)',
},
]}
>
<Input
placeholder="请输入镜像名称"
maxLength={64}
disabled={nameDisabled}
disabled={isAddVersion}
showCount
allowClear
/>
@@ -174,33 +180,33 @@ function MirrorCreate() {
rules={[
{
required: true,
message: '请输入镜像Tag',
message: '请输入镜像版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
message: '镜像版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
]}
>
<Input placeholder="请输入镜像Tag" maxLength={64} showCount allowClear />
<Input placeholder="请输入镜像版本" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Row gutter={10}>
<Col span={20}>
<Form.Item
label="镜像描述"
label={descTitle}
name="description"
rules={[
{
required: true,
message: '请输入镜像描述',
message: `请输入${descTitle}`,
},
]}
>
<Input.TextArea
autoSize={{ minRows: 2, maxRows: 6 }}
placeholder="请输入镜像描述,最长128字符"
placeholder={`请输入${descTitle}`}
maxLength={128}
showCount
allowClear
@@ -303,7 +309,7 @@ function MirrorCreate() {

<Form.Item wrapperCol={{ offset: 0, span: 16 }}>
<Button type="primary" htmlType="submit">
创建镜像
确定
</Button>
<Button
type="default"


+ 12
- 5
react-ui/src/pages/Mirror/Info/index.tsx View File

@@ -155,7 +155,7 @@ function MirrorInfo() {
};

const createMirrorVersion = () => {
navigate(`/dataset/mirror/create`);
navigate(`add-version`);
SessionStorage.setItem(SessionStorage.mirrorNameKey, mirrorInfo.name || '');
setCacheState({
pagination,
@@ -174,20 +174,27 @@ function MirrorInfo() {
title: '镜像地址',
dataIndex: 'url',
key: 'url',
render: tableCellRender(),
width: '25%',
render: tableCellRender('auto', TableCellValueType.Text, { copyable: true }),
},
{
title: '版本描述',
dataIndex: 'description',
key: 'description',
render: tableCellRender(true),
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 150,
width: 100,
render: MirrorStatusCell,
},
{
title: '镜像大小',
dataIndex: 'file_size',
key: 'file_size',
width: 150,
width: 120,
render: tableCellRender(),
},
{
@@ -200,7 +207,7 @@ function MirrorInfo() {
{
title: '操作',
dataIndex: 'operation',
width: 150,
width: 120,
key: 'operation',
hidden: isPublic,
render: (_: any, record: MirrorVersionData) => (


+ 3
- 3
react-ui/src/pages/Mirror/List/index.tsx View File

@@ -128,7 +128,7 @@ function MirrorList() {

// 查看详情
const toDetail = (record: MirrorData) => {
navigate(`/dataset/mirror/info/${record.id}`);
navigate(`info/${record.id}`);
setCacheState({
activeTab,
pagination,
@@ -149,7 +149,7 @@ function MirrorList() {

// 创建镜像
const createMirror = () => {
navigate(`/dataset/mirror/create`);
navigate(`create`);
SessionStorage.setItem(SessionStorage.mirrorNameKey, '');
setCacheState({
activeTab,
@@ -262,7 +262,7 @@ function MirrorList() {
onClick={createMirror}
icon={<KFIcon type="icon-xinjian2" />}
>
制作镜像
创建镜像
</Button>
)}
<Button


+ 1
- 0
react-ui/src/pages/ModelDeployment/ServiceInfo/index.less View File

@@ -18,6 +18,7 @@

&__table {
flex: 1;
min-height: 0;
margin-top: 24px;
}
}


+ 7
- 6
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -133,17 +133,18 @@ function ServiceInfo() {
if (res) {
message.success('删除成功');
// 如果是一页的唯一数据,删除时,请求第一页的数据
// 否则直接刷新这一页的数据
// 避免回到第一页
if (tableData.length > 1) {
// 否则直接刷新这一页的数据,避免回到第一页
if (tableData.length === 1) {
setPagination((prev) => ({
...prev,
current: 1,
}));
} else {
getServiceInfo();
getServiceVersions();
setPagination((prev) => ({
...prev,
}));
}
getServiceInfo();
}
};

@@ -432,7 +433,7 @@ function ServiceInfo() {
onClick={() => createServiceVersion(ServiceOperationType.Create)}
icon={<KFIcon type="icon-xinjian2" />}
>
新增版本
新增服务版本
</Button>
<Button style={{ marginRight: '15px' }} type="default" onClick={handleVersionCompare}>
版本对比


+ 29
- 25
react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx View File

@@ -136,31 +136,35 @@ const GlobalParamsDrawer = forwardRef(
cur.global_param?.[name]?.param_type
}
>
{({ getFieldValue }) => (
<Form.Item
{...restField}
name={[name, 'param_value']}
label="值"
rules={getParamRules(
getFieldValue(['global_param', name, 'param_type']),
true,
)}
>
{getParamComponent(getFieldValue(['global_param', name, 'param_type']))}
</Form.Item>
)}
</Form.Item>
<Form.Item
{...restField}
name={[name, 'is_sensitive']}
label="脱敏显示"
rules={[{ required: true, message: '请选择' }]}
tooltip="展示关联的流水线的参数,脱敏的参数以xxxx展示"
>
<Radio.Group>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
{({ getFieldValue }) => {
const type = getFieldValue(['global_param', name, 'param_type']);
return (
<>
<Form.Item
{...restField}
name={[name, 'param_value']}
label="值"
rules={getParamRules(type, true)}
>
{getParamComponent(type)}
</Form.Item>
{type !== 3 && (
<Form.Item
{...restField}
name={[name, 'is_sensitive']}
label="脱敏显示"
rules={[{ required: true, message: '请选择' }]}
tooltip="展示关联的流水线的参数,脱敏的参数以xxxx展示"
>
<Radio.Group>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
</Form.Item>
)}
</>
);
}}
</Form.Item>
<Tooltip title="删除参数">
<Button


+ 5
- 0
react-ui/src/pages/Points/index.tsx View File

@@ -216,6 +216,11 @@ function PointsDetail() {
label: '进行中',
color: themes['primaryColor'],
},
{
value: 0,
label: '准备中',
color: themes['pendingColor'],
},
]),
},
];


+ 7
- 1
react-ui/src/pages/Workspace/components/TotalStatistics/index.less View File

@@ -1,12 +1,18 @@
.total-statistics {
display: flex;
align-items: center;
justify-content: center;
height: 140px;
padding: 0 16px;

// 媒体查询
@media screen and (max-width: 1600px) {
flex: 1 1 content;
}

&__icon {
width: 63px;
margin-right: 15px;
margin-right: 16px;
}

&__title {


+ 6
- 2
react-ui/src/pages/Workspace/index.less View File

@@ -8,7 +8,6 @@
&__overview {
flex: 1;
gap: 15px;
margin-bottom: 16px;
padding: 20px 30px;
background-color: white;
border-radius: 4px;
@@ -22,7 +21,6 @@

&__content {
display: flex;

flex-wrap: wrap;
gap: 15px;
align-items: center;
@@ -35,6 +33,11 @@
rgba(22, 100, 255, 0.02) 58.35%
);
border-radius: 4px;

// 媒体查询
@media screen and (max-width: 1600px) {
flex: 1 1 content;
}
}
}
}
@@ -44,6 +47,7 @@
gap: 15px;
align-items: flex-start;
width: 100%;
margin-top: 16px;
}

&__user {


+ 2
- 2
react-ui/src/pages/Workspace/index.tsx View File

@@ -46,7 +46,7 @@ function Workspace() {
return (
<div className={styles.workspace}>
<WorkspaceIntro></WorkspaceIntro>
<Flex gap={'0 15px'} wrap>
<Flex gap={'15px'} wrap>
<div className={styles['workspace__overview']}>
<div className={styles['workspace__overview__title']}>运行概览</div>
<div className={styles['workspace__overview__content']}>
@@ -59,7 +59,7 @@ function Workspace() {
<Divider
type="vertical"
dashed
style={{ color: '1px dashed rgba(96, 107, 122, 0.19)', height: 68 }}
style={{ color: '1px dashed rgba(96, 107, 122, 0.19)', height: 68, margin: 0 }}
/>
<TotalStatistics
icon={require('@/assets/img/workspace-experiment.png')}


+ 1
- 0
react-ui/src/types.ts View File

@@ -71,6 +71,7 @@ export type PipelineNodeModel = {
component_label: string;
icon_path: string;
workflowId?: string;
message?: string;
};

// 流水线节点模型数据


+ 4
- 1
react-ui/src/utils/date.ts View File

@@ -41,7 +41,10 @@ export const elapsedTime = (begin?: string | null, end?: string | null): string
if (hours !== 0) {
return `${hours}小时${minutes}分`;
}
return `${minutes}分${seconds}秒`;
if (minutes !== 0) {
return `${minutes}分${seconds}秒`;
}
return `${seconds}秒`;
};

/**


+ 17
- 9
react-ui/src/utils/table.tsx View File

@@ -25,6 +25,7 @@ export type TableCellValueOptions<T> = {
dateFormat?: string; // 类型为 Date 时有效
onClick?: (record: T, e: React.MouseEvent) => void; // 类型为 Link 时有效
format?: (value: any | undefined | null, record: T, index: number) => string | undefined | null; // 类型为 Custom 时有效
copyable?: boolean; // 省略时是否可以复制
};

type TableCellFormatter = (value: any | undefined | null) => string | undefined | null;
@@ -93,17 +94,17 @@ function tableCellRender<T>(
}

if (ellipsis === 'auto' && text) {
return renderCell(type, text, 'auto', record, options?.onClick);
return renderCell(type, text, 'auto', record, options);
} else if (ellipsis && text) {
const tooltipProps = typeof ellipsis === 'object' ? ellipsis : {};
const { overlayStyle, ...rest } = tooltipProps;
return (
<Tooltip {...rest} overlayStyle={{ maxWidth: 400, ...overlayStyle }} title={text}>
{renderCell(type, text, true, record, options?.onClick)}
{renderCell(type, text, true, record, options)}
</Tooltip>
);
} else {
return renderCell(type, text, false, record, options?.onClick);
return renderCell(type, text, false, record, options);
}
};
}
@@ -113,31 +114,38 @@ function renderCell<T>(
text: any | undefined | null,
ellipsis: boolean | 'auto',
record: T,
onClick?: (record: T, e: React.MouseEvent) => void,
options?: TableCellValueOptions<T>,
) {
return type === TableCellValueType.Link
? renderLink(text, ellipsis, record, onClick)
: renderText(text, ellipsis);
? renderLink(text, ellipsis, record, options)
: renderText(text, ellipsis, options);
}

function renderLink<T>(
text: any | undefined | null,
ellipsis: boolean | 'auto',
record: T,
onClick?: (record: T, e: React.MouseEvent) => void,
options?: TableCellValueOptions<T>,
) {
const { onClick } = options ?? {};
return (
<a className="kf-table-row-link" onClick={(e) => onClick?.(record, e)}>
{renderText(text, ellipsis)}
{renderText(text, ellipsis, options)}
</a>
);
}

function renderText(text: any | undefined | null, ellipsis: boolean | 'auto') {
function renderText<T>(
text: any | undefined | null,
ellipsis: boolean | 'auto',
options?: TableCellValueOptions<T>,
) {
const { copyable } = options ?? {};
if (ellipsis === 'auto') {
return (
<Typography.Paragraph
style={{ marginBottom: 0 }}
copyable={copyable}
ellipsis={{
tooltip: {
title: text,


Loading…
Cancel
Save