import Layout from 'layouts/index';
import { useRouter } from 'next/router';
import React, { useMemo, useState, useContext, useEffect, useRef, Suspense } from 'react';
import { Descriptions, Button, Checkbox, Select, message, InputNumber, Input, Typography, Tooltip } from 'antd';
import { HolderOutlined, EditOutlined, CloseOutlined, SaveOutlined, ExportOutlined } from '@ant-design/icons';
import { DndContext } from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import SearchTables from '@components/SearchTables';
import { getMetaTableDetail, batchUdopMetaField, udopMetaTable } from 'api/systemConfig';
import { getStaticLookupListAll, getDynLookupListAll } from 'api/dictionary';
import StateWrapper from '@components/StateWrapper';
import CustomProTable from '@components/CustomProTable';
import EditFieldModal from './components/EditFieldModal';
import EditOrderOptionModal from './components/EditOrderOptionModal';
import { numberVisibleTypes, orderMap } from '@lib/constants';
import { useSelector, useDispatch } from 'react-redux';
const TableStructurePage = React.lazy(() => import('@pages/systemConfig/tableStructure/index.page'));
import LayoutBreadcrumb from '@components/LayoutBreadcrumb';
import FullScreenLoading from '@components/FullScreenLoading';
import PermissionWrapper from '@components/PermissionWrapper';
import st from './index.module.scss';
const RowContext = React.createContext({});
const DragHandle = () => {
const { setActivatorNodeRef, listeners } = useContext(RowContext);
return (
<Button
type='text'
size='small'
icon={<HolderOutlined />}
style={{ cursor: 'move' }}
ref={setActivatorNodeRef}
{...listeners}
/>
);
};
const Row = (props) => {
const { isEdit } = props;
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
id: props['data-row-key'],
disabled: !isEdit, // 禁用拖拽时设置为 true
});
const style = {
...props.style,
transform: CSS.Translate.toString(transform),
transition,
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
};
const contextValue = useMemo(() => ({ setActivatorNodeRef, listeners }), [setActivatorNodeRef, listeners]);
return (
<RowContext.Provider value={contextValue}>
<tr {...props} ref={setNodeRef} style={style} {...attributes} />
</RowContext.Provider>
);
};
const getVisibleTypeOptions = (allOptions = [], dataType) => {
switch (dataType) {
case 'TINYINT':
case 'SMALLINT':
case 'INTEGER':
case 'BIGINT':
return allOptions.filter((item) => [1, 2, 6, 8].includes(item.code));
case 'FLOAT':
case 'DOUBLE':
case 'DECIMAL':
return allOptions.filter((item) => [1, 3, 7].includes(item.code));
case 'CHAR':
case 'VARCHAR':
case 'TEXT':
return allOptions.filter((item) => [1, 2, 5, 8].includes(item.code));
case 'BOOLEAN':
return allOptions.filter((item) => [1, 2].includes(item.code));
case 'DATE':
return allOptions.filter((item) => [1, 4].includes(item.code));
default:
return allOptions;
}
};
const fieldTypeOptions = [
{
label: '业务字段',
value: 1,
},
{
label: '管理字段',
value: 2,
},
{
label: '审计字段',
value: 3,
},
];
const isValidRegex = (pattern) => {
try {
new RegExp(pattern);
return true;
} catch {
console.error('Invalid regex pattern:', pattern);
return false;
}
};
export default function Home(props) {
let { tableId: componentTableId, tableName: componentTableName, componentTabTitle } = props;
const router = useRouter();
let params = new URLSearchParams(location.search);
let tableIdVal = componentTableId || params.get('tableId');
let tableNameVal = params.get('tableName') || componentTableName;
let tabTitleVal = params.get('tabTitle') || componentTabTitle;
const tableId = useRef(tableIdVal);
const { enumList } = useSelector((state) => state.modelCommonData);
const [editDataSource, setEditDataSource] = useState([]);
const [dataSource, setDataSource] = useState([]);
const [detailInfo, setDetailInfo] = useState({});
const [detailLoading, setDetailLoading] = useState(false);
const [isEdit, setIsEdit] = useState(false);
const [isTableTypeEdit, setIsTableTypeEdit] = useState(false);
const [editedTableTypeValue, setEditedTableTypeValue] = useState();
const [saveLoading, setSaveLoading] = useState(false);
const [dictList, setDictList] = useState([]);
const [dynLookupList, setDynLookupList] = useState([]);
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedEditData, setEditSelectedData] = useState();
const [invalidRegexMap, setInvalidRegexMap] = useState({});
const [editOrderOptionModalVisible, setEditOrderOptionModalVisible] = useState(false);
const { tableTypeEnumDic, visibleTypeEnumDic } = enumList || {};
const { activeTab, tabs, appCodes = [] } = useSelector((state) => state.modelUser);
const dispatch = useDispatch();
const {
modelUser: { setActiveTab, updateTab },
modelCommonData: { fetchCommonData },
} = dispatch;
useEffect(() => {
getDetailInfo();
}, []);
const getDetailInfo = async (showLoading = true) => {
try {
showLoading && setDetailLoading(true);
// 并行请求多个接口
const [detail, dictListRes, dynLookupListRes] = await Promise.all([
getMetaTableDetail({ tableId: tableId.current }),
getStaticLookupListAll(),
getDynLookupListAll(),
fetchCommonData('enumList'),
]);
const dictList = dictListRes || [];
const dynLookupList = dynLookupListRes || [];
setDictList(dictList);
setDynLookupList(dynLookupList);
setDetailInfo(detail);
const convertFields = (detail.fields || []).map((item) => ({
...item,
inputPattern: item.inputPattern ? JSON.parse(item.inputPattern || '{}').pattern : '',
inputPatternMsg: item.inputPattern ? JSON.parse(item.inputPattern || '{}').patternMsg : '',
}));
setDataSource(JSON.parse(JSON.stringify(convertFields)));
setEditDataSource(JSON.parse(JSON.stringify(convertFields)));
showLoading && setDetailLoading(false);
} catch (error) {
setDetailLoading(false);
message.error(error.msg || error.message || '获取数据失败');
}
};
const onEditTableChange = (type, value, fieldId) => {
let editDataSourceTmp = [...editDataSource];
for (let item of editDataSource) {
if (item.fieldId === fieldId)
item[type] = ['inputPattern', 'inputPatternMsg'].includes(type)
? value || ''
: [
'isSearchable',
'isListVisible',
'isSortable',
'isEditable',
'excludeFromTranslation',
'dataPrecision',
'dataScale',
'minValue',
'maxValue',
].includes(type)
? value
: value || '-1';
}
if (type == 'inputPattern') {
const isValid = isValidRegex(value);
setInvalidRegexMap((prev) => ({
...prev,
[fieldId]: !isValid,
}));
}
setEditDataSource(editDataSourceTmp);
};
const batchUpdateFields = async () => {
try {
setSaveLoading(true);
let fields = [];
let idx = 1;
//todo 是否需要根据是不是数字类型清空相关字段值minVal等
for (let item of editDataSource) {
item.fieldSeq = idx;
fields.push({
...item,
dataPrecision: item.dataPrecision || null,
dataScale: item.dataScale || null,
dictId: item.visibleType == 2 ? item.dictId : null,
inputPattern:
item.visibleType != 1 || item.inputPattern == ''
? ''
: JSON.stringify({
pattern: item.inputPattern,
patternMsg: item.inputPatternMsg,
}),
});
idx++;
}
await batchUdopMetaField({ tableId: tableId.current, fields });
getDetailInfo(false);
setIsEdit(false);
setSaveLoading(false);
message.success('保存成功');
} catch (error) {
setSaveLoading(false);
message.error(error.msg || error.message || '操作失败');
}
};
const handleSaveTableType = async () => {
try {
setSaveLoading(true);
await udopMetaTable({
tableId: tableId.current,
tableType: editedTableTypeValue,
orderOption: detailInfo?.orderOption,
});
setDetailInfo({ ...detailInfo, tableType: editedTableTypeValue });
setIsTableTypeEdit(false);
setSaveLoading(false);
message.success('保存成功');
} catch (error) {
setSaveLoading(false);
message.error(error.msg || error.message || '操作失败');
}
};
const orderOption = JSON.parse(detailInfo?.orderOption || '[]')
.map((item) => `${item.fieldName} ${orderMap.get(item.orderType)}`)
.join(',');
const items = [
{
label: '表名称',
children: `${detailInfo?.displayName}(${detailInfo?.tableName})`,
},
{
label: '主题域名称',
children: detailInfo?.domainName,
},
// {
// label: '描述信息',
// children: detailInfo?.description,
// },
{
label: '表类型',
children: isTableTypeEdit ? (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Select
options={tableTypeEnumDic.filter((item) => item.code !== 4)}
fieldNames={{ label: 'description', value: 'code' }}
value={editedTableTypeValue}
style={{ width: '100%' }}
onChange={(value) => {
setEditedTableTypeValue(value);
}}
/>
<Tooltip title='取消'>
<Button
type='link'
icon={<CloseOutlined />}
onClick={() => {
if (!!saveLoading) {
message.info('保存中');
return;
}
setIsTableTypeEdit(false);
}}
/>
</Tooltip>
<Tooltip title='保存'>
<Button type='link' icon={<SaveOutlined />} loading={saveLoading} onClick={handleSaveTableType} />
</Tooltip>
</div>
) : (
<div style={{ display: 'flex', alignItems: 'center' }}>
{tableTypeEnumDic?.find((item) => item.code == detailInfo?.tableType)?.description}
{detailInfo?.tableType !== 4 && (
<PermissionWrapper code='udop:metadata:settings:edit_table'>
<Tooltip title='编辑'>
<Button
type='link'
icon={<EditOutlined />}
onClick={() => {
setIsTableTypeEdit(true);
setEditedTableTypeValue(detailInfo?.tableType);
}}
/>
</Tooltip>
</PermissionWrapper>
)}
</div>
),
},
{
label: '标签',
children: detailInfo?.tags?.join('、'),
},
{
label: '默认排序设定',
children: (
<div style={{ display: 'flex', alignItems: 'center' }}>
{orderOption}
<PermissionWrapper code='udop:metadata:settings:edit_table'>
<Tooltip title='编辑'>
<Button type='link' icon={<EditOutlined />} onClick={() => setEditOrderOptionModalVisible(true)} />
</Tooltip>
</PermissionWrapper>
</div>
),
},
// {
// label: '分级',
// span: 'filled', // span = 2
// children: 'No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China',
// },
];
const handleEdit = ({ fieldId }) => {
const editRecord = editDataSource.find((item) => item.fieldId === fieldId);
setEditSelectedData(editRecord);
setEditModalVisible(true);
};
const columns = [
{ title: '拖拽位', dataIndex: 'fieldId', align: 'center', width: 80, render: () => <DragHandle /> },
{ title: '字段名', dataIndex: 'fieldName', width: 120 },
{
title: '字段展示名称',
dataIndex: 'displayName',
width: 120,
render: (_, record, __, extraProps) =>
isEdit || !!extraProps.editInModal ? (
<Input
allowClear
disabled={!isEdit}
value={record.displayName}
onChange={(e) => onEditTableChange('displayName', e.target.value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
) : (
record.displayName
),
},
{ title: '字段描述', dataIndex: 'description', width: 180 },
{ title: '数据类型', dataIndex: 'dataType', width: 100 },
{
title: '字段长度',
dataIndex: 'dataLength',
width: 90,
render: (val) => <div style={{ textAlign: 'right' }}>{val !== -1 ? val : ''}</div>,
},
{
title: '是否为主键',
dataIndex: 'isPrimaryKey',
align: 'center',
width: 100,
render: (value) => <Checkbox checked={!!value} disabled />,
},
{
title: '是否为搜索字段',
dataIndex: 'isSearchable',
align: 'center',
width: 130,
render: (value, record, _, extraProps) => (
<Checkbox
checked={!!value}
disabled={!isEdit}
onChange={(e) => onEditTableChange('isSearchable', e.target.checked, record.fieldId)}
{...extraProps}
/>
),
},
{
title: '是否在列表展示',
dataIndex: 'isListVisible',
align: 'center',
width: 130,
render: (value, record, _, extraProps) => (
<Checkbox
checked={!!value}
disabled={!isEdit}
onChange={(e) => onEditTableChange('isListVisible', e.target.checked, record.fieldId)}
{...extraProps}
/>
),
},
{
title: '是否必填',
dataIndex: 'isNullable',
align: 'center',
width: 100,
render: (value) => <Checkbox checked={!value} disabled />,
},
{
title: '是否编辑',
dataIndex: 'isEditable',
align: 'center',
width: 100,
render: (value, record, _, extraProps) => (
<Checkbox
checked={!!value}
disabled={record.fieldType !== 1 || !isEdit}
onChange={(e) => onEditTableChange('isEditable', e.target.checked, record.fieldId)}
{...extraProps}
/>
),
},
{
title: '是否排序',
dataIndex: 'isSortable',
align: 'center',
width: 100,
render: (value, record, _, extraProps) => (
<Checkbox
checked={!!value}
disabled={!isEdit}
onChange={(e) => onEditTableChange('isSortable', e.target.checked, record.fieldId)}
{...extraProps}
/>
),
},
{
title: '是否翻译排除',
dataIndex: 'excludeFromTranslation',
align: 'center',
width: 130,
render: (value, record, _, extraProps) => (
<Checkbox
checked={!!value}
disabled={record.fieldType !== 1 || !isEdit}
onChange={(e) => onEditTableChange('excludeFromTranslation', e.target.checked, record.fieldId)}
{...extraProps}
/>
),
},
{
title: '页面展示类型',
dataIndex: 'visibleType',
align: 'center',
width: 130,
render: (value, record, _, extraProps) => {
let selectVal = record.visibleType && record.visibleType != -1 ? record.visibleType : undefined;
let options = getVisibleTypeOptions(visibleTypeEnumDic, record.dataType) || [];
return isEdit || !!extraProps.editInModal ? (
<Select
allowClear
options={options}
fieldNames={{ label: 'description', value: 'code' }}
disabled={record.fieldType !== 1 || !isEdit}
style={{ width: '100%' }}
getPopupContainer={(triggerNode) => triggerNode.parentElement}
value={selectVal}
onChange={(value) => onEditTableChange('visibleType', value, record.fieldId)}
read
{...extraProps}
/>
) : (
options.find((item) => item.code == selectVal)?.description
);
},
},
{
title: '字段类型',
dataIndex: 'fieldType',
align: 'center',
width: 130,
render: (_, record) =>
record.fieldType && record.fieldType != -1
? fieldTypeOptions.find(({ value }) => value === record.fieldType)?.label
: undefined,
},
{
title: '静态字典名称',
dataIndex: 'dictId',
align: 'center',
width: 150,
render: (value, record, _, extraProps) => {
let selectVal = record.dictId && record.dictId != -1 ? record.dictId : undefined;
return record.visibleType === 2 ? (
isEdit || !!extraProps.editInModal ? (
<Select
allowClear
showSearch
filterOption={(input, option) => (option?.displayName ?? '').toLowerCase().includes(input.toLowerCase())}
options={dictList}
fieldNames={{ label: 'displayName', value: 'id' }}
disabled={record.fieldType !== 1 || !isEdit}
style={{ width: '100%' }}
getPopupContainer={(triggerNode) => triggerNode.parentElement}
value={record.dictId && record.dictId != -1 ? record.dictId : undefined}
onChange={(value) => onEditTableChange('dictId', value, record.fieldId)}
{...extraProps}
/>
) : (
(dictList || []).find((item) => item.id == selectVal)?.displayName
)
) : (
''
);
},
},
{
title: '动态字典名称',
dataIndex: 'dynLookupId',
align: 'center',
width: 150,
render: (value, record, _, extraProps) => {
let selectVal = record.dynLookupId && record.dynLookupId != -1 ? record.dynLookupId : undefined;
return isEdit || !!extraProps.editInModal ? (
<Select
allowClear
showSearch
filterOption={(input, option) => (option?.displayName ?? '').toLowerCase().includes(input.toLowerCase())}
options={dynLookupList}
fieldNames={{ label: 'displayName', value: 'id' }}
disabled={!isEdit}
style={{ width: '100%' }}
getPopupContainer={(triggerNode) => triggerNode.parentElement}
value={record.dynLookupId && record.dynLookupId != -1 ? record.dynLookupId : undefined}
onChange={(value) => onEditTableChange('dynLookupId', value, record.fieldId)}
{...extraProps}
/>
) : (
(dynLookupList || []).find((item) => item.id == selectVal)?.displayName
);
},
},
{
title: '校验规则',
dataIndex: 'inputPattern',
align: 'center',
width: 220,
render: (_, record, __, extraProps) =>
record.visibleType === 1 ? (
isEdit || !!extraProps.editInModal ? (
<div className={st.inputPatternWrapper}>
<Input
stringMode
allowClear
disabled={record.fieldType !== 1 || !isEdit}
value={record.inputPattern}
onChange={(e) => onEditTableChange('inputPattern', e.target.value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
{invalidRegexMap[record.fieldId] && (
<Typography.Text type='danger' className={st.inputPatternError}>
请输入合法的正则表达式
</Typography.Text>
)}
</div>
) : (
record.inputPattern
)
) : (
''
),
},
{
title: '校验规则提示',
dataIndex: 'inputPatternMsg',
align: 'center',
width: 220,
render: (_, record, __, extraProps) =>
record.visibleType === 1 ? (
isEdit || !!extraProps.editInModal ? (
<div className={st.inputPatternWrapper}>
<Input
stringMode
allowClear
disabled={record.fieldType !== 1 || !isEdit}
value={record.inputPatternMsg}
onChange={(e) => onEditTableChange('inputPatternMsg', e.target.value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
</div>
) : (
record.inputPatternMsg
)
) : (
''
),
},
{
title: '数据精度',
dataIndex: 'dataPrecision',
align: 'center',
width: 150,
render: (_, record, __, extraProps) =>
isEdit || !!extraProps.editInModal ? (
<InputNumber
stringMode
allowClear
min={1}
step={1}
disabled={!isEdit}
value={record.dataPrecision === null ? null : Number(record.dataPrecision)}
parser={(value) => {
const parsed = value.replace(/[^\d]/g, '');
return parsed ? parseInt(parsed, 10) : null;
}}
formatter={(value) => (value ? `${value}` : '')}
onChange={(value) => onEditTableChange('dataPrecision', value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
) : (
<div style={{ textAlign: 'right' }}>{record.dataPrecision}</div>
),
},
{
title: '小数点位数',
dataIndex: 'dataScale',
align: 'center',
width: 150,
render: (_, record, __, extraProps) =>
numberVisibleTypes.includes(record.visibleType) ? (
isEdit || !!extraProps.editInModal ? (
<InputNumber
stringMode
allowClear
min={1}
step={1}
disabled={record.fieldType !== 1 || !isEdit}
value={record.dataScale === null ? null : Number(record.dataScale)}
parser={(value) => {
const parsed = value.replace(/[^\d]/g, '');
return parsed ? parseInt(parsed, 10) : null;
}}
formatter={(value) => (value ? `${value}` : '')}
onChange={(value) => onEditTableChange('dataScale', value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
) : (
<div style={{ textAlign: 'right' }}>{record.dataScale}</div>
)
) : (
''
),
},
{
title: '最小值',
dataIndex: 'minValue',
align: 'center',
width: 150,
render: (_, record, __, extraProps) =>
numberVisibleTypes.includes(record.visibleType) ? (
isEdit || !!extraProps.editInModal ? (
<InputNumber
stringMode
allowClear
disabled={record.fieldType !== 1 || !isEdit}
value={record.minValue}
onChange={(value) => onEditTableChange('minValue', value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
) : (
<div style={{ textAlign: 'right' }}>{record.minValue}</div>
)
) : (
''
),
},
{
title: '最大值',
dataIndex: 'maxValue',
align: 'center',
width: 150,
render: (_, record, __, extraProps) =>
numberVisibleTypes.includes(record.visibleType) ? (
isEdit || !!extraProps.editInModal ? (
<InputNumber
stringMode
allowClear
disabled={record.fieldType !== 1 || !isEdit}
value={record.maxValue}
onChange={(value) => onEditTableChange('maxValue', value, record.fieldId)}
style={{ width: '100%' }}
{...extraProps}
/>
) : (
<div style={{ textAlign: 'right' }}>{record.maxValue}</div>
)
) : (
''
),
},
{ title: '标签', dataIndex: 'tags', width: 100 },
{
title: '操作',
dataIndex: 'operation',
fixed: 'right',
width: 100,
render: (_, record) => (
<PermissionWrapper code='udop:metadata:settings:edit_table'>
<Button
type='link'
size='small'
onClick={() => handleEdit(record)}
key='edit'
disabled={isEdit}
style={{ padding: 0 }}
>
编辑
</Button>
</PermissionWrapper>
),
},
];
const onItemClick = async (node) => {
let {
key,
tableData: { tableName, displayName },
} = node;
tableId.current = key;
// let path = `/systemConfig/tableStructure?tableId=${tableIdVal}&tableName=${tableNameVal}&tabTitle=${tabTitleVal}`;
let path = `/systemConfig/tableStructure?tableId=${key}&tableName=${tableName}&tabTitle=${encodeURIComponent(
displayName
)}`;
// for (let item of tabs) {
// if (decodeURIComponent(item.path) == path) {
// updateTab({
// ...item,
// label: '表结构详情 - ' + displayName,
// });
// }
// }
// await getDetailInfo();
const children = (
<>
<LayoutBreadcrumb />
<div className={st.content}>
<Suspense fallback={<FullScreenLoading state='loading' />}>
<TableStructurePage tableId={key} tableName={tableName} />
</Suspense>
</div>
</>
);
updateTab({
key: path,
label: '表结构详情 - ' + displayName,
children,
path: path,
});
setActiveTab(path);
router.push(path);
};
const onDragEnd = ({ active, over }) => {
if (active.id !== over?.id) {
setEditDataSource((prevState) => {
const activeIndex = prevState.findIndex((record) => record.fieldId === active?.id);
const overIndex = prevState.findIndex((record) => record.fieldId === over?.id);
return arrayMove(prevState, activeIndex, overIndex);
});
}
};
const tableComponents = useMemo(
() => ({
body: {
row: (rowProps) => <Row {...rowProps} isEdit={isEdit} />,
},
}),
[isEdit]
);
const handleViewDataDetail = () => {
window.open(`/data/detail?tableId=${tableId.current}&tableName=${detailInfo?.tableName}`), '_blank';
};
return (
<div className={st.wrapper}>
<div className={st.menu}>
<SearchTables defaultSelectKey={tableId.current} onItemClick={onItemClick} isFixSelectedKeys={true} />
</div>
<div className={st.content}>
<StateWrapper state={detailLoading ? 'loading' : ''}>
<div className={st.title}>数据表详情</div>
<div className={st.basicInfo}>
<Descriptions bordered items={items} className={st.listItem} column={2} />
</div>
<div className={st.table}>
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext items={editDataSource.map((i) => i.fieldId)} strategy={verticalListSortingStrategy}>
<CustomProTable
rowKey='fieldId'
// components={{ body: { row: Row } }}
components={tableComponents}
columns={columns}
dataSource={editDataSource}
pagination={false}
scroll={{ x: 'max-content', y: 'calc(100vh - 38.7rem)' }}
options={{
fullScreen: false,
reload: false,
density: false,
}}
columnsState={{
persistenceKey: 'pro-table-field-list',
persistenceType: 'localStorage',
}}
toolBarRender={() => (
<>
{!isEdit ? (
<PermissionWrapper code='udop:metadata:settings:edit_table'>
<Button className={st.editButton} type='primary' onClick={() => setIsEdit(true)}>
编辑
</Button>
</PermissionWrapper>
) : (
<>
<Button
className={st.cancelButton}
onClick={() => {
setIsEdit(false);
setEditDataSource(JSON.parse(JSON.stringify(dataSource)));
}}
>
取消
</Button>
<Button
className={st.saveButton}
type='primary'
loading={saveLoading}
onClick={batchUpdateFields}
>
保存
</Button>
</>
)}
<PermissionWrapper code='udop:metadata:data_maintenance'>
<Tooltip title='查看数据详情'>
<Button
type='link'
icon={<ExportOutlined />}
onClick={handleViewDataDetail}
style={{ paddingRight: 0 }}
/>
</Tooltip>
</PermissionWrapper>
</>
)}
headerTitle={<div className={st.title}>数据表设置</div>}
size='large'
/>
</SortableContext>
</DndContext>
</div>
</StateWrapper>
</div>
{editModalVisible && (
<EditFieldModal
visible={editModalVisible}
setVisible={setEditModalVisible}
columns={columns}
dataSource={dataSource}
setEditDataSource={setEditDataSource}
selectedEditData={selectedEditData}
setEditSelectedData={setEditSelectedData}
batchUpdateFields={batchUpdateFields}
/>
)}
{editOrderOptionModalVisible && (
<EditOrderOptionModal
visible={editOrderOptionModalVisible}
setVisible={setEditOrderOptionModalVisible}
dataSource={dataSource}
detailInfo={detailInfo}
setDetailInfo={setDetailInfo}
/>
)}
</div>
);
}
Home.getLayout = function getLayout(page) {
return <Layout>{page}</Layout>;
};
你需要把里面的中文都提取出来(注释内的不需要),每一个都作为object的key,对应的value是一个数组,数组第一个元素和key相同,第二个元素是key翻译出来的英文,直接给结果