ci4sManagement-cloud/react-ui/src/utils/table.tsx

223 lines
5.9 KiB
TypeScript

/*
* @Author: 赵伟
* @Date: 2024-06-26 10:05:52
* @Description: Table Cell 自定义 render
*/
import { isEmpty } from '@/utils';
import { formatDate } from '@/utils/date';
import { Tooltip, TooltipProps, Typography } from 'antd';
import dayjs from 'dayjs';
import React from 'react';
import { formatEnum, type EnumOptions } from './format';
export enum TableCellValueType {
/** 序号 */
Index = 'Index',
/** 文本 */
Text = 'Text',
/** 日期 */
Date = 'Date',
/** 数组 */
Array = 'Array',
/** 链接 */
Link = 'Link',
/** 枚举 */
Enum = 'Enum',
/** 自定义 */
Custom = 'Custom',
}
export type TableCellValueOptions<T> = {
/** 页数,类型为 Index 时有效 */
page?: number;
/** 分页大小,类型为 Index 时有效 */
pageSize?: number;
/** 取数组对象的哪个属性值,类型为 Array 时有效 */
property?: string;
/** 日期格式,类型为 Date 时有效*/
dateFormat?: string;
/** 链接点击回调,类型为 Link 时有效 */
onClick?: (record: T, e: React.MouseEvent) => void;
/** 枚举选项,类型为 Enum 时有效*/
options?: EnumOptions[];
/** 自定义函数,类型为 Custom 时有效*/
format?: (
value: any | undefined | null,
record?: T,
index?: number,
) => React.ReactNode | undefined | null;
/** 省略时是否可以复制 */
copyable?: boolean;
};
type TableCellFormatter = (value: any | undefined | null) => string | undefined | null;
/**
* 日期转换函数
* @param {string | undefined} dateFormat - 日期格式
* @returns {TableCellFormatter} Table cell 渲染函数
*/
function formatDateText(dateFormat?: string): TableCellFormatter {
return (value: any | undefined | null): ReturnType<TableCellFormatter> => {
if (value === undefined || value === null || value === '') {
return null;
}
if (!dayjs(value).isValid()) {
return null;
}
return formatDate(value, dateFormat);
};
}
/**
* 数组转换函数,将数组元素转换为字符串,用逗号分隔
* @param {string} property - 如果数组元素是对象,那么取数组元素的某个属性
* @returns {TableCellFormatter} Table cell 渲染函数
*/
function formatArray(property?: string): TableCellFormatter {
return (value: any | undefined | null): ReturnType<TableCellFormatter> => {
if (
value === undefined ||
value === null ||
Array.isArray(value) === false ||
value.length === 0
) {
return null;
}
const list =
property && typeof value[0] === 'object' ? value.map((item) => item[property]) : value;
return list.join(',');
};
}
/**
* Table cell render 函数
* @param ellipsis - 是否省略
* @param type - 类型
* @param options - 选项
* @returns Ant Design Table 的 render
*/
function tableCellRender<T>(
ellipsis: boolean | TooltipProps | 'auto' = false,
type: TableCellValueType = TableCellValueType.Text,
options?: TableCellValueOptions<T>,
) {
return (value: any | undefined | null, record: T, index: number) => {
let text = value;
switch (type) {
case TableCellValueType.Index:
text = (options?.page ?? 0) * (options?.pageSize ?? 0) + index + 1;
break;
case TableCellValueType.Text:
case TableCellValueType.Link:
text = value;
break;
case TableCellValueType.Date:
text = formatDateText(options?.dateFormat)(value);
break;
case TableCellValueType.Array:
text = formatArray(options?.property)(value);
break;
case TableCellValueType.Enum:
if (options?.options) {
text = formatEnum(options.options)(value);
}
break;
case TableCellValueType.Custom:
text = options?.format?.(value, record, index);
break;
default:
break;
}
if (ellipsis === 'auto' && text) {
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)}
</Tooltip>
);
} else {
return renderCell(type, text, false, record, options);
}
};
}
function renderCell<T>(
type: TableCellValueType,
text: any | undefined | null,
ellipsis: boolean | 'auto',
record: T,
options?: TableCellValueOptions<T>,
) {
return type === TableCellValueType.Link
? renderLink(text, ellipsis, record, options)
: renderText(text, ellipsis, options);
}
function renderLink<T>(
text: any | undefined | null,
ellipsis: boolean | 'auto',
record: T,
options?: TableCellValueOptions<T>,
) {
const { onClick } = options ?? {};
return (
<a className="kf-table-row-link" onClick={(e) => onClick?.(record, e)}>
{renderText(text, ellipsis, options)}
</a>
);
}
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,
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%',
verticalAlign: 'middle',
}
: undefined
}
>
{!isEmpty(text) ? text : '--'}
</span>
);
}
export default tableCellRender;