Browse Source

Merge branch 'dev' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev

dev-active_learn
somunslotus 11 months ago
parent
commit
3a6e202bd5
46 changed files with 199 additions and 550 deletions
  1. +3
    -0
      react-ui/src/components/TableColTitle/index.less
  2. +32
    -0
      react-ui/src/components/TableColTitle/index.tsx
  3. +0
    -5
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
  4. +1
    -1
      react-ui/src/pages/AutoML/components/ExperimentInstance/index.less
  5. +4
    -4
      react-ui/src/pages/AutoML/components/ExperimentInstance/index.tsx
  6. +2
    -5
      react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
  7. +8
    -2
      react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx
  8. +3
    -3
      react-ui/src/pages/Experiment/Comparison/index.less
  9. +6
    -16
      react-ui/src/pages/Experiment/Comparison/index.tsx
  10. +4
    -4
      react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx
  11. +1
    -2
      react-ui/src/pages/Experiment/index.jsx
  12. +44
    -43
      react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.tsx
  13. +14
    -13
      react-ui/src/pages/HyperParameter/components/ParameterInfo/index.tsx
  14. +0
    -1
      react-ui/src/pages/Mirror/List/index.tsx
  15. +3
    -0
      react-ui/src/pages/Model/components/ModelMetrics/index.less
  16. +13
    -18
      react-ui/src/pages/Model/components/ModelMetrics/index.tsx
  17. +0
    -3
      react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx
  18. +4
    -1
      react-ui/src/pages/Pipeline/index.jsx
  19. +1
    -1
      react-ui/src/stories/BasicTableInfo.stories.tsx
  20. +1
    -1
      react-ui/src/stories/docs/Configure.mdx
  21. +0
    -0
      react-ui/src/stories/docs/assets/accessibility.png
  22. +0
    -0
      react-ui/src/stories/docs/assets/accessibility.svg
  23. +0
    -0
      react-ui/src/stories/docs/assets/addon-library.png
  24. +0
    -0
      react-ui/src/stories/docs/assets/assets.png
  25. +0
    -0
      react-ui/src/stories/docs/assets/avif-test-image.avif
  26. +0
    -0
      react-ui/src/stories/docs/assets/context.png
  27. +0
    -0
      react-ui/src/stories/docs/assets/discord.svg
  28. +0
    -0
      react-ui/src/stories/docs/assets/docs.png
  29. +0
    -0
      react-ui/src/stories/docs/assets/figma-plugin.png
  30. +0
    -0
      react-ui/src/stories/docs/assets/github.svg
  31. +0
    -0
      react-ui/src/stories/docs/assets/share.png
  32. +0
    -0
      react-ui/src/stories/docs/assets/styling.png
  33. +0
    -0
      react-ui/src/stories/docs/assets/testing.png
  34. +0
    -0
      react-ui/src/stories/docs/assets/theming.png
  35. +0
    -0
      react-ui/src/stories/docs/assets/tutorials.svg
  36. +0
    -0
      react-ui/src/stories/docs/assets/youtube.svg
  37. +0
    -53
      react-ui/src/stories/example/Button.stories.ts
  38. +0
    -37
      react-ui/src/stories/example/Button.tsx
  39. +0
    -33
      react-ui/src/stories/example/Header.stories.ts
  40. +0
    -56
      react-ui/src/stories/example/Header.tsx
  41. +0
    -32
      react-ui/src/stories/example/Page.stories.ts
  42. +0
    -73
      react-ui/src/stories/example/Page.tsx
  43. +0
    -30
      react-ui/src/stories/example/button.css
  44. +0
    -32
      react-ui/src/stories/example/header.css
  45. +0
    -68
      react-ui/src/stories/example/page.css
  46. +55
    -13
      react-ui/src/utils/table.tsx

+ 3
- 0
react-ui/src/components/TableColTitle/index.less View File

@@ -0,0 +1,3 @@
.ant-table .ant-table-cell .kf-table-col-title {
margin-bottom: 0;
}

+ 32
- 0
react-ui/src/components/TableColTitle/index.tsx View File

@@ -0,0 +1,32 @@
/*
* @Author: 赵伟
* @Date: 2025-03-11 10:52:23
* @Description: 用于内容可变的表格类标题
*/

import { Typography } from 'antd';
import classNames from 'classnames';
import './index.less';

type TableColTitleProps = {
/** 标题 */
title: string;
/** 自定义类名 */
className?: string;
/** 自定义样式 */
style?: React.CSSProperties;
};

function TableColTitle({ title, className, style }: TableColTitleProps) {
return (
<Typography.Paragraph
ellipsis={{ tooltip: title }}
className={classNames('kf-table-col-title', className)}
style={style}
>
{title}
</Typography.Paragraph>
);
}

export default TableColTitle;

+ 0
- 5
react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx View File

@@ -69,35 +69,30 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
dataIndex: 'accuracy',
key: 'accuracy',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '耗时',
dataIndex: 'duration',
key: 'duration',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '训练损失',
dataIndex: 'train_loss',
key: 'train_loss',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '特征处理',
dataIndex: 'feature',
key: 'feature',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '算法',
dataIndex: 'althorithm',
key: 'althorithm',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '状态',


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

@@ -26,7 +26,7 @@

.startTime {
.singleLine();
width: calc(20% + 10px);
width: 200px;
}

.status {


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

@@ -8,7 +8,7 @@ import { elapsedTime, formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { DoubleRightOutlined } from '@ant-design/icons';
import { App, Button, Checkbox, ConfigProvider, Tooltip } from 'antd';
import { App, Button, Checkbox, ConfigProvider, Typography } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo } from 'react';
import { ExperimentListType, experimentListConfig } from '../ExperimentList/config';
@@ -159,9 +159,9 @@ function ExperimentInstanceComponent({
{elapsedTime(item.create_time, item.finish_time)}
</div>
<div className={styles.startTime}>
<Tooltip title={formatDate(item.create_time)}>
<span>{formatDate(item.create_time)}</span>
</Tooltip>
<Typography.Text ellipsis={{ tooltip: formatDate(item.create_time) }}>
{formatDate(item.create_time)}
</Typography.Text>
</div>
<div className={styles.statusBox}>
<img


+ 2
- 5
react-ui/src/pages/AutoML/components/ExperimentList/index.tsx View File

@@ -261,16 +261,13 @@ function ExperimentList({ type }: ExperimentListProps) {
dataIndex: config.descProperty,
key: 'ml_description',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},

{
title: '创建时间',
dataIndex: 'update_time',
key: 'update_time',
width: '20%',
render: tableCellRender(true, TableCellValueType.Date),
ellipsis: { showTitle: false },
width: 200,
render: tableCellRender(false, TableCellValueType.Date),
},
{
title: '最近五次运行状态',


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

@@ -58,7 +58,7 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) {
title: '文件大小',
dataIndex: 'file_size',
key: 'file_size',
render: tableCellRender(),
render: tableCellRender(false),
},
{
title: '更新时间',
@@ -99,7 +99,13 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) {
</Button>
</Flex>
</Flex>
<Table columns={columns} dataSource={fileList} pagination={false} rowKey="url" />
<Table
columns={columns}
dataSource={fileList}
pagination={false}
rowKey="url"
tableLayout="fixed"
/>
</div>
);
}


+ 3
- 3
react-ui/src/pages/Experiment/Comparison/index.less View File

@@ -34,13 +34,13 @@
border-left: none !important;
}
}
.ant-table-tbody-virtual::after {
border-bottom: none !important;
}
.ant-table-footer {
padding: 0;
border: none !important;
}
.ant-table-column-title {
min-width: 0;
}
}
}
}

+ 6
- 16
react-ui/src/pages/Experiment/Comparison/index.tsx View File

@@ -4,6 +4,7 @@
* @Description: 实验对比
*/

import TableColTitle from '@/components/TableColTitle';
import {
getExpEvaluateInfosReq,
getExpMetricsReq,
@@ -13,7 +14,7 @@ import { tableSorter } from '@/utils';
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 { App, Button, Table, TablePaginationConfig, TableProps } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import ExperimentStatusCell from '../components/ExperimentStatusCell';
@@ -154,7 +155,6 @@ function ExperimentComparison() {
fixed: 'left',
align: 'center',
render: tableCellRender(true, TableCellValueType.Array),
ellipsis: { showTitle: false },
},
],
},
@@ -162,17 +162,12 @@ function ExperimentComparison() {
title: `${config.title}参数`,
align: 'center',
children: paramsNames.map((name) => ({
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: ['params', name],
key: name,
width: 120,
width: 150,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
sorter: (a, b) => tableSorter(a.params?.[name], b.params?.[name]),
showSorterTooltip: false,
})),
@@ -181,17 +176,12 @@ function ExperimentComparison() {
title: `${config.title}指标`,
align: 'center',
children: metricsNames.map((name) => ({
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: ['metrics', name],
key: name,
width: 120,
width: 150,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
sorter: (a, b) => tableSorter(a.metrics?.[name], b.metrics?.[name]),
showSorterTooltip: false,
})),


+ 4
- 4
react-ui/src/pages/Experiment/components/ExperimentInstance/index.tsx View File

@@ -13,7 +13,7 @@ import { elapsedTime, formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { DoubleRightOutlined } from '@ant-design/icons';
import { App, Button, Checkbox, ConfigProvider, Tooltip } from 'antd';
import { App, Button, Checkbox, ConfigProvider, Typography } from 'antd';
import classNames from 'classnames';
import { useEffect, useMemo } from 'react';
import TensorBoardStatusCell from '../TensorBoardStatus';
@@ -186,9 +186,9 @@ function ExperimentInstanceComponent({
<div className={styles.description}>
<div style={{ width: '50%' }}>{elapsedTime(item.create_time, item.finish_time)}</div>
<div style={{ width: '50%' }} className={styles.startTime}>
<Tooltip title={formatDate(item.create_time)}>
<span>{formatDate(item.create_time)}</span>
</Tooltip>
<Typography.Text ellipsis={{ tooltip: formatDate(item.create_time) }}>
{formatDate(item.create_time)}
</Typography.Text>
</div>
</div>
<div className={styles.statusBox}>


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

@@ -383,7 +383,7 @@ function Experiment() {
title: '实验名称',
dataIndex: 'name',
key: 'name',
render: tableCellRender(),
render: tableCellRender(false),
width: '16%',
},
{
@@ -400,7 +400,6 @@ function Experiment() {
dataIndex: 'description',
key: 'description',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '最近五次运行状态',


+ 44
- 43
react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.tsx View File

@@ -1,14 +1,15 @@
import InfoGroup from '@/components/InfoGroup';
import KFIcon from '@/components/KFIcon';
import TableColTitle from '@/components/TableColTitle';
import TrialStatusCell from '@/pages/HyperParameter/components/TrialStatusCell';
import { HyperParameterFile, HyperParameterTrial } from '@/pages/HyperParameter/types';
import { getExpMetricsReq } from '@/services/hyperParameter';
import { downLoadZip } from '@/utils/downloadfile';
import { to } from '@/utils/promise';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { App, Button, Table, Tooltip, Tree, type TableProps, type TreeDataNode } from 'antd';
import { App, Button, Table, Tree, type TableProps, type TreeDataNode } from 'antd';
import classNames from 'classnames';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import styles from './index.less';

const { DirectoryTree } = Tree;
@@ -22,13 +23,16 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const { message } = App.useApp();
const [tableData, setTableData] = useState<HyperParameterTrial[]>([]);
const [loading, setLoading] = useState(true);
const [loading, setLoading] = useState(false);

// 防止 Tabs 卡顿
setTimeout(() => {
setTableData(trialList);
setLoading(false);
}, 100);
useEffect(() => {
setLoading(true);
setTimeout(() => {
setTableData(trialList);
setLoading(false);
}, 500);
}, []);

// 计算 column
const first: HyperParameterTrial | undefined = trialList[0];
@@ -43,6 +47,7 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
dataIndex: 'index',
key: 'index',
width: 100,
fixed: 'left',
render: (_text, record, index: number) => {
return (
<div className={styles['cell-index']}>
@@ -53,28 +58,36 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
},
},
{
title: '运行次数',
dataIndex: 'training_iteration',
key: 'training_iteration',
width: 120,
render: tableCellRender(false),
},
{
title: '平均时长(秒)',
dataIndex: 'time_avg',
key: 'time_avg',
width: 150,
render: tableCellRender(false, TableCellValueType.Custom, {
format: (value = 0) => Number(value).toFixed(2),
}),
ellipsis: { showTitle: false },
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
render: TrialStatusCell,
title: '基本信息',
align: 'center',
children: [
{
title: '运行次数',
dataIndex: 'training_iteration',
key: 'training_iteration',
width: 120,
fixed: 'left',
render: tableCellRender(false),
},
{
title: '平均时长(秒)',
dataIndex: 'time_avg',
key: 'time_avg',
width: 150,
fixed: 'left',
render: tableCellRender(false, TableCellValueType.Custom, {
format: (value = 0) => Number(value).toFixed(2),
}),
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
fixed: 'left',
render: TrialStatusCell,
},
],
},
];

@@ -85,18 +98,12 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
key: 'config',
align: 'center',
children: paramsNames.map((name) => ({
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: ['config', name],
key: name,
width: 120,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
showSorterTooltip: false,
})),
});
}
@@ -108,18 +115,12 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
key: 'metrics',
align: 'center',
children: metricNames.map((name) => ({
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: ['metric_analysis', name],
key: name,
width: 120,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
showSorterTooltip: false,
})),
});
}


+ 14
- 13
react-ui/src/pages/HyperParameter/components/ParameterInfo/index.tsx View File

@@ -1,10 +1,11 @@
import TableColTitle from '@/components/TableColTitle';
import {
getReqParamName,
type FormParameter,
} from '@/pages/HyperParameter/components/CreateForm/utils';
import { HyperParameterData } from '@/pages/HyperParameter/types';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { Table, Tooltip, type TableProps } from 'antd';
import { Table, type TableProps } from 'antd';
import { useMemo } from 'react';
import styles from './index.less';

@@ -43,16 +44,14 @@ function ParameterInfo({ info }: ParameterInfoProps) {
dataIndex: 'name',
key: 'type',
width: '40%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
render: tableCellRender('auto'),
},
{
title: '参数类型',
dataIndex: 'type',
key: 'type',
width: '20%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
render: tableCellRender(false),
},
{
title: '取值范围',
@@ -64,7 +63,6 @@ function ParameterInfo({ info }: ParameterInfoProps) {
return JSON.stringify(value);
},
}),
ellipsis: { showTitle: false },
},
];

@@ -72,16 +70,11 @@ function ParameterInfo({ info }: ParameterInfoProps) {
runParameters.length > 0
? parameters.map(({ name }) => {
return {
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: name,
key: name,
width: 150,
render: tableCellRender(true),
ellipsis: { showTitle: false },
};
})
: [];
@@ -89,7 +82,14 @@ function ParameterInfo({ info }: ParameterInfoProps) {
return (
<div className={styles['parameter-info']}>
<div className={styles['parameter-info__title']}>超参数</div>
<Table dataSource={parameters} columns={columns} rowKey="name" bordered pagination={false} />
<Table
dataSource={parameters}
columns={columns}
rowKey="name"
bordered
pagination={false}
tableLayout="fixed"
/>
<div className={styles['parameter-info__title']}>手动运行超参数</div>
<Table
dataSource={runParameters}
@@ -98,6 +98,7 @@ function ParameterInfo({ info }: ParameterInfoProps) {
bordered
pagination={false}
scroll={{ x: '100%' }}
tableLayout="fixed"
/>
</div>
);


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

@@ -192,7 +192,6 @@ function MirrorList() {
key: 'description',
width: '35%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '创建时间',


+ 3
- 0
react-ui/src/pages/Model/components/ModelMetrics/index.less View File

@@ -21,6 +21,9 @@
border-left: none !important;
}
}
.ant-table-column-title {
min-width: 0;
}
}
}



+ 13
- 18
react-ui/src/pages/Model/components/ModelMetrics/index.tsx View File

@@ -1,10 +1,11 @@
import SubAreaTitle from '@/components/SubAreaTitle';
import TableColTitle from '@/components/TableColTitle';
import { useCheck } from '@/hooks';
import { getModelPageVersionsReq, getModelVersionsMetricsReq } from '@/services/dataset';
import { tableSorter } from '@/utils';
import { to } from '@/utils/promise';
import tableCellRender from '@/utils/table';
import { Checkbox, Table, Tooltip, type TablePaginationConfig, type TableProps } from 'antd';
import { Checkbox, Flex, Table, type TablePaginationConfig, type TableProps } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import MetricsChart, { MetricsChatData } from '../MetricsChart';
import styles from './index.less';
@@ -174,17 +175,12 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr
title: `训练参数`,
align: 'center',
children: paramsNames.map((name) => ({
title: (
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
),
title: <TableColTitle title={name} />,
dataIndex: ['params', name],
key: name,
width: 120,
width: 150,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
sorter: (a, b) => tableSorter(a.params?.[name], b.params?.[name]),
showSorterTooltip: false,
})),
@@ -197,14 +193,14 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr
indeterminate={metricsIndeterminate}
onChange={checkAllMetrics}
disabled={metricsNames.length === 0}
></Checkbox>{' '}
<span>训练指标</span>
></Checkbox>
<span style={{ marginLeft: 4 }}>训练指标</span>
</div>
),
align: 'center',
children: metricsNames.map((name) => ({
title: (
<div>
<Flex align="center">
<Checkbox
checked={isSingleMetricsChecked(name)}
onChange={(e) => {
@@ -212,18 +208,15 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr
checkSingleMetrics(name);
}}
onClick={(e) => e.stopPropagation()}
></Checkbox>{' '}
<Tooltip title={name}>
<span>{name}</span>
</Tooltip>
</div>
></Checkbox>
<TableColTitle style={{ marginLeft: 4 }} title={name} />
</Flex>
),
dataIndex: ['metrics', name],
key: name,
width: 120,
width: 150,
align: 'center',
render: tableCellRender(true),
ellipsis: { showTitle: false },
sorter: (a, b) => tableSorter(a.metrics?.[name], b.metrics?.[name]),
showSorterTooltip: false,
})),
@@ -253,6 +246,8 @@ function ModelMetrics({ resourceId, identifier, owner, version }: ModelMetricsPr
}}
onChange={handleTableChange}
rowKey="name"
tableLayout="fixed"
scroll={{ x: '100%' }}
/>
</div>
<div className={styles['model-metrics__chart']}>


+ 0
- 3
react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx View File

@@ -285,7 +285,6 @@ function ServiceInfo() {
key: 'model',
width: '20%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '状态',
@@ -300,7 +299,6 @@ function ServiceInfo() {
key: 'image',
width: '20%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '副本数量',
@@ -317,7 +315,6 @@ function ServiceInfo() {
render: tableCellRender(true, TableCellValueType.Custom, {
format: getResourceDescription,
}),
ellipsis: { showTitle: false },
},
{
title: '操作',


+ 4
- 1
react-ui/src/pages/Pipeline/index.jsx View File

@@ -152,6 +152,7 @@ const Pipeline = () => {
title: '流水线名称',
dataIndex: 'name',
key: 'name',
width: '50%',
render: tableCellRender(false, TableCellValueType.Link, {
onClick: gotoDetail,
}),
@@ -160,19 +161,21 @@ const Pipeline = () => {
title: '流水线描述',
dataIndex: 'description',
key: 'description',
width: '50%',
render: tableCellRender(true),
ellipsis: { showTitle: false },
},
{
title: '创建时间',
dataIndex: 'create_time',
key: 'create_time',
width: 180,
render: tableCellRender(false, TableCellValueType.Date),
},
{
title: '修改时间',
dataIndex: 'update_time',
key: 'update_time',
width: 180,
render: tableCellRender(false, TableCellValueType.Date),
},
{


+ 1
- 1
react-ui/src/stories/BasicTableInfo.stories.tsx View File

@@ -4,7 +4,7 @@ import * as BasicInfoStories from './BasicInfo.stories';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Components/BasicTableInfo 表格基本信息',
title: 'Components/BasicTableInfo 基本信息表格版',
component: BasicTableInfo,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout


react-ui/src/stories/example/Configure.mdx → react-ui/src/stories/docs/Configure.mdx View File

@@ -31,7 +31,7 @@ export const RightArrow = () => <svg
<path d="m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z" />
</svg>

<Meta title="Configure your project" />
<Meta title="Documentation/Storybook" />

<div className="sb-container">
<div className='sb-section-title'>

react-ui/src/stories/example/assets/accessibility.png → react-ui/src/stories/docs/assets/accessibility.png View File


react-ui/src/stories/example/assets/accessibility.svg → react-ui/src/stories/docs/assets/accessibility.svg View File


react-ui/src/stories/example/assets/addon-library.png → react-ui/src/stories/docs/assets/addon-library.png View File


react-ui/src/stories/example/assets/assets.png → react-ui/src/stories/docs/assets/assets.png View File


react-ui/src/stories/example/assets/avif-test-image.avif → react-ui/src/stories/docs/assets/avif-test-image.avif View File


react-ui/src/stories/example/assets/context.png → react-ui/src/stories/docs/assets/context.png View File


react-ui/src/stories/example/assets/discord.svg → react-ui/src/stories/docs/assets/discord.svg View File


react-ui/src/stories/example/assets/docs.png → react-ui/src/stories/docs/assets/docs.png View File


react-ui/src/stories/example/assets/figma-plugin.png → react-ui/src/stories/docs/assets/figma-plugin.png View File


react-ui/src/stories/example/assets/github.svg → react-ui/src/stories/docs/assets/github.svg View File


react-ui/src/stories/example/assets/share.png → react-ui/src/stories/docs/assets/share.png View File


react-ui/src/stories/example/assets/styling.png → react-ui/src/stories/docs/assets/styling.png View File


react-ui/src/stories/example/assets/testing.png → react-ui/src/stories/docs/assets/testing.png View File


react-ui/src/stories/example/assets/theming.png → react-ui/src/stories/docs/assets/theming.png View File


react-ui/src/stories/example/assets/tutorials.svg → react-ui/src/stories/docs/assets/tutorials.svg View File


react-ui/src/stories/example/assets/youtube.svg → react-ui/src/stories/docs/assets/youtube.svg View File


+ 0
- 53
react-ui/src/stories/example/Button.stories.ts View File

@@ -1,53 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';

import { Button } from './Button';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Example/Button',
component: Button,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
backgroundColor: { control: 'color' },
},
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
args: { onClick: fn() },
} satisfies Meta<typeof Button>;

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: {
primary: true,
label: 'Button',
},
};

export const Secondary: Story = {
args: {
label: 'Button',
},
};

export const Large: Story = {
args: {
size: 'large',
label: 'Button',
},
};

export const Small: Story = {
args: {
size: 'small',
label: 'Button',
},
};

+ 0
- 37
react-ui/src/stories/example/Button.tsx View File

@@ -1,37 +0,0 @@
import React from 'react';

import './button.css';

export interface ButtonProps {
/** Is this the principal call to action on the page? */
primary?: boolean;
/** What background color to use */
backgroundColor?: string;
/** How large should the button be? */
size?: 'small' | 'medium' | 'large';
/** Button contents */
label: string;
/** Optional click handler */
onClick?: () => void;
}

/** Primary UI component for user interaction */
export const Button = ({
primary = false,
size = 'medium',
backgroundColor,
label,
...props
}: ButtonProps) => {
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
return (
<button
type="button"
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
style={{ backgroundColor }}
{...props}
>
{label}
</button>
);
};

+ 0
- 33
react-ui/src/stories/example/Header.stories.ts View File

@@ -1,33 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';

import { Header } from './Header';

const meta = {
title: 'Example/Header',
component: Header,
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
args: {
onLogin: fn(),
onLogout: fn(),
onCreateAccount: fn(),
},
} satisfies Meta<typeof Header>;

export default meta;
type Story = StoryObj<typeof meta>;

export const LoggedIn: Story = {
args: {
user: {
name: 'Jane Doe',
},
},
};

export const LoggedOut: Story = {};

+ 0
- 56
react-ui/src/stories/example/Header.tsx View File

@@ -1,56 +0,0 @@
import React from 'react';

import { Button } from './Button';
import './header.css';

type User = {
name: string;
};

export interface HeaderProps {
user?: User;
onLogin?: () => void;
onLogout?: () => void;
onCreateAccount?: () => void;
}

export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
<header>
<div className="storybook-header">
<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF"
/>
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9"
/>
<path
d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z"
fill="#91BAF8"
/>
</g>
</svg>
<h1>Acme</h1>
</div>
<div>
{user ? (
<>
<span className="welcome">
Welcome, <b>{user.name}</b>!
</span>
<Button size="small" onClick={onLogout} label="Log out" />
</>
) : (
<>
<Button size="small" onClick={onLogin} label="Log in" />
<Button primary size="small" onClick={onCreateAccount} label="Sign up" />
</>
)}
</div>
</div>
</header>
);

+ 0
- 32
react-ui/src/stories/example/Page.stories.ts View File

@@ -1,32 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';

import { Page } from './Page';

const meta = {
title: 'Example/Page',
component: Page,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
} satisfies Meta<typeof Page>;

export default meta;
type Story = StoryObj<typeof meta>;

export const LoggedOut: Story = {};

// More on component testing: https://storybook.js.org/docs/writing-tests/component-testing
export const LoggedIn: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const loginButton = canvas.getByRole('button', { name: /Log in/i });
await expect(loginButton).toBeInTheDocument();
await userEvent.click(loginButton);
await expect(loginButton).not.toBeInTheDocument();

const logoutButton = canvas.getByRole('button', { name: /Log out/i });
await expect(logoutButton).toBeInTheDocument();
},
};

+ 0
- 73
react-ui/src/stories/example/Page.tsx View File

@@ -1,73 +0,0 @@
import React from 'react';

import { Header } from './Header';
import './page.css';

type User = {
name: string;
};

export const Page: React.FC = () => {
const [user, setUser] = React.useState<User>();

return (
<article>
<Header
user={user}
onLogin={() => setUser({ name: 'Jane Doe' })}
onLogout={() => setUser(undefined)}
onCreateAccount={() => setUser({ name: 'Jane Doe' })}
/>

<section className="storybook-page">
<h2>Pages in Storybook</h2>
<p>
We recommend building UIs with a{' '}
<a href="https://componentdriven.org" target="_blank" rel="noopener noreferrer">
<strong>component-driven</strong>
</a>{' '}
process starting with atomic components and ending with pages.
</p>
<p>
Render pages with mock data. This makes it easy to build and review page states without
needing to navigate to them in your app. Here are some handy patterns for managing page
data in Storybook:
</p>
<ul>
<li>
Use a higher-level connected component. Storybook helps you compose such data from the
"args" of child component stories
</li>
<li>
Assemble data in the page component from your services. You can mock these services out
using Storybook.
</li>
</ul>
<p>
Get a guided tutorial on component-driven development at{' '}
<a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
Storybook tutorials
</a>
. Read more in the{' '}
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">
docs
</a>
.
</p>
<div className="tip-wrapper">
<span className="tip">Tip</span> Adjust the width of the canvas with the{' '}
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
id="a"
fill="#999"
/>
</g>
</svg>
Viewports addon in the toolbar
</div>
</section>
</article>
);
};

+ 0
- 30
react-ui/src/stories/example/button.css View File

@@ -1,30 +0,0 @@
.storybook-button {
display: inline-block;
cursor: pointer;
border: 0;
border-radius: 3em;
font-weight: 700;
line-height: 1;
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.storybook-button--primary {
background-color: #555ab9;
color: white;
}
.storybook-button--secondary {
box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
background-color: transparent;
color: #333;
}
.storybook-button--small {
padding: 10px 16px;
font-size: 12px;
}
.storybook-button--medium {
padding: 11px 20px;
font-size: 14px;
}
.storybook-button--large {
padding: 12px 24px;
font-size: 16px;
}

+ 0
- 32
react-ui/src/stories/example/header.css View File

@@ -1,32 +0,0 @@
.storybook-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 15px 20px;
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.storybook-header svg {
display: inline-block;
vertical-align: top;
}

.storybook-header h1 {
display: inline-block;
vertical-align: top;
margin: 6px 0 6px 10px;
font-weight: 700;
font-size: 20px;
line-height: 1;
}

.storybook-header button + button {
margin-left: 10px;
}

.storybook-header .welcome {
margin-right: 10px;
color: #333;
font-size: 14px;
}

+ 0
- 68
react-ui/src/stories/example/page.css View File

@@ -1,68 +0,0 @@
.storybook-page {
margin: 0 auto;
padding: 48px 20px;
max-width: 600px;
color: #333;
font-size: 14px;
line-height: 24px;
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.storybook-page h2 {
display: inline-block;
vertical-align: top;
margin: 0 0 4px;
font-weight: 700;
font-size: 32px;
line-height: 1;
}

.storybook-page p {
margin: 1em 0;
}

.storybook-page a {
color: inherit;
}

.storybook-page ul {
margin: 1em 0;
padding-left: 30px;
}

.storybook-page li {
margin-bottom: 8px;
}

.storybook-page .tip {
display: inline-block;
vertical-align: top;
margin-right: 10px;
border-radius: 1em;
background: #e7fdd8;
padding: 4px 12px;
color: #357a14;
font-weight: 700;
font-size: 11px;
line-height: 12px;
}

.storybook-page .tip-wrapper {
margin-top: 40px;
margin-bottom: 40px;
font-size: 13px;
line-height: 20px;
}

.storybook-page .tip-wrapper svg {
display: inline-block;
vertical-align: top;
margin-top: 3px;
margin-right: 4px;
width: 12px;
height: 12px;
}

.storybook-page .tip-wrapper svg path {
fill: #1ea7fd;
}

+ 55
- 13
react-ui/src/utils/table.tsx View File

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

import { isEmpty } from '@/utils';
import { formatDate } from '@/utils/date';
import { Tooltip } from 'antd';
import { Tooltip, TooltipProps, Typography } from 'antd';
import dayjs from 'dayjs';

export enum TableCellValueType {
@@ -65,7 +65,7 @@ function formatArray(property?: string): TableCellFormatter {
}

function tableCellRender<T>(
ellipsis: boolean = false,
ellipsis: boolean | TooltipProps | 'auto' = false,
type: TableCellValueType = TableCellValueType.Text,
options?: TableCellValueOptions<T>,
) {
@@ -92,41 +92,83 @@ function tableCellRender<T>(
break;
}

if (ellipsis && text) {
if (ellipsis === 'auto' && text) {
return renderCell(type, text, 'auto', record, options?.onClick);
} else if (ellipsis && text) {
const tooltipProps = typeof ellipsis === 'object' ? ellipsis : {};
const { overlayStyle, ...rest } = tooltipProps;
return (
<Tooltip title={text} placement="topLeft" overlayStyle={{ maxWidth: '400px' }}>
{renderCell(text, type === TableCellValueType.Link, record, options?.onClick)}
<Tooltip {...rest} overlayStyle={{ maxWidth: 400, ...overlayStyle }} title={text}>
{renderCell(type, text, true, record, options?.onClick)}
</Tooltip>
);
} else {
return renderCell(text, type === TableCellValueType.Link, record, options?.onClick);
return renderCell(type, text, false, record, options?.onClick);
}
};
}

function renderCell<T>(
type: TableCellValueType,
text: any | undefined | null,
isLink: boolean,
ellipsis: boolean | 'auto',
record: T,
onClick?: (record: T, e: React.MouseEvent) => void,
) {
return isLink ? renderLink(text, record, onClick) : renderText(text);
}

function renderText(text: any | undefined | null) {
return <span>{!isEmpty(text) ? text : '--'}</span>;
return type === TableCellValueType.Link
? renderLink(text, ellipsis, record, onClick)
: renderText(text, ellipsis);
}

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

function renderText(text: any | undefined | null, ellipsis: boolean | 'auto') {
if (ellipsis === 'auto') {
return (
<Typography.Paragraph
style={{ marginBottom: 0 }}
ellipsis={{
tooltip: {
title: text,
destroyTooltipOnHide: true,
overlayStyle: { maxWidth: 400 },
},
}}
>
{!isEmpty(text) ? text : '--'}
</Typography.Paragraph>
);
}

return (
<span
style={
ellipsis
? {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
wordBreak: 'break-all',
display: 'inline-block',
maxWidth: '100%',
}
: undefined
}
>
{!isEmpty(text) ? text : '--'}
</span>
);
}

export default tableCellRender;

Loading…
Cancel
Save