Commit 1e0e1b61 by zhaochengxiang

增加模型索引

parent 5736f07a
......@@ -9,7 +9,7 @@ import AssetModal from "./AssetModal"
import AssetDetail from "./AssetDetail"
import AssetItem from './AssetItem';
import { dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
import { showMessage, showNotifaction } from '../../../../util';
import "./AssetTable.less";
......@@ -205,8 +205,12 @@ const AssetTable = (props) =>{
refresh && getTable();
}
const onImportElementVisibleChange = (visible = false) => {
const onImportElementVisibleChange = (visible = false, tip = '') => {
setImportElementVisible(visible);
if (tip && tip!== '') {
showNotifaction('导入提示', tip, 5);
}
}
const onFilterElementVisibleChange = (visible = false, refresh = false) => {
......
......@@ -46,7 +46,7 @@ const ImportElement = (props) => {
callback: data => {
setConfirmLoading(false);
reset();
onCancel && onCancel();
onCancel && onCancel(false, data);
},
error: () => {
setConfirmLoading(false);
......
......@@ -3,7 +3,7 @@ import { Select } from 'antd';
import ImportActionHeader from './ImportActionHeader';
import ImportActionTable from './ImportActionTable';
// import ImportActionIndex from './ImportActionIndex';
import ImportActionIndex from './ImportActionIndex';
import { dispatchLatest, dispatch } from '../../../../model';
......@@ -82,8 +82,11 @@ const ImportAction = (props) => {
data
},
callback: data => {
setModelerData({...modelerData, easyDataModelerDataModelAttributes: [...data.easyDataModelerDataModelAttributes] })
onChange && onChange(data||{});
let newModelerData = {...(data||{})};
newModelerData = { ...newModelerData, easyDataModelerIndices: getIndicesWhenTableChange(newModelerData) }
setModelerData(newModelerData)
onChange && onChange(newModelerData);
}
})
}
......@@ -149,6 +152,7 @@ const ImportAction = (props) => {
});
const newModelerData = {...modelerData, easyDataModelerModelingTemplate: currentTemplate };
setModelerData(newModelerData)
onChange && onChange(newModelerData);
......@@ -168,8 +172,10 @@ const ImportAction = (props) => {
//validate 是否需要对字段进行校验
const onTableChange = (data, validate=false) => {
const newModelerData = {...modelerData, easyDataModelerDataModelAttributes: data};
let newModelerData = {...modelerData, ...{easyDataModelerDataModelAttributes: data}};
newModelerData = { ...newModelerData, easyDataModelerIndices: getIndicesWhenTableChange(newModelerData) };
setModelerData(newModelerData);
onChange && onChange(newModelerData);
......@@ -190,6 +196,35 @@ const ImportAction = (props) => {
})
}
const onIndexChange = (data) => {
const newModelerData = {...modelerData, easyDataModelerIndices: data};
setModelerData(newModelerData);
onChange && onChange(newModelerData);
}
const getIndicesWhenTableChange = (newModelerData) => {
const newEasyDataModelerIndices = [...(newModelerData.easyDataModelerIndices||[])];
(newModelerData.easyDataModelerIndices||[]).forEach((easyDataModelerIndex, index) => {
const newIndexedEasyDataModelAttributes = [], newIndexedAttributeOrders = [];
(easyDataModelerIndex.indexedEasyDataModelAttributes||[]).forEach((indexedEasyDataModelAttribute, _index) => {
const __index = (newModelerData.easyDataModelerDataModelAttributes||[]).findIndex(item => item.iid === indexedEasyDataModelAttribute.iid);
if (__index !== -1) {
newIndexedEasyDataModelAttributes.push({...newModelerData.easyDataModelerDataModelAttributes[__index]});
newIndexedAttributeOrders.push(easyDataModelerIndex.indexedAttributeOrders[_index]);
}
})
const item = newEasyDataModelerIndices[index];
newEasyDataModelerIndices.splice(index, 1, {...item, ...{ indexedEasyDataModelAttributes: newIndexedEasyDataModelAttributes, indexedAttributeOrders: newIndexedAttributeOrders }} )
})
return newEasyDataModelerIndices.filter(item => (item.indexedEasyDataModelAttributes||[]).length > 0);
}
return (
<>
{
......@@ -219,8 +254,15 @@ const ImportAction = (props) => {
validateReports={validateReports}
supportedDatatypes={supportedDatatypes}
onChange={onTableChange}
editable={action!=='detail'} />
{/* <ImportActionIndex data={indexData} fileds={fileds} onChange={onIndexChange} editable={action!=='detail'} /> */}
editable={action!=='detail'}
/>
<ImportActionIndex
modelerData={modelerData||{}}
constraint={constraint}
template={template}
onChange={onIndexChange}
editable={action!=='detail'}
/>
</>
);
};
......
import React, { useState, useCallback, useRef } from 'react';
import { Table, Input, Form, Typography, Divider, Button, Select } from 'antd';
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { Table, Input, Form, Typography, Divider, Button, Select, Row, Col, Popover, Checkbox, Tooltip } from 'antd';
import { QuestionCircleOutlined, DeleteOutlined } from '@ant-design/icons';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
import './ImportActionTable.less';
const { Option } = Select;
const type = 'DragableIndexBodyRow';
const modes = [
'主键索引',
'唯一索引',
'普通索引',
'组合索引',
'全文索引'
];
const AttributesInputItem = ({ indexedAttribute = null, indexedAttributeOrder = null, attributes, onAttributeChange, onOrderChange, onDelete , className }) => {
return (
<Row align='middle' className={className} >
<Col span={4}>
<span>字段名称:</span>
</Col>
<Col span={6}>
<Select
onChange={(value) => { onAttributeChange && onAttributeChange(value) }}
value={indexedAttribute ? (indexedAttribute.name||'') : ''}
placeholder='请选择字段名称'
>
{
(attributes||[]).map((attribute, index) => {
return (
((attribute.name||'')==='') ? null : <Option key={index} value={attribute.iid||''}>{attribute.name||''}</Option>
);
})
}
</Select>
</Col>
<Col span={1}></Col>
<Col span={2}>
<span>排序:</span>
</Col>
<Col span={6}>
<Select
onChange={(value) => { onOrderChange && onOrderChange(value) }}
value={indexedAttributeOrder||''}
placeholder='请选择排序方式'
>
<Option value='DESC'>DESC</Option>
<Option value='ASC'>ASC</Option>
</Select>
</Col>
<Col span={1}></Col>
<Col span={2}>
<Tooltip title="删除">
<Button type="text" icon={<DeleteOutlined />} onClick={onDelete} />
</Tooltip>
</Col>
</Row>
);
}
const AttributesInput = ({ value = {}, attributes, onChange }) => {
const { indexedEasyDataModelAttributes, indexedAttributeOrders } = value;
const onAttributeChange = (value, index) => {
const newIndexedEasyDataModelAttributes = [...indexedEasyDataModelAttributes];
const _index = attributes.findIndex(item => item.iid === value);
newIndexedEasyDataModelAttributes.splice(index, 1, {...attributes[_index]});
triggerChange({
indexedEasyDataModelAttributes: newIndexedEasyDataModelAttributes
});
}
const onOrderChange = (value, index) => {
const newIndexedAttributeOrders = [...indexedAttributeOrders];
newIndexedAttributeOrders.splice(index, 1, value);
triggerChange({
indexedAttributeOrders: newIndexedAttributeOrders
})
}
const onItemDelete = (index) => {
const newIndexedEasyDataModelAttributes = [...indexedEasyDataModelAttributes];
const newIndexedAttributeOrders = [...indexedAttributeOrders];
newIndexedEasyDataModelAttributes.splice(index, 1);
newIndexedAttributeOrders.splice(index, 1);
if (newIndexedEasyDataModelAttributes.length === 0) {
newIndexedEasyDataModelAttributes.push({});
}
if (newIndexedAttributeOrders.length === 0) {
newIndexedAttributeOrders.push('');
}
triggerChange({
indexedEasyDataModelAttributes: newIndexedEasyDataModelAttributes,
indexedAttributeOrders: newIndexedAttributeOrders,
});
}
const addAttribute = () => {
triggerChange({
indexedEasyDataModelAttributes: [...indexedEasyDataModelAttributes, {}],
indexedAttributeOrders: [...indexedAttributeOrders, ''],
})
}
const triggerChange = (changedValue) => {
onChange && onChange({
...value,
...changedValue,
});
};
return (
<>
{
(indexedEasyDataModelAttributes||[]).map((indexedAttribute, index) => {
return (
<AttributesInputItem
key={index}
className='mb-2'
indexedAttribute={indexedAttribute}
indexedAttributeOrder={indexedAttributeOrders[index]||''}
attributes={attributes}
onAttributeChange={(value) => { onAttributeChange(value, index) } }
onOrderChange={(value) => { onOrderChange(value, index) }}
onDelete={() => { onItemDelete(index) }}
/>
);
})
}
<Button onClick={addAttribute}>新增字段</Button>
</>
)
}
const EditableCell = ({
editing,
dataIndex,
title,
colTitle,
inputType,
fileds,
record,
index,
attributes,
children,
...restProps
}) => {
let inputNode = <Input />;
if (inputType==='select') {
inputNode = (
<Select>
{
modes && modes.map((mode, index) => {
return (
<Option key={mode}>{mode}</Option>
);
})
}
</Select>
)
} else if (inputType === 'select-multiple') {
inputNode = (
<Select mode="multiple">
{
fileds && fileds.map((filed, index) => {
return (
<Option key={filed}>{filed}</Option>
)
})
}
</Select>
)
}
let editingComponent = null;
if (editing) {
if (dataIndex !== 'attributesWithOrders') {
return (
<td {...restProps}>
{editing ? (
const inputNode = inputType === 'check' ? <Checkbox /> : <Input />
editingComponent = (
<Form.Item
name={dataIndex}
style={{
margin: 0,
}}
valuePropName={(inputType==='check')? 'checked': 'value'}
rules={[
{
required: true,
message: `请输入${title}!`,
required: (inputType === 'text'),
message: `请输入${colTitle}!`,
},
]}
>
{inputNode}
{ inputNode }
</Form.Item>
);
} else {
editingComponent = (
<Form.Item
name={dataIndex}
style={{
margin: 0,
}}
valuePropName={'value'}
rules={[
{
required: true,
message: `请输入${colTitle}!`,
},
]}
>
<AttributesInput attributes={attributes} />
</Form.Item>
)
}
}
return (
<td {...restProps}>
{editing ? (
editingComponent
) : (
children
)}
......@@ -127,19 +252,47 @@ const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) =>
};
const ImportActionIndex = (props) => {
const { data, fileds, onChange, editable } = props;
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
const [suggests, setSuggests] = useState([]);
const isEditing = (record) => record.key === editingKey;
const { modelerData, onChange, editable, constraint, template } = props;
const onAddClick = () => {
const newData = [{}, ...data];
const [ attributes, setAttributes ] = useState([]);
const [ data, setData ] = useState([]);
const [ form ] = Form.useForm();
const [ editingKey, setEditingKey ] = useState(null);
const [ keyword, setKeyword ] = useState('');
const [ filterData, setFilterData ] = useState([]);
//规则改变的时候 数据表为可编辑状态
useEffect(() => {
setEditingKey(null);
}, [constraint, template])
useEffect(() => {
setAttributes(modelerData.easyDataModelerDataModelAttributes||[]);
setData(modelerData.easyDataModelerIndices||[]);
const _filterData = (modelerData.easyDataModelerIndices||[]).filter(item => (item.name||'').indexOf(keyword)!==-1);
const __filterData = [];
(_filterData||[]).forEach(item => {
(newData||[]).forEach((item, index) => {
item.key = index.toString();
__filterData.push({...item, ...{ attributesWithOrders: {
indexedEasyDataModelAttributes: item.indexedEasyDataModelAttributes||[],
indexedAttributeOrders: item.indexedAttributeOrders||[],
} }});
})
setFilterData(__filterData);
}, [modelerData, keyword])
const isEditing = (record) => record.name === editingKey;
const onAddClick = () => {
const newData = [{name: ''}, ...data];
onChange && onChange(newData);
edit(newData[0]);
......@@ -148,16 +301,19 @@ const ImportActionIndex = (props) => {
const edit = (record) => {
form.setFieldsValue({
name: '',
mode: '',
fileds: [],
attributesWithOrders: {
indexedEasyDataModelAttributes: [{}],
indexedAttributeOrders: [''],
},
unique: false,
...record,
});
setEditingKey(record.key);
setEditingKey(record.name);
};
const remove = (record) => {
const newData = [...data];
const index = newData.findIndex((item) => record.key === item.key);
const index = newData.findIndex((item) => record.name === item.name);
newData.splice(index, 1);
onChange && onChange(newData);
}
......@@ -165,129 +321,196 @@ const ImportActionIndex = (props) => {
const cancel = () => {
const newData = [...data];
const item = newData[editingKey];
const index = newData.findIndex((item) => editingKey === item.name);
const item = newData[index];
if (!item.name || item.name==='') {
newData.splice(editingKey, 1);
newData.splice(index, 1);
onChange && onChange(newData);
}
setEditingKey('');
setEditingKey(null);
};
const save = async () => {
const save = async() => {
try {
await form.validateFields();
const _suggests = [
{
name: '建议1'
},
{
name: '建议2'
const row = await form.validateFields();
// console.log('row', row);
const newData = [...data];
const index = newData.findIndex((item) => editingKey === item.name);
//判断索引名称是否唯一
const newDataExcludeSelf = [...data];
newDataExcludeSelf.splice(index, 1);
const _index = (newDataExcludeSelf||[]).findIndex(item => item.name === row.name);
if (_index !== -1) {
form.setFields([{ name: 'name', errors: ['索引名称不能重复'] }]);
return;
}
if (/^[a-zA-Z]+$/.test(row.name) === false) {
form.setFields([{ name: 'name', errors: ['索引名称必须全是字母'] }]);
return;
}
const _indexedEasyDataModelAttributes = [], _indexedAttributeOrders = [];
row.attributesWithOrders.indexedEasyDataModelAttributes.forEach((item, index) => {
if ((item.iid||'')!=='') {
_indexedEasyDataModelAttributes.push(item);
_indexedAttributeOrders.push(row.attributesWithOrders.indexedAttributeOrders[index]);
}
];
setSuggests(_suggests)
})
if (_suggests.length === 0) {
constraintSave();
if (_indexedEasyDataModelAttributes.length === 0) {
form.setFields([{ name: 'attributesWithOrders', errors: ['必须选择字段'] }]);
return;
}
newData.splice(index, 1, {...{
name: row.name,
unique: row.unique,
indexedEasyDataModelAttributes: _indexedEasyDataModelAttributes,
indexedAttributeOrders: _indexedAttributeOrders,
}});
onChange && onChange(newData, true);
setEditingKey(null);
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
};
const constraintSave = async () => {
const row = await form.validateFields();
//test
row.key = '-1';
const newData = [...data];
const index = newData.findIndex((item) => editingKey === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
} else {
newData.push(row);
}
onChange && onChange(newData);
setEditingKey('');
setSuggests([]);
}
const onValuesChange = (changedValues, allValues) => {
// console.log('changed values', changedValues);
// console.log('all values', allValues);
};
const columns = [
{
title: '',
width: 48,
},
{
title: '序号',
dataIndex: 'key',
editable: false,
render: (text, record, index) => {
width: 50,
render: (_, __, index) => {
return (index+1).toString();
}
},
{
title: '索引名称',
width: 200,
dataIndex: 'name',
editable: true,
ellipsis: true,
},
{
title: '索引类型',
dataIndex: 'mode',
title: '是否唯一索引',
width: 200,
dataIndex: 'unique',
editable: true,
render: (unique, _, __) => {
if (unique === false) {
return '否';
} else if (unique === true) {
return '是';
}
return '';
}
},
{
title: '索引字段列表',
dataIndex: 'fileds',
dataIndex: 'attributesWithOrders',
editable: true,
ellipsis: true,
render: (_, record, index) => {
return (
<div>
{
(record.indexedEasyDataModelAttributes||[]).map((item, index) => {
const order = record.indexedAttributeOrders[index]||'';
const _text = `字段: ${item.name||''} 排序: ${order||''}`;
return (
<Row key={index}><span>{_text}</span></Row>
)
})
}
</div>
);
}
},
];
const editableColumn = [
...columns,
{
title: '操作',
dataIndex: 'action',
width: 100,
render: (_, record) => {
if (!editable) return null;
return isEditing(record) ? (
<>
<Typography.Link className='mr-3' disabled={editingKey === ''} onClick={() => save()}>
<Typography.Link className='mr-3' disabled={editingKey===null} onClick={() => save()}>
保存
</Typography.Link>
<Typography.Link disabled={editingKey === ''} onClick={() => {cancel()}}>
<Typography.Link disabled={editingKey===null} onClick={() => {cancel()}}>
取消
</Typography.Link>
</>
) : (
<>
<Typography.Link className='mr-3' disabled={editingKey !== ''} onClick={() => edit(record)}>
<Typography.Link className='mr-3' disabled={editingKey!==null} onClick={() => edit(record)}>
编辑
</Typography.Link>
<Typography.Link disabled={editingKey !== ''} onClick={() => remove(record)}>
<Typography.Link className='mr-3' disabled={editingKey!==null} onClick={() => remove(record)}>
删除
</Typography.Link>
</>
);
},
},
];
]
const mergedColumns = columns.map((col) => {
if (!col.editable) {
return col;
const mergedColumns = () => {
if (editable) {
let _columns = editableColumn;
return _columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
dataIndex: col.dataIndex,
inputType: (col.dataIndex==='unique') ? 'check' : 'text',
colTitle: col.title,
editing: isEditing(record),
attributes,
}),
};
});
}
return {
...col,
onCell: (record) => ({
record,
inputType: (col.dataIndex==='fileds') ? 'select-multiple' : (col.dataIndex==='mode'?'select':'text'),
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
fileds: fileds||[]
}),
};
});
return columns;
}
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
......@@ -305,74 +528,68 @@ const ImportActionIndex = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps
[data],
);
const onSearchInputChange = (e) => {
setEditingKey(null);
setKeyword(e.target.value||'');
}
return (
<>
<Divider>数据表索引</Divider>
{
editable && <div className='d-flex mb-3'>
<Button type="primary" onClick={onAddClick} style={{ marginLeft: 'auto' }} disabled={ editingKey!=='' } >新增行</Button>
<div className='model-import-action-index mt-7'>
<Divider>
<>
<span>数据表索引</span>
{ editable && (
<Popover content='表格可以通过拖拽来排序'>
<QuestionCircleOutlined className='ml-1 pointer' />
</Popover>
)
}
</>
</Divider>
<div className='d-flex mb-3' style={{ justifyContent: 'space-between' }}>
<div className='d-flex' style={{ alignItems: 'center' }}>
<span className='mr-3'>索引搜索:</span>
<Input
placeholder="请输入索引名称"
allowClear
value={keyword}
onChange={onSearchInputChange}
style={{ width: 230 }}
/>
</div>
}
{
editable && <Button className='ml-3' type="primary" onClick={onAddClick} disabled={ editingKey!==null || keyword!=='' } >新增行</Button>
}
</div>
<DndProvider backend={HTML5Backend} >
<Form form={form} component={false}>
<Form form={form} component={false} onValuesChange={onValuesChange}>
<Table
components={{
body: {
cell: EditableCell,
//编辑状态下不允许拖动
row: (editable&&editingKey==='')?DragableBodyRow:null,
//编辑或者搜索状态下不允许拖动
row: (editable&&editingKey===null&&keyword==='')?DragableBodyRow:null,
},
}}
onRow={(record, index) => {
if (!editable || editingKey!=='') return null;
if (!editable || editingKey!==null || keyword.length>0) return null;
return {
index,
moveRow
}
}}
dataSource={data}
columns={mergedColumns}
dataSource={filterData||[]}
columns={mergedColumns()}
size='small'
rowKey='name'
rowClassName="editable-row"
pagination={false}
expandable={{
expandedRowRender: record => (
<>
{
editingKey!=='' && <>
{
suggests && suggests.length>0 && (
<>
<Divider orientation="left">建议</Divider>
<div className='mb-3 ml-7'>
{
suggests && suggests.map((suggest, index) => {
return (
<div key={index} className='mt-3'>{suggest.name||''}</div>
)
})
}
</div>
<Button className='mb-3 ml-7' type='primary' onClick={constraintSave}>强制保存</Button>
</>
)
}
</>
}
</>
),
expandIcon: ({ expanded, onExpand, record }) => {
return <></>;
},
rowExpandable: record => (editingKey!==''&&(suggests||[]).length>0),
expandedRowKeys: [editingKey]
}}
/>
</Form>
</DndProvider>
</>
</div>
);
};
......
......@@ -220,7 +220,7 @@ const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) =>
const ImportActionTable = (props) => {
const { modelerData, onChange, editable, supportedDatatypes, constraint, template, validateReports } = props;
const data = modelerData.easyDataModelerDataModelAttributes||[];
const [ data, setData ] = useState([]);
const [ form ] = Form.useForm();
const [ editingKey, setEditingKey ] = useState('');
......@@ -228,6 +228,9 @@ const ImportActionTable = (props) => {
const [ keyword, setKeyword ] = useState('');
const [ filterData, setFilterData ] = useState([]);
const onChangeRef = useRef();
onChangeRef.current = onChange;
//规则改变的时候 数据表为可编辑状态
useEffect(() => {
......@@ -236,6 +239,7 @@ const ImportActionTable = (props) => {
useEffect(() => {
setData(modelerData.easyDataModelerDataModelAttributes||[]);
setFilterData((modelerData.easyDataModelerDataModelAttributes||[]).filter(item => (item.name||'').indexOf(keyword)!==-1 || (item.cnName).indexOf(keyword)!==-1));
}, [modelerData, keyword])
......@@ -287,14 +291,17 @@ const ImportActionTable = (props) => {
try {
const row = await form.validateFields();
if ((row.datatype.name||'')==='') {
form.setFields([{ name: 'datatype', errors: ['必须选择类型'] }]);
return;
}
(row.datatype.parameterNames||[]).forEach((parameterName, index) => {
if (!row.datatype.parameterValues[index] || row.datatype.parameterValues[index]==='') {
row.datatype.parameterValues[index] = 0;
}
})
// console.log('row', row);
const newData = [...data];
const index = newData.findIndex((item) => editingKey === item.iid);
......@@ -392,7 +399,7 @@ const ImportActionTable = (props) => {
dataIndex: 'nullable',
editable: true,
render: (nullable, record, index) => {
if (nullable === false) {
if (!nullable) {
return '否';
} else if (nullable === true) {
return '是';
......@@ -407,7 +414,7 @@ const ImportActionTable = (props) => {
dataIndex: 'partOfPrimaryKey',
editable: true,
render: (partOfPrimaryKey, record, index) => {
if (partOfPrimaryKey === false) {
if (!partOfPrimaryKey) {
return '否';
} else if (partOfPrimaryKey === true) {
return '是';
......@@ -422,7 +429,7 @@ const ImportActionTable = (props) => {
dataIndex: 'partOfDistributionKey',
editable: true,
render: (partOfDistributionKey, record, index) => {
if (partOfDistributionKey === false) {
if (!partOfDistributionKey) {
return '否';
} else if (partOfDistributionKey === true) {
return '是';
......@@ -574,7 +581,7 @@ const ImportActionTable = (props) => {
],
});
onChange && onChange(newData);
onChangeRef.current && onChangeRef.current(newData);
},
//eslint-disable-next-line react-hooks/exhaustive-deps
[data],
......@@ -665,7 +672,7 @@ const ImportActionTable = (props) => {
</>
),
expandIcon: ({ expanded, onExpand, record }) => {
return <></>;
return null;
},
rowExpandable: record => (editingKey!==''&&(suggests||[]).length>0),
expandedRowKeys: [editingKey]
......
......@@ -110,7 +110,7 @@ const ImportModal = (props) => {
const cancel = () => {
reset();
onCancel && onCancel(true);
onCancel && onCancel();
}
const save = async () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment