223 lines
5.9 KiB
TypeScript
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;
|