Commit ab593e04 by zhaochengxiang

主数据模版字段编辑

parent f256b872
...@@ -167,4 +167,147 @@ export const expandedTableData = [ ...@@ -167,4 +167,147 @@ export const expandedTableData = [
attr5: 'attr5', attr5: 'attr5',
attr6: 'attr6', attr6: 'attr6',
}, },
];
export const templateFileds = [
{
"name": "company_short_name",
"remark": "公司简称",
"cnName": "公司简称",
"modelingTemplateTag": null,
"datatype": {
"name": "Varchar",
"cnName": "字符串",
"supportedDBTypes": [
"Greenplum",
"MySQL"
],
"parameterNames": [
"length"
],
"parameterCnNames": [
"长度"
],
"parameterValues": [
68
],
"jdbctype": 12
},
"recommendedStats": null,
"iid": "fc3ad8bb-1ebd-4eb2-ae0e-5cc8305bced3",
"isPossibleNewTerm": null,
"isPossibleNewRecommendedDefinition": null,
"notNull": false,
"foreignKey": false,
"defaultValue": "",
"needAttention": false,
"definition": null,
"valueRange": null,
"partOfPrimaryKeyLogically": false
},
{
"name": "counterparty_executing_trader",
"remark": "对手方交易账户",
"cnName": "对手方交易账户",
"modelingTemplateTag": null,
"datatype": {
"name": "Char",
"cnName": "字符",
"supportedDBTypes": [
"Greenplum",
"MySQL"
],
"parameterNames": [
"length"
],
"parameterCnNames": [
"长度"
],
"parameterValues": [
13
],
"jdbctype": 1
},
"recommendedStats": null,
"iid": "9f0642cc-f9b6-4644-acc5-a6f146c5f89e",
"isPossibleNewTerm": null,
"isPossibleNewRecommendedDefinition": null,
"notNull": false,
"foreignKey": false,
"defaultValue": "",
"needAttention": false,
"definition": null,
"valueRange": null,
"partOfPrimaryKeyLogically": false
},
{
"name": "counterparty_member_branch_code",
"remark": "对手方营业部识别码",
"cnName": "对手方营业部识别码",
"modelingTemplateTag": null,
"datatype": {
"name": "Char",
"cnName": "字符",
"supportedDBTypes": [
"Greenplum",
"MySQL"
],
"parameterNames": [
"length"
],
"parameterCnNames": [
"长度"
],
"parameterValues": [
15
],
"jdbctype": 1
},
"recommendedStats": null,
"iid": "95070aa0-74de-4e97-9227-1952a875054e",
"isPossibleNewTerm": null,
"isPossibleNewRecommendedDefinition": null,
"notNull": false,
"foreignKey": false,
"defaultValue": "",
"needAttention": false,
"definition": null,
"valueRange": null,
"partOfPrimaryKeyLogically": false
},
{
"name": "record_sequence",
"remark": "记录序号",
"cnName": "记录序号",
"modelingTemplateTag": null,
"datatype": {
"name": "Char",
"cnName": "字符",
"supportedDBTypes": [
"Greenplum",
"MySQL"
],
"parameterNames": [
"length"
],
"parameterCnNames": [
"长度"
],
"parameterValues": [
6
],
"jdbctype": 1
},
"recommendedStats": null,
"iid": "ed72ad1a-9455-4be0-b093-2af2621a766e",
"isPossibleNewTerm": null,
"isPossibleNewRecommendedDefinition": null,
"notNull": false,
"foreignKey": false,
"defaultValue": "",
"needAttention": false,
"definition": null,
"valueRange": null,
"partOfPrimaryKeyLogically": false
}
]; ];
\ No newline at end of file
import { Form, Input, Divider } from "antd"; import { Form, Input, Divider, Row, Col } from "antd";
const { TextArea } = Input;
const UpdateBasicInfo = (props) => { const UpdateBasicInfo = (props) => {
const { form } = props; const { form } = props;
...@@ -16,20 +18,28 @@ const UpdateBasicInfo = (props) => { ...@@ -16,20 +18,28 @@ const UpdateBasicInfo = (props) => {
return ( return (
<div> <div>
<Divider>基本信息</Divider> <h3>基本信息</h3>
<Form <Form
form={form} form={form}
{...formItemLayout} {...formItemLayout}
> >
<Form.Item label='模版名称' name='name' rules={[{ required: true, message: '请填写模版名称' }]}> <Row gutter={10}>
<Input /> <Col xs={24} sm={24} lg={12}>
</Form.Item> <Form.Item label='中文名称' name='cnName' rules={[{ required: true, message: '请填写模版中文名称' }]}>
<Form.Item label='模版中文名称' name='cnName' rules={[{ required: true, message: '请填写模版中文名称' }]}> <Input />
<Input /> </Form.Item>
</Form.Item> </Col>
<Form.Item label='模版描述' name='desc'> <Col xs={24} sm={24} lg={12}>
<Input /> <Form.Item label='英文名称' name='name' rules={[{ required: true, message: '请填写模版名称' }]}>
</Form.Item> <Input />
</Form.Item>
</Col>
<Col xs={24} sm={24} lg={12}>
<Form.Item label='模版描述' name='desc'>
<TextArea row={4} />
</Form.Item>
</Col>
</Row>
</Form> </Form>
</div> </div>
); );
......
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { Tooltip, Table, Space, Popover, Input, Button, Form } from "antd";
import { QuestionCircleOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import { useClickAway } from 'ahooks';
import update from 'immutability-helper';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import DebounceInput from "../../Model/Component/DebounceInput";
import { templateFileds } from "./Mock";
import { inputWidth, generateUUID } from "../../../../util";
import { DatatypeInput, EditableCell, DragableBodyRow } from "../../Model/Component/ImportActionTable";
import { dispatch } from "../../../../model";
const InputDebounce = DebounceInput(300)(Input);
const UpdateField = (props) => {
const {editable = true} = props;
const [data, setData] = useState(templateFileds);
const [keyword, setKeyword] = useState('');
const [editingKey, setEditingKey] = useState('');
const [insertIndex, setInsertIndex] = useState(0);
const [supportedDatatypes, setSupportedDatatypes] = useState([]);
const [form] = Form.useForm();
const tableRef = useRef(null);
const columns = [
{
title: '序号',
dataIndex: 'key',
editable: false,
width: 60,
render: (_, __, index) => {
return (index+1).toString();
}
},
{
title: '中文名称',
width: 200,
dataIndex: 'cnName',
editable: true,
ellipsis: true,
require: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text}</span>
</Tooltip>
)
}
},
{
title: '英文名称',
width: 200,
dataIndex: 'name',
editable: true,
ellipsis: true,
require: true,
render: (text, record, index) => {
return (
<Tooltip title={text||''}>
<span style={{ fontWeight: 'bold' }} >{text}</span>
</Tooltip>
)
}
},
{
title: '类型',
width: (editingKey!=='')?250:150,
dataIndex: 'datatype',
editable: true,
ellipsis: true,
require: true,
render: (_, record, __) => {
if (record?.datatype) {
if ((record?.datatype?.name==='Char'||record?.datatype?.name==='Varchar') && record?.datatype?.parameterValues?.length>0) {
return `${record?.datatype?.name||''}(${(record?.datatype?.parameterValues[0]?record.datatype.parameterValues[0]:0)})`;
} else if ((record?.datatype?.name==='Decimal'||record?.datatype?.name==='Numeric') && record?.datatype?.parameterValues?.length>1) {
return `${record?.datatype?.name||''}(${(record?.datatype?.parameterValues[0]?record.datatype.parameterValues[0]:0)},${(record?.datatype?.parameterValues[1]?record.datatype.parameterValues[1]:0)})`;
}
return record.datatype.name||'';
}
return '';
}
},
{
title: '业务含义',
dataIndex: 'remark',
editable: true,
ellipsis: true,
require: true,
width: 200,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text}</span>
</Tooltip>
)
}
},
{
title: '操作',
dataIndex: 'action',
width: 90,
fixed: 'right',
render: (_, record) => {
return (
<React.Fragment>
{
editable && <React.Fragment>
{
<React.Fragment>
<Button
className='mr-3'
size='small'
type='text'
icon={<PlusOutlined className='default' />}
onClick={(event) => {
event.stopPropagation();
insertToFrontItem(record);
}}
/>
<Button
className='mr-3'
size='small'
type='text'
icon={<DeleteOutlined style={{ color: 'red' }} />}
onClick={(event) => {
event.stopPropagation();
removeItem(record);
}}
/>
</React.Fragment>
}
</React.Fragment>
}
</React.Fragment>
)
},
},
];
const filterData = useMemo(() => {
return (data||[]).filter(item => (item?.name||'').indexOf(keyword)!==-1 || (item.cnName).indexOf(keyword)!==-1);
}, [keyword, data])
useClickAway(() => {
save();
}, tableRef);
useEffect(() => {
getSupportedDatatypes();
}, [])
const isEditing = (record) => record?.iid === editingKey;
const getSupportedDatatypes = () => {
dispatch({
type: 'datamodel.getSupportedDatatypes',
callback: data => {
setSupportedDatatypes(data||[]);
}
});
}
const onAddClick = (event) => {
event.stopPropagation();
save().then(result => {
if (result) {
setKeyword('');
const iid = generateUUID();
const newData = [...data, { iid }];
setData(newData);
setInsertIndex(newData.length-1);
editItem(newData[newData.length-1], false);
setTimeout(() => {
document.getElementById(`field-${iid}`)?.scrollIntoView();
}, 200)
}
})
}
const insertToFrontItem = (record) => {
save().then(result => {
if (result) {
setKeyword('');
let newData = [...data];
const index = newData.findIndex((item) => record.iid === item.iid);
const iid = generateUUID();
if (index === 0) {
newData = [{ iid }, ...newData];
setInsertIndex(0);
editItem(newData[0], false);
} else {
newData.splice(index, 0, { iid });
setInsertIndex(index);
editItem(newData[index], false);
}
setData(newData);
setTimeout(() => {
document.getElementById(`field-${iid}`)?.scrollIntoView();
}, 200)
}
})
}
const removeItemLogic = (record) => {
const newData = [...data];
const index = newData.findIndex((item) => record.iid === item.iid);
if (index !== -1) {
newData.splice(index, 1);
}
setData(newData);
}
const removeItem = (record) => {
if (record.iid !== editingKey) {
save().then(result => {
if (result) {
removeItemLogic(record);
}
});
} else {
removeItemLogic(record);
}
}
const onSearchInputChange = (value) => {
save().then(result => {
if (result) {
setKeyword(value);
}
});
}
const editItemLogic = (record) => {
form.resetFields();
form.setFieldsValue(record);
setEditingKey(record?.iid);
}
const editItem = (record) => {
save().then((result) => {
if (result) {
editItemLogic(record);
}
});
};
const save = async() => {
try {
if (editingKey!=='') {
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;
}
})
const newData = [...data];
const index = newData.findIndex((item) => editingKey === item.iid);
//判断字段名称是否唯一
let _index;
if (index === -1) {
_index = (data||[]).findIndex(item => item.name === row.name);
} else {
const newDataExcludeSelf = [...data];
newDataExcludeSelf.splice(index, 1);
_index = (newDataExcludeSelf||[]).findIndex(item => item.name === row.name);
}
if (_index !== -1) {
form.setFields([{ name: 'name', errors: ['字段名称不能重复'] }]);
return;
}
let attribute = {};
if (index === -1) {
attribute = {...row, iid: editingKey};
newData.splice(insertIndex, 0, attribute);
} else {
const item = newData[index];
attribute = { ...item, ...row};
newData.splice(index, 1, attribute);
}
setEditingKey('');
setData(newData);
}
return true;
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
return false;
}
};
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
const dragRow = data[dragIndex];
setData(
update(data, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragRow],
],
}),
);
},
[data],
);
const onTableRow = (record, index) => {
let rowParams = {
index,
id: `field-${record.iid}`,
};
if (editable) {
if (!isEditing(record)) {
rowParams = {...rowParams, onClick: (event) => {
event.stopPropagation();
editItem(record);
}
}
if (keyword.length===0) {
rowParams = {...rowParams, moveRow};
}
}
}
return rowParams;
}
const mergedColumns = columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
dataIndex: col.dataIndex,
colTitle: col.title,
editing: isEditing(record),
datatypes: supportedDatatypes,
require: col.require
}),
};
});
return (
<div>
<div className='d-flex mb-3' style={{ justifyContent: 'space-between' }}>
<Space>
<h2 style={{ marginBottom: 0 }}>字段信息</h2>
{
editable && <Popover content='点击行进行编辑,表格可以通过拖拽来排序'>
<QuestionCircleOutlined className='pointer' />
</Popover>
}
</Space>
<Space>
{
editable && <Button onClick={onAddClick} >新建</Button>
}
<div className='d-flex' style={{ alignItems: 'center' }}>
<InputDebounce
placeholder="请输入索引名称"
allowClear
value={keyword}
onChange={onSearchInputChange}
style={{ width: inputWidth }}
/>
</div>
</Space>
</div>
<div id="containerId" ref={tableRef}>
<DndProvider backend={HTML5Backend} >
<Form form={form} component={false}>
<Table
rowKey='iid'
dataSource={filterData}
columns={mergedColumns}
pagination={false}
components={{
body: {
cell: EditableCell,
//编辑或者搜索状态下不允许拖动
row: (editable&&editingKey===''&&keyword==='')?DragableBodyRow:null,
},
}}
onRow={onTableRow}
/>
</Form>
</DndProvider>
</div>
</div>
);
}
export default UpdateField;
\ No newline at end of file
import { Modal, Form, Input, Button } from "antd"; import { Modal, Form } from "antd";
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import UpdateBasicInfo from "./UpdateBasicInfo"; import UpdateBasicInfo from "./UpdateBasicInfo";
import UpdateField from "./UpdateField";
import './UpdateTemplateModal.less'; import './UpdateTemplateModal.less';
const UpdateTemplateModal = (props) => { const UpdateTemplateModal = (props) => {
const { visible, onCancel } = props; const { visible, onCancel } = props;
const [form] = Form.useForm(); const [form] = Form.useForm();
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
},
};
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 18,
offset: 6,
},
},
};
const onFormFinish = async() => { const onFormFinish = async() => {
try { try {
await form.validateFields(); await form.validateFields();
...@@ -45,12 +21,14 @@ const UpdateTemplateModal = (props) => { ...@@ -45,12 +21,14 @@ const UpdateTemplateModal = (props) => {
return ( return (
<Modal <Modal
className='update-template-modal' className='update-template-modal'
width='80%'
title='新建模版' title='新建模版'
visible={visible} visible={visible}
onCancel={onCancel} onCancel={onCancel}
onOk={onFormFinish} onOk={onFormFinish}
> >
<UpdateBasicInfo form={form} /> <UpdateBasicInfo form={form} />
<UpdateField />
</Modal> </Modal>
); );
} }
......
...@@ -3,7 +3,7 @@ import { Spin } from 'antd'; ...@@ -3,7 +3,7 @@ import { Spin } from 'antd';
import LocalStorage from 'local-storage'; import LocalStorage from 'local-storage';
import ImportActionHeader from './ImportActionHeader'; import ImportActionHeader from './ImportActionHeader';
import ImportActionTable from './ImportActionTable'; import { ImportActionTable } from './ImportActionTable';
import ImportActionIndex from './ImportActionIndex'; import ImportActionIndex from './ImportActionIndex';
import { getQueryParam } from '../../../../util'; import { getQueryParam } from '../../../../util';
import { Action } from '../../../../util/constant'; import { Action } from '../../../../util/constant';
......
...@@ -22,7 +22,7 @@ import './ImportActionTable.less'; ...@@ -22,7 +22,7 @@ import './ImportActionTable.less';
const { Option } = Select; const { Option } = Select;
const type = 'DragableTableBodyRow'; const type = `DragableTableBodyRow${generateUUID()}`;
const perSuggestCount = 5; const perSuggestCount = 5;
const supportMaxAttributeCountPerPage = 100; const supportMaxAttributeCountPerPage = 100;
const MENU_ID = 'model-attribute-menu'; const MENU_ID = 'model-attribute-menu';
...@@ -59,7 +59,7 @@ const ResizeableHeaderCell = props => { ...@@ -59,7 +59,7 @@ const ResizeableHeaderCell = props => {
); );
}; };
const DatatypeInput = ({ value = {}, datatypes, onChange }) => { export const DatatypeInput = ({ value = {}, datatypes, onChange }) => {
const onNameChange = (value) => { const onNameChange = (value) => {
...@@ -152,7 +152,7 @@ const DatatypeInput = ({ value = {}, datatypes, onChange }) => { ...@@ -152,7 +152,7 @@ const DatatypeInput = ({ value = {}, datatypes, onChange }) => {
) )
} }
const EditableCell = ({ export const EditableCell = ({
editing, editing,
dataIndex, dataIndex,
colTitle, colTitle,
...@@ -222,7 +222,7 @@ const EditableCell = ({ ...@@ -222,7 +222,7 @@ const EditableCell = ({
); );
}; };
const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => { export const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
const ref = useRef(); const ref = useRef();
const [{ isOver, dropClassName }, drop] = useDrop( const [{ isOver, dropClassName }, drop] = useDrop(
...@@ -275,7 +275,7 @@ const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => ...@@ -275,7 +275,7 @@ const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) =>
); );
}; };
const ImportActionTable = (props) => { export const ImportActionTable = (props) => {
const { modelerData, onChange, editable, supportedDatatypes, constraint, template, validateReports, type = 'model', terms, action, originAction } = props; const { modelerData, onChange, editable, supportedDatatypes, constraint, template, validateReports, type = 'model', terms, action, originAction } = props;
const [ data, setData ] = useState([]); const [ data, setData ] = useState([]);
...@@ -1411,5 +1411,3 @@ const ImportActionTable = (props) => { ...@@ -1411,5 +1411,3 @@ const ImportActionTable = (props) => {
</div> </div>
); );
}; };
export default ImportActionTable;
\ No newline at end of file
...@@ -3,7 +3,7 @@ import { Spin, Tabs, Popover, Divider, Button, Space } from 'antd'; ...@@ -3,7 +3,7 @@ import { Spin, Tabs, Popover, Divider, Button, Space } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons'; import { QuestionCircleOutlined } from '@ant-design/icons';
import TemplateActionHeader from './TemplateActionHeader'; import TemplateActionHeader from './TemplateActionHeader';
import ImportActionTable from '../../Model/Component/ImportActionTable'; import { ImportActionTable } from '../../Model/Component/ImportActionTable';
import { dispatchLatest, dispatch } from '../../../../model'; import { dispatchLatest, dispatch } from '../../../../model';
......
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