feat: 实验对比添加上拉加载更多

This commit is contained in:
cp3hnu 2024-10-22 09:47:17 +08:00
parent 4c24faafe9
commit 42ce67062f
10 changed files with 319 additions and 56 deletions

1
react-ui/.npmrc Normal file
View File

@ -0,0 +1 @@
save-prefix=~

View File

@ -60,7 +60,7 @@
"@antv/hierarchy": "^0.6.12",
"@types/crypto-js": "^4.2.2",
"@umijs/route-utils": "^4.0.1",
"antd": "^5.4.4",
"antd": "~5.21.4",
"classnames": "^2.3.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0",
@ -111,7 +111,7 @@
"umi-presets-pro": "^2.0.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=16.14.0"
},
"create-umi": {
"ignoreScript": [

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -20,7 +20,7 @@
height: 40px;
padding: 0 30px;
font-size: @font-size-content;
border-radius: 10px;
border-radius: 6px;
}
.ant-btn-default {
border-color: transparent;

View File

@ -168,7 +168,7 @@
height: 40px;
padding: 0 30px;
font-size: @font-size-content;
border-radius: 10px;
border-radius: 6px;
}
.ant-btn-default {
border-color: transparent;

View File

@ -14,10 +14,30 @@
&__table {
height: calc(100% - 60px);
padding: 20px 30px 0;
padding: 20px 30px;
background-color: white;
border-radius: 10px;
&__footer {
display: flex;
align-items: center;
padding-top: 20px;
color: @text-color-secondary;
font-size: 12px;
background-color: white;
div {
flex: 1;
height: 1px;
background-color: @border-color-base;
}
p {
flex: none;
margin: 0 8px;
}
}
:global {
.ant-table-container {
border: none !important;
@ -34,6 +54,13 @@
border-left: none !important;
}
}
.ant-table-tbody-virtual::after {
border-bottom: none !important;
}
.ant-table-footer {
padding: 0;
border: none !important;
}
}
}
}

View File

@ -1,4 +1,10 @@
// import { useCacheState } from '@/hooks/pageCacheState';
/*
* @Author:
* @Date: 2024-10-10 09:55:12
* @Description:
*/
import { useDomSize } from '@/hooks';
import {
getExpEvaluateInfosReq,
getExpMetricsReq,
@ -8,7 +14,6 @@ import { to } from '@/utils/promise';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { useSearchParams } from '@umijs/max';
import { App, Button, Table, /* TablePaginationConfig,*/ TableProps, Tooltip } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import ExperimentStatusCell from '../components/ExperimentStatusCell';
import { ComparisonType, comparisonConfig } from './config';
@ -26,40 +31,57 @@ type TableData = {
params: Record<string, number>;
};
const pageSize = 30;
// function Footer() {
// return (
// <div className={styles['experiment-comparison__table__footer']}>
// <div></div>
// <p>我是有底线的</p>
// <div></div>
// </div>
// );
// }
function ExperimentComparison() {
const [searchParams] = useSearchParams();
const comparisonType = searchParams.get('type') as ComparisonType;
const experimentId = searchParams.get('id');
const [tableData, setTableData] = useState<TableData[]>([]);
// const [cacheState, setCacheState] = useCacheState();
// const [total, setTotal] = useState(0);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
// const [loading, setLoading] = useState(false);
const { message } = App.useApp();
const config = useMemo(() => comparisonConfig[comparisonType], [comparisonType]);
// const [pagination, setPagination] = useState<TablePaginationConfig>(
// cacheState?.pagination ?? {
// current: 1,
// pageSize: 10,
// },
// );
const [tableRef, { width: tableWidth, height: tableHeight }] = useDomSize<HTMLDivElement>(
0,
0,
[],
);
const [loadCompleted, setLoadCompleted] = useState(false);
const [loading, setLoading] = useState(false); // 避免误触发加载更多
useEffect(() => {
getComparisonData();
}, [experimentId]);
// 获取对比数据列表
const getComparisonData = async () => {
// setLoading(true);
const getComparisonData = async (offset: string = '') => {
const request =
comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq;
const [res] = await to(request(experimentId, { offset: '', limit: 50 }));
// setLoading(false);
const [res] = await to(request(experimentId, { offset: offset, limit: pageSize }));
if (res && res.data) {
// const { content = [], totalElements = 0 } = res.data;
setTableData(res.data);
// setTotal(totalElements);
setTableData((prev) => [...prev, ...res.data]);
if (res.data.length === 0) {
setLoadCompleted(true);
const ele = document.getElementsByClassName('ant-table-body')[0];
if (ele) {
const div = document.createElement('div');
div.className = styles['experiment-comparison__table__footer'];
div.innerHTML = '<div></div><p>我是有底线的</p><div></div>';
ele.appendChild(div);
}
}
}
setLoading(false);
};
// 获取对比 url
@ -80,17 +102,10 @@ function ExperimentComparison() {
getExpMetrics();
};
// 分页切换
// const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => {
// if (action === 'paginate') {
// setPagination(pagination);
// }
// // console.log(pagination, filters, sorter, action);
// };
// 选择行
const rowSelection: TableProps['rowSelection'] = {
type: 'checkbox',
columnWidth: 48,
fixed: 'left',
selectedRowKeys,
onChange: (selectedRowKeys: React.Key[]) => {
@ -98,7 +113,20 @@ function ExperimentComparison() {
},
};
const columns: TableProps<TableData>['columns'] = useMemo(() => {
const handleTableScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => {
const target = e.target as HTMLDivElement;
const { scrollTop, scrollHeight, clientHeight } = target;
// 实现自动加载更多
if (!loadCompleted && !loading && scrollHeight - scrollTop - clientHeight <= 0) {
const last = tableData[tableData.length - 1];
setLoading(true);
getComparisonData(last?.run_id);
}
};
const columns: TableProps['columns'] = useMemo(() => {
const first: TableData | undefined = tableData[0];
return [
{
@ -192,29 +220,15 @@ function ExperimentComparison() {
</Button>
</div>
<div
className={classNames(
'vertical-scroll-table-no-page',
styles['experiment-comparison__table'],
)}
>
<div className={styles['experiment-comparison__table']} ref={tableRef}>
<Table
dataSource={tableData}
columns={columns}
rowSelection={rowSelection}
scroll={{ y: 'calc(100% - 110px)', x: '100%' }}
scroll={{ y: tableHeight - 150, x: tableWidth - 60 }}
pagination={false}
bordered={true}
virtual
// onScroll={handleTableScroll}
// loading={loading}
// pagination={{
// ...pagination,
// total: total,
// showSizeChanger: true,
// showQuickJumper: true,
// }}
// onChange={handleTableChange}
onScroll={handleTableScroll}
rowKey="run_id"
/>
</div>

View File

@ -126,7 +126,7 @@ function AddExperimentModal({
<Button key="cancel" onClick={onCancel}>
</Button>,
<Button key="submit" type="primary" onClick={() => handleRun(false)}>
<Button key="submit" type={isAdd ? 'primary' : 'default'} onClick={() => handleRun(false)}>
</Button>,
];

View File

@ -47,13 +47,11 @@
&__robot-img {
position: fixed;
right: 30px;
bottom: 20px;
right: 20px;
bottom: 90px;
z-index: 99;
width: 64px;
height: 64px;
background-color: white;
border-radius: 10px;
width: 56px;
height: 56px;
cursor: pointer;
}
}

View File

@ -188,4 +188,227 @@ declare namespace API {
filter?: string;
sorter?: string;
};
type CurrentUser = UserInfo & {
signature?: string;
title?: string;
group?: string;
tags?: { key?: string; label?: string }[];
notifyCount?: number;
unreadCount?: number;
country?: string;
access?: string;
geographic?: {
province?: { label?: string; key?: string };
city?: { label?: string; key?: string };
};
address?: string;
phone?: string;
roleNames?: {
roleName?: string;
}[];
};
type ErrorResponse = {
/** 业务约定的错误码 */
errorCode: string;
/** 业务上的错误信息 */
errorMessage?: string;
/** 业务上的请求是否成功 */
success?: boolean;
};
type FakeCaptcha = {
code?: number;
status?: string;
};
type getFakeCaptchaParams = {
/** 手机号 */
phone?: string;
};
type LoginParams = {
username?: string;
password?: string;
uuid?: string;
autoLogin?: boolean;
type?: string;
};
type LoginResult = {
code: number;
msg?: string;
type?: string;
data?: {
access_token?: string;
expires_in?: number;
};
};
type NoticeIconItem = {
id?: string;
extra?: string;
key?: string;
read?: boolean;
avatar?: string;
title?: string;
status?: string;
datetime?: string;
description?: string;
type?: NoticeIconItemType;
};
type NoticeIconItemType = 'notification' | 'message' | 'event';
type NoticeIconList = {
data?: NoticeIconItem[];
/** 列表的内容总数 */
total?: number;
success?: boolean;
};
type PageParams = {
current?: number;
pageSize?: number;
};
type RuleList = {
data?: RuleListItem[];
/** 列表的内容总数 */
total?: number;
success?: boolean;
};
type RuleListItem = {
key?: number;
disabled?: boolean;
href?: string;
avatar?: string;
name?: string;
owner?: string;
desc?: string;
callNo?: number;
status?: number;
updatedAt?: string;
createdAt?: string;
progress?: number;
};
type ruleParams = {
/** 当前的页码 */
current?: number;
/** 页面的容量 */
pageSize?: number;
};
type ApiResponse = {
code?: number;
type?: string;
message?: string;
};
type Category = {
id?: number;
name?: string;
};
type deleteOrderParams = {
/** ID of the order that needs to be deleted */
orderId: number;
};
type deletePetParams = {
api_key?: string;
/** Pet id to delete */
petId: number;
};
type deleteUserParams = {
/** The name that needs to be deleted */
username: string;
};
type findPetsByStatusParams = {
/** Status values that need to be considered for filter */
status: ('available' | 'pending' | 'sold')[];
};
type findPetsByTagsParams = {
/** Tags to filter by */
tags: string[];
};
type getOrderByIdParams = {
/** ID of pet that needs to be fetched */
orderId: number;
};
type getPetByIdParams = {
/** ID of pet to return */
petId: number;
};
type getUserByNameParams = {
/** The name that needs to be fetched. Use user1 for testing. */
username: string;
};
type loginUserParams = {
/** The user name for login */
username: string;
/** The password for login in clear text */
password: string;
};
type Order = {
id?: number;
petId?: number;
quantity?: number;
shipDate?: string;
/** Order Status */
status?: 'placed' | 'approved' | 'delivered';
complete?: boolean;
};
type Pet = {
id?: number;
category?: Category;
name: string;
photoUrls: string[];
tags?: Tag[];
/** pet status in the store */
status?: 'available' | 'pending' | 'sold';
};
type Tag = {
id?: number;
name?: string;
};
type updatePetWithFormParams = {
/** ID of pet that needs to be updated */
petId: number;
};
type updateUserParams = {
/** name that need to be updated */
username: string;
};
type uploadFileParams = {
/** ID of pet to update */
petId: number;
};
type User = {
id?: number;
username?: string;
firstName?: string;
lastName?: string;
email?: string;
password?: string;
phone?: string;
/** User Status */
userStatus?: number;
};
}