配置
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { FileTextOutlined, UploadOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, FileTextOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
@@ -10,14 +10,49 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
message,
|
message,
|
||||||
Select,
|
Select,
|
||||||
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Upload,
|
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const EditableForm = ({ value = [], onChange, schema }: any) => {
|
||||||
|
// 兼容性处理:从 columns 中提取字段定义
|
||||||
|
const fields = schema?.columns || [];
|
||||||
|
|
||||||
|
// 假设 form 模式操作的是数组的第一个对象
|
||||||
|
const data = value[0] || {};
|
||||||
|
|
||||||
|
const handleFieldChange = (dataIndex: string, val: any) => {
|
||||||
|
const newData = [{ ...data, [dataIndex]: val }];
|
||||||
|
onChange?.(newData); // 保持数据结构依然是 [{...}]
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '16px',
|
||||||
|
background: '#fafafa',
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '1px solid #f0f0f0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fields.map((col: any) => (
|
||||||
|
<div key={col.dataIndex} style={{ marginBottom: 12 }}>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 500, color: '#333', marginBottom: 4 }}>{col.title}</div>
|
||||||
|
<Input
|
||||||
|
placeholder={col.placeholder}
|
||||||
|
value={data[col.dataIndex]}
|
||||||
|
onChange={(e) => handleFieldChange(col.dataIndex, e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// --- 复杂类型:可编辑表格组件 (用于分段价格等 array + table 模式) ---
|
// --- 复杂类型:可编辑表格组件 (用于分段价格等 array + table 模式) ---
|
||||||
interface EditableTableProps {
|
interface EditableTableProps {
|
||||||
value?: any[];
|
value?: any[];
|
||||||
@@ -31,19 +66,18 @@ interface EditableTableProps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const EditableTable = ({ value = [], onChange, schema }: EditableTableProps) => {
|
const EditableTable = ({ value = [], onChange, schema }: EditableTableProps) => {
|
||||||
const columns = (schema?.columns || []).map((col: any) => ({
|
// --- 1. 字段定义 ---
|
||||||
|
const baseColumns = (schema?.columns || []).map((col: any) => ({
|
||||||
title: col.title,
|
title: col.title,
|
||||||
dataIndex: col.dataIndex,
|
dataIndex: col.dataIndex,
|
||||||
render: (text: any, record: any, index: number) => {
|
render: (text: any, record: any, index: number) => {
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
value: text,
|
value: text,
|
||||||
// 这里 val 可能是 Event 对象也可能是数值,antd 的 InputNumber 直接返回 val
|
|
||||||
// 普通 Input 返回 e.target.value
|
|
||||||
onChange: (e: any) => {
|
onChange: (e: any) => {
|
||||||
const val = e?.target ? e.target.value : e;
|
const val = e?.target ? e.target.value : e;
|
||||||
const newData = [...value];
|
const newData = [...value];
|
||||||
newData[index] = { ...newData[index], [col.dataIndex]: val };
|
newData[index] = { ...newData[index], [col.dataIndex]: val };
|
||||||
onChange?.(newData); // 使用可选链调用,安全消灭波浪线
|
onChange?.(newData);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,25 +85,51 @@ const EditableTable = ({ value = [], onChange, schema }: EditableTableProps) =>
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// --- 2. 操作列 (删除) ---
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 70,
|
||||||
|
align: 'center' as const,
|
||||||
|
render: (_: any, __: any, index: number) => (
|
||||||
|
<Button
|
||||||
|
type='link'
|
||||||
|
danger
|
||||||
|
size='small'
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
const newData = [...value];
|
||||||
|
newData.splice(index, 1);
|
||||||
|
onChange?.(newData);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
...baseColumns,
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ border: '1px solid #f0f0f0', padding: 8, borderRadius: 4 }}>
|
<div style={{ border: '1px solid #f0f0f0', padding: 12, borderRadius: 6, background: '#fff' }}>
|
||||||
|
{/* --- 3. 顶部操作栏 (全部对齐左侧) --- */}
|
||||||
|
<Space style={{ marginBottom: 12 }}>
|
||||||
|
<Button type='primary' size='small' icon={<span>+ </span>} onClick={() => onChange?.([...value, {}])}>
|
||||||
|
新增一行
|
||||||
|
</Button>
|
||||||
|
<Button type='default' danger size='small' disabled={value.length === 0} onClick={() => onChange?.([])}>
|
||||||
|
全部清空
|
||||||
|
</Button>
|
||||||
|
<span style={{ marginLeft: 8, color: '#999', fontSize: '12px' }}>共 {value.length} 条记录</span>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
{/* --- 4. 表格主体 --- */}
|
||||||
<Table
|
<Table
|
||||||
dataSource={value}
|
dataSource={value}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size='small'
|
size='small'
|
||||||
// 显式声明 i 为 number,消灭 rowKey 处的波浪线
|
|
||||||
rowKey={(_: any, i: any) => i.toString()}
|
rowKey={(_: any, i: any) => i.toString()}
|
||||||
|
scroll={{ y: 300 }}
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
type='dashed'
|
|
||||||
block
|
|
||||||
// 关键点:这里也需要使用 ?. 确保 onChange 存在时才调用
|
|
||||||
onClick={() => onChange?.([...value, {}])}
|
|
||||||
style={{ marginTop: 8 }}
|
|
||||||
>
|
|
||||||
新增一行
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -110,12 +170,12 @@ const ConfigList = () => {
|
|||||||
label: '通知方式',
|
label: '通知方式',
|
||||||
type: 'array',
|
type: 'array',
|
||||||
value: [1, 2],
|
value: [1, 2],
|
||||||
array_config: '{"mode":"checkbox"}',
|
array_config: '{"mode":"checkbox", "multiple":"true"}',
|
||||||
options: '[{"label":"短信","value":1},{"label":"邮件","value":2}]',
|
options: '[{"label":"短信","value":1},{"label":"邮件","value":2}]',
|
||||||
},
|
},
|
||||||
{ id: 8, config_key: 'birth_date', label: '日期', type: 'date', value: '2026-01-01' },
|
{ id: 8, config_key: 'birth_date', label: '日期', type: 'date', value: '2026-01-01' },
|
||||||
{ id: 9, config_key: 'expire_time', label: '时间', type: 'datetime', value: '2026-01-01 12:00:00' },
|
{ id: 9, config_key: 'expire_time', label: '时间', type: 'datetime', value: '2026-01-01 12:00:00' },
|
||||||
{ id: 10, config_key: 'logo', label: 'Logo', type: 'file', value: [{ url: '/logo.png', name: 'logo.png' }] },
|
//{ id: 10, config_key: 'logo', label: 'Logo', type: 'file', value: [{ url: '/logo.png', name: 'logo.png' }] },
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
config_key: 'price',
|
config_key: 'price',
|
||||||
@@ -130,6 +190,45 @@ const ConfigList = () => {
|
|||||||
schema:
|
schema:
|
||||||
'{"columns":[{"title":"最小","dataIndex":"min","type":"int"},{"title":"最大","dataIndex":"max","type":"int"},{"title":"价格","dataIndex":"price","type":"int"}]}',
|
'{"columns":[{"title":"最小","dataIndex":"min","type":"int"},{"title":"最大","dataIndex":"max","type":"int"},{"title":"价格","dataIndex":"price","type":"int"}]}',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
config_key: 'admin_info',
|
||||||
|
label: '管理员信息',
|
||||||
|
type: 'array',
|
||||||
|
value: [{ name: '张三', phone: '李四' }],
|
||||||
|
array_config: '{"mode":"form"}',
|
||||||
|
schema:
|
||||||
|
'{"columns":[{"title":"管理员姓名","dataIndex":"name","type":"string","placeholder":"请输入姓名"},{"title":"管理员手机号","dataIndex":"phone","type":"string","placeholder":"请输入手机号"}]}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
config_key: 'audit_status',
|
||||||
|
label: '审核状态',
|
||||||
|
type: 'select',
|
||||||
|
value: 1,
|
||||||
|
options: '[{"label":"待审核","value":1},{"label":"通过","value":2},{"label":"驳回","value":3}]',
|
||||||
|
props: '{"placeholder":"请选择状态"}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
config_key: 'user_roles',
|
||||||
|
label: '关联角色',
|
||||||
|
type: 'array',
|
||||||
|
value: ['admin'], // 初始选中的值
|
||||||
|
array_config: '{"mode":"select", "multiple": true}', // 核心标志位
|
||||||
|
options:
|
||||||
|
'[{"label":"超级管理员","value":"admin"},{"label":"运营","value":"editor"},{"label":"财务","value":"finance"}]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 15,
|
||||||
|
config_key: 'single_check',
|
||||||
|
label: '单选确认',
|
||||||
|
type: 'array',
|
||||||
|
value: [1],
|
||||||
|
// 增加 single 属性
|
||||||
|
array_config: '{"mode":"checkbox", "multiple":"false"}',
|
||||||
|
options: '[{"label":"选项A","value":1},{"label":"选项B","value":2}]',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
setData(
|
setData(
|
||||||
@@ -153,7 +252,7 @@ const ConfigList = () => {
|
|||||||
const { type, props, options, array_config, schema } = item;
|
const { type, props, options, array_config, schema } = item;
|
||||||
|
|
||||||
// 1. 获取该配置项当前的实时值 (用于 tags 模式下合并临时选项)
|
// 1. 获取该配置项当前的实时值 (用于 tags 模式下合并临时选项)
|
||||||
const currentValue = form.getFieldValue(item.config_key) || [];
|
//const currentValue = form.getFieldValue(item.config_key) || [];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'string':
|
case 'string':
|
||||||
@@ -162,18 +261,20 @@ const ConfigList = () => {
|
|||||||
return <Input.TextArea {...props} />;
|
return <Input.TextArea {...props} />;
|
||||||
case 'int':
|
case 'int':
|
||||||
return <InputNumber style={{ width: '100%' }} {...props} />;
|
return <InputNumber style={{ width: '100%' }} {...props} />;
|
||||||
|
case 'select':
|
||||||
|
return <Select {...props} options={options} style={{ width: '100%' }} allowClear />;
|
||||||
case 'bool':
|
case 'bool':
|
||||||
return <Switch />;
|
return <Switch />;
|
||||||
case 'date':
|
case 'date':
|
||||||
return <DatePicker style={{ width: '100%' }} format='YYYY-MM-DD' />;
|
return <DatePicker style={{ width: '100%' }} format='YYYY-MM-DD' />;
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
return <DatePicker showTime style={{ width: '100%' }} format='YYYY-MM-DD HH:mm:ss' />;
|
return <DatePicker showTime style={{ width: '100%' }} format='YYYY-MM-DD HH:mm:ss' />;
|
||||||
case 'file':
|
// case 'file':
|
||||||
return (
|
// return (
|
||||||
<Upload listType='picture' defaultFileList={form.getFieldValue(item.config_key)} beforeUpload={() => false}>
|
// <Upload listType='picture' defaultFileList={form.getFieldValue(item.config_key)} beforeUpload={() => false}>
|
||||||
<Button icon={<UploadOutlined />}>选择文件</Button>
|
// <Button icon={<UploadOutlined />}>选择文件</Button>
|
||||||
</Upload>
|
// </Upload>
|
||||||
);
|
// );
|
||||||
|
|
||||||
case 'array': {
|
case 'array': {
|
||||||
const mode = array_config?.mode;
|
const mode = array_config?.mode;
|
||||||
@@ -184,43 +285,93 @@ const ConfigList = () => {
|
|||||||
return <EditableTable {...props} value={currentValue} schema={schema} />;
|
return <EditableTable {...props} value={currentValue} schema={schema} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 处理复选框模式 (Checkbox)
|
if (mode === 'form') {
|
||||||
if (mode === 'checkbox') {
|
|
||||||
return <Checkbox.Group options={options} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 处理下拉选择与标签模式 (Select / Tags)
|
|
||||||
if (mode === 'select' || mode === 'tags') {
|
|
||||||
// 【关键】始终以原始 options 为基准,防止预设选项丢失
|
|
||||||
const mergedOptions = [...(options || [])];
|
|
||||||
|
|
||||||
// 如果是 tags 模式,需要把当前已输入但不在 options 里的值临时加入下拉列表
|
|
||||||
// 这样可以保证“已选即显示”,但不会影响原始 options 的持久性
|
|
||||||
if (mode === 'tags' && Array.isArray(currentValue)) {
|
|
||||||
currentValue.forEach((v) => {
|
|
||||||
const exists = mergedOptions.some((opt) => opt.value === v);
|
|
||||||
if (!exists) {
|
|
||||||
// 如果是用户新输入的,动态补全到当前渲染列表里
|
|
||||||
mergedOptions.push({ label: String(v), value: v });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<EditableForm
|
||||||
{...props}
|
schema={schema} // 传入包含 columns 的 schema
|
||||||
// 模式判定:tags 优先,其次看是否是多选 select
|
value={currentValue} // 这里的 currentValue 预期是 [{...}]
|
||||||
mode={mode === 'tags' ? 'tags' : array_config.multiple ? 'multiple' : undefined}
|
onChange={(val: any) => {
|
||||||
style={{ width: '100%' }}
|
form.setFieldsValue({ [item.config_key]: val });
|
||||||
placeholder='请选择或输入'
|
}}
|
||||||
options={mergedOptions}
|
|
||||||
optionFilterProp='label'
|
|
||||||
// 允许清空
|
|
||||||
allowClear
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 处理复选框模式 (Checkbox)
|
||||||
|
if (mode === 'checkbox') {
|
||||||
|
//return <Checkbox.Group options={options} />;
|
||||||
|
return (
|
||||||
|
<Checkbox.Group
|
||||||
|
options={options}
|
||||||
|
value={currentValue}
|
||||||
|
onChange={(checkedValues) => {
|
||||||
|
if (array_config?.multiple === 'false') {
|
||||||
|
if (checkedValues.length === 0) return;
|
||||||
|
|
||||||
|
const nextValue = [checkedValues[checkedValues.length - 1]];
|
||||||
|
form.setFieldValue(item.config_key, nextValue);
|
||||||
|
} else {
|
||||||
|
// 普通多选模式
|
||||||
|
form.setFieldValue(item.config_key, checkedValues);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 处理下拉选择与标签模式 (Select / Tags)
|
||||||
|
// if (mode === 'select' || mode === 'tags') {
|
||||||
|
// // 【关键】始终以原始 options 为基准,防止预设选项丢失
|
||||||
|
// const mergedOptions = [...(options || [])];
|
||||||
|
|
||||||
|
// // 如果是 tags 模式,需要把当前已输入但不在 options 里的值临时加入下拉列表
|
||||||
|
// // 这样可以保证“已选即显示”,但不会影响原始 options 的持久性
|
||||||
|
// if (mode === 'tags' && Array.isArray(currentValue)) {
|
||||||
|
// currentValue.forEach((v) => {
|
||||||
|
// const exists = mergedOptions.some((opt) => opt.value === v);
|
||||||
|
// if (!exists) {
|
||||||
|
// // 如果是用户新输入的,动态补全到当前渲染列表里
|
||||||
|
// mergedOptions.push({ label: String(v), value: v });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <Select
|
||||||
|
// {...props}
|
||||||
|
// // 模式判定:tags 优先,其次看是否是多选 select
|
||||||
|
// mode={mode === 'tags' ? 'tags' : array_config.multiple ? 'multiple' : undefined}
|
||||||
|
// style={{ width: '100%' }}
|
||||||
|
// placeholder='请选择或输入'
|
||||||
|
// options={mergedOptions}
|
||||||
|
// allowClear
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
if (mode === 'select' || mode === 'tags') {
|
||||||
|
// 1. 不再使用 mergedOptions 逻辑,直接使用原始预设的 options
|
||||||
|
// 因为不允许输入新值,所以不需要把 currentValue 里的值往 options 里塞
|
||||||
|
const selectOptions = options || [];
|
||||||
|
|
||||||
|
// 2. 判定是否为多选模式
|
||||||
|
// 即使 mode 是 'tags',我们也将其转为 'multiple',以达到“标签化显示但禁止输入”的效果
|
||||||
|
const isMultiple = mode === 'tags' || array_config?.multiple;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...props}
|
||||||
|
// 关键修改:禁止使用 'tags',改用 'multiple'
|
||||||
|
mode={isMultiple ? 'multiple' : undefined}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
// 修改 Placeholder 提示,避免误导用户可以输入
|
||||||
|
placeholder={isMultiple ? '请选择(多选)' : '请选择'}
|
||||||
|
options={selectOptions}
|
||||||
|
allowClear
|
||||||
|
// 开启搜索,方便在长列表中查找,但 multiple 模式下搜索非选项内容无法回车添加
|
||||||
|
showSearch
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
// 如果没有任何匹配的模式,默认返回普通 Select
|
// 如果没有任何匹配的模式,默认返回普通 Select
|
||||||
return <Select options={options} style={{ width: '100%' }} />;
|
return <Select options={options} style={{ width: '100%' }} />;
|
||||||
}
|
}
|
||||||
@@ -252,7 +403,75 @@ const ConfigList = () => {
|
|||||||
render: (v: any, record: any) => {
|
render: (v: any, record: any) => {
|
||||||
if (record.type === 'bool') return v ? '是' : '否';
|
if (record.type === 'bool') return v ? '是' : '否';
|
||||||
if (record.type === 'file') return <FileTextOutlined />;
|
if (record.type === 'file') return <FileTextOutlined />;
|
||||||
return JSON.stringify(v);
|
if (record.type === 'select' && record.options) {
|
||||||
|
const option = record.options.find((opt: any) => opt.value === v);
|
||||||
|
return option ? <Tag color='blue'>{option.label}</Tag> : v;
|
||||||
|
}
|
||||||
|
if (record.array_config?.mode === 'table' && Array.isArray(v)) {
|
||||||
|
return (
|
||||||
|
<div style={{ fontSize: '12px', color: '#666' }}>
|
||||||
|
{v.length > 0 ? `共 ${v.length} 条记录` : <span style={{ color: '#ccc' }}>暂无记录</span>}
|
||||||
|
{/* 也可以选择渲染前两条记录作为预览 */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const presetColors = ['magenta', 'red', 'volcano', 'orange', 'gold', 'lime', 'green', 'cyan', 'blue', 'purple'];
|
||||||
|
if (
|
||||||
|
(record.array_config?.mode === 'checkbox' ||
|
||||||
|
record.array_config?.mode === 'select' ||
|
||||||
|
record.array_config?.mode === 'tags') &&
|
||||||
|
Array.isArray(v)
|
||||||
|
) {
|
||||||
|
// 必须加上 return,否则 render 函数没有返回值
|
||||||
|
return (
|
||||||
|
<Space size={[0, 4]} wrap>
|
||||||
|
{v.map((val: any, index: number) => {
|
||||||
|
const option = record.options?.find((opt: any) => opt.value === val);
|
||||||
|
const label = option ? option.label : val;
|
||||||
|
const color = presetColors[index % presetColors.length];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
key={val}
|
||||||
|
color={color}
|
||||||
|
style={{
|
||||||
|
borderRadius: '12px',
|
||||||
|
marginRight: '2px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (record.array_config?.mode === 'form' && Array.isArray(v) && v.length > 0) {
|
||||||
|
const data = v[0]; // 获取数组中的对象
|
||||||
|
const fields = record.schema?.columns || []; // 获取定义的字段标题
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: '#f5f5f5',
|
||||||
|
padding: '8px 12px',
|
||||||
|
borderRadius: '6px',
|
||||||
|
display: 'inline-block',
|
||||||
|
minWidth: '180px',
|
||||||
|
border: '1px solid #eee',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fields.map((col: any) => (
|
||||||
|
<div key={col.dataIndex} style={{ fontSize: '12px', lineHeight: '1.8' }}>
|
||||||
|
<span style={{ color: '#8c8c8c', marginRight: '8px' }}>{col.title}:</span>
|
||||||
|
<span style={{ color: '#333', fontWeight: 500 }}>{data[col.dataIndex] || '-'}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return typeof v === 'object' ? JSON.stringify(v) : String(v);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user