Commit de0be0e4 by zhaochengxiang

qiankun

parent 2be49a33
PORT=3008
\ No newline at end of file
const CracoLessPlugin = require('craco-less'); const CracoLessPlugin = require('craco-less');
const { name } = require('./package');
module.exports = { module.exports = {
plugins: [ plugins: [
...@@ -7,11 +8,32 @@ module.exports = { ...@@ -7,11 +8,32 @@ module.exports = {
options: { options: {
lessLoaderOptions: { lessLoaderOptions: {
lessOptions: { lessOptions: {
modifyVars: { }, modifyVars: {
'@ant-prefix': 'yy',
},
javascriptEnabled: true, javascriptEnabled: true,
}, },
}, },
}, },
}, },
], ],
webpack: {
configure: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
globalObject: 'window',
}
}
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
// historyApiFallback: true,
// hot: false,
// watchContentBase: false,
// liveReload: false,
},
}; };
\ No newline at end of file
{ {
"name": "my-app", "name": "data-govern",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
...@@ -38,7 +38,10 @@ ...@@ -38,7 +38,10 @@
"extends": [ "extends": [
"react-app", "react-app",
"react-app/jest" "react-app/jest"
] ],
"rules": {
"jsx-a11y/anchor-is-valid": "off"
}
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
......
import React from 'react'; import React from 'react';
import { import {
BrowserRouter as Router, BrowserRouter as Router,
Route, Switch, Redirect Route, Switch
} from 'react-router-dom'; } from 'react-router-dom';
import { ContextPath } from './util'; import { ContextPath } from './util';
...@@ -9,18 +9,20 @@ import Signin from './view/Signin'; ...@@ -9,18 +9,20 @@ import Signin from './view/Signin';
import Home from './view/Home'; import Home from './view/Home';
import Manage from './view/Manage'; import Manage from './view/Manage';
import Map from './view/Manage/Map'; import Map from './view/Manage/Map';
import Model from './view/Manage/Model';
export default class App extends React.Component { export default class App extends React.Component {
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
<Router> <Router basename={window.__POWERED_BY_QIANKUN__ ? '/data-govern' : '/'}>
<Switch> <Switch>
<Route path={`${ContextPath}/login`} component={Signin} exact /> <Route path={`${ContextPath}/login`} component={Signin} exact />
<Route path={`${ContextPath}/home`} component={Home} /> <Route path={`${ContextPath}/home`} component={Home} />
<Route path={`${ContextPath}/data-map`} component={Map} exact /> <Route path={`${ContextPath}/data-map`} component={Map} exact />
<Route path={`${ContextPath}/manage`} component={Manage} /> <Route path={`${ContextPath}/manage`} component={Manage} />
<Route component={() => <Redirect to={`${ContextPath}/login`} />}/> <Route path={`/center-home/view/modelmap`} component={Map} exact />
<Route path={`/center-home/view/datamodel`} component={Model} exact />
</Switch> </Switch>
</Router> </Router>
</React.Fragment> </React.Fragment>
......
import "core-js/stable"; import "core-js/stable";
import "regenerator-runtime/runtime"; import "regenerator-runtime/runtime";
import React, { Suspense, lazy } from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { ConfigProvider } from 'antd'; import { ConfigProvider } from 'antd';
import zh_CN from 'antd/es/locale-provider/zh_CN'; import zh_CN from 'antd/es/locale-provider/zh_CN';
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { store } from './model'; import { store } from './model';
import App from './App'
import './index.less'; import './index.less';
const App = lazy(() => import("./App"));
const app = ( const app = (
<ConfigProvider locale={zh_CN}> //解决主次应用样式冲突
<Suspense fallback={<div className="text-center">正在加载界面...</div>}> //https://qiankun.umijs.org/faq#how-to-guarantee-the-main-app-stylesheet-isolated-with-sub-apps
<ConfigProvider locale={zh_CN} prefixCls="yy">
<Provider store={store}><App /></Provider> <Provider store={store}><App /></Provider>
</Suspense>
</ConfigProvider> </ConfigProvider>
); );
ReactDOM.render(app, document.getElementById('root')); function render(props) {
\ No newline at end of file const { container } = props;
ReactDOM.render(app, container ? container.querySelector('#root') : document.querySelector('#root'));
}
function storeTest(props) {
props.onGlobalStateChange((value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('[data-govern] react app bootstraped');
}
export async function mount(props) {
console.log('[data-govern] props from main framework', props);
storeTest(props);
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}
\ No newline at end of file
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
...@@ -89,10 +89,7 @@ export const SetSource = function (source) { ...@@ -89,10 +89,7 @@ export const SetSource = function (source) {
const callback = resp => { const callback = resp => {
if (resp.status === 401) { if (resp.status === 401) {
message.warning("session过期,请重新登录!"); message.warning("session过期,请重新登录!");
if (window.parent) { window.location.href="/center-home/view/login"
window.parent.postMessage('session invalid');
}
return null; return null;
} }
else if (resp.status !== 200) { else if (resp.status !== 200) {
......
import React from 'react'; import React from 'react';
import G6 from '@antv/g6'; import G6 from '@antv/g6';
import { ContextPath } from '../../../../util'; // import { ContextPath } from '../../../../util';
let graph = null; let graph = null;
...@@ -32,7 +32,7 @@ const EXPAND_ICON = function EXPAND_ICON(x, y, r) { ...@@ -32,7 +32,7 @@ const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
class Org extends React.Component { class Org extends React.Component {
componentDidMount() { componentDidMount() {
const { type, loadMoreData, history } = this.props; const { type, loadMoreData } = this.props;
const container = document.getElementById(`container${type||''}`); const container = document.getElementById(`container${type||''}`);
if (!container) return; if (!container) return;
......
import React from 'react'; import React from 'react';
import G6 from '@antv/g6'; import G6 from '@antv/g6';
import { ContextPath } from '../../../../util'; // import { ContextPath } from '../../../../util';
const colors = [ const colors = [
'#BDD2FD', '#BDD2FD',
...@@ -43,7 +43,7 @@ let graph = null; ...@@ -43,7 +43,7 @@ let graph = null;
class Relation extends React.Component { class Relation extends React.Component {
componentDidMount() { componentDidMount() {
const { type, loadMoreData , history } = this.props; const { type, loadMoreData } = this.props;
setTimeout(() => { setTimeout(() => {
const container = document.getElementById(`container${type||''}`); const container = document.getElementById(`container${type||''}`);
......
import React from 'react'; import React from 'react';
import G6 from '@antv/g6'; import G6 from '@antv/g6';
import { ContextPath } from '../../../../util'; // import { ContextPath } from '../../../../util';
let graph = null; let graph = null;
const globalFontSize = 20; const globalFontSize = 20;
...@@ -31,7 +31,7 @@ const EXPAND_ICON = function EXPAND_ICON(x, y, r) { ...@@ -31,7 +31,7 @@ const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
class Tree extends React.Component { class Tree extends React.Component {
componentDidMount() { componentDidMount() {
const { type, loadMoreData, history } = this.props; const { type, loadMoreData } = this.props;
const container = document.getElementById(`container${type||''}`); const container = document.getElementById(`container${type||''}`);
if (!container) return; if (!container) return;
......
import React, { useState, useCallback, useRef } from 'react'; import React, { useState, useEffect } from 'react';
import { Table, Input, InputNumber, Form, Typography, Radio, Divider, Alert, Button } from 'antd'; import { Alert } from 'antd';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import ImportActionTable from './ImportActionTable';
import update from 'immutability-helper'; import ImportActionIndex from './ImportActionIndex';
const originData = []; const originData = [];
const type = 'DragableBodyRow';
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
originData.push({ originData.push({
key: i.toString(), key: i.toString(),
name: `交易流水${i}`, name: `trade_id${i}`,
cnName: 'trade_id', cnName: `交易流水${i}`,
type: 'varchar', type: 'varchar',
length: 32, length: 32,
desc: '流水单号' desc: '流水单号',
nonnull: true,
primaryKey: false,
distributionKey: false,
zone: false,
}); });
} }
const EditableCell = ({
editing,
dataIndex,
title,
inputType,
record,
index,
children,
...restProps
}) => {
const inputNode = inputType === 'number' ? <InputNumber /> : <Input />
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{
margin: 0,
}}
rules={[
{
required: true,
message: `请输入${title}!`,
},
]}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
const ref = useRef();
const [{ isOver, dropClassName }, drop] = useDrop(
() => ({
accept: type,
collect: monitor => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
};
},
drop: item => {
if (className === 'ant-table-expanded-row') return;
if (moveRow) {
moveRow(item.index, index);
}
},
}),
[index],
);
const [, drag] = useDrag(
() => ({
type,
item: { index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
}),
[index],
);
drop(drag(ref));
return (
<tr
ref={ref}
className={`${className}${isOver ? dropClassName : ''}`}
style={{ cursor: 'move', ...style }}
{...restProps}
/>
);
};
const ImportAction = () => { const ImportAction = () => {
const [form] = Form.useForm(); const [ tableData, setTableData ] = useState([...originData]);
const [data, setData] = useState(originData); const [indexData, setIndexData] = useState([]);
const [editingKey, setEditingKey] = useState(''); const [fileds, setFileds] = useState([]);
const [recommends, setRecommends] = useState([]);
const [suggests, setSuggests] = useState([]);
const dataRef = useRef();
dataRef.current = data;
const isEditing = (record) => record.key === editingKey;
const onAddClick = () => { useEffect(() =>{
const newData = [{}, ...data];
(newData||[]).forEach((item, index) => { const _fileds = [];
item.key = index.toString(); tableData && tableData.forEach(item => {
_fileds.push(item.name||'');
}) })
setFileds(_fileds);
setData(newData); //数据表结构变化 索引结构也要跟着变化
edit(newData[0]); const _indexData = (indexData||[]).filter(item => {
} let exsit = true;
const edit = (record) => {
form.setFieldsValue({
name: '',
cnName: '',
type: '',
length: 0,
desc: '',
...record,
});
setEditingKey(record.key);
};
const remove = (record) => {
const newData = [...data];
const index = newData.findIndex((item) => record.key === item.key);
newData.splice(index, 1);
setData(newData);
}
const cancel = () => { (item.fileds||[]).forEach(filed => {
setEditingKey(''); if (_fileds.indexOf(filed) ===-1) {
setRecommends([]); exsit = false;
};
const save = () => {
try {
setRecommends([]);
const _suggests = [
{
name: '建议1'
},
{
name: '建议2'
} }
]; })
setSuggests(_suggests)
if (_suggests.length === 0) {
constraintSave();
}
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
};
const constraintSave = async () => {
const row = await form.validateFields();
const newData = [...data];
const index = newData.findIndex((item) => editingKey === item.key);
if (index > -1) { return exsit;
const item = newData[index]; })
newData.splice(index, 1, { ...item, ...row });
setData(newData);
dataRef.current = newData;
} else { setIndexData(_indexData);
newData.push(row); //eslint-disable-next-line react-hooks/exhaustive-deps
setData(newData); }, [tableData]);
dataRef.current = newData;
const onTableChange = (data) => {
setTableData(data);
} }
setEditingKey(''); const onIndexChange = (data) => {
setSuggests([]); setIndexData(data);
} }
const onValuesChange = (changedValues, allValues) => {
// console.log('changed values', changedValues);
// console.log('all values', allValues);
setRecommends([
{
name: '流水交易',
cnName: 'trade_id',
type: 'varchar',
length: 32,
desc: '流水交易'
},
{
key: 0,
name: `流水交易交易`,
cnName: 'trade_id',
type: 'varchar',
length: 32,
desc: '流水单号'
}
])
};
const onRecommendChange = (e) => {
form.setFieldsValue({
...recommends[e.target.value]
});
setRecommends([]);
};
const columns = [
{
title: '中文名称',
dataIndex: 'name',
editable: true,
},
{
title: '英文名称',
dataIndex: 'cnName',
editable: true,
},
{
title: '类型',
dataIndex: 'type',
editable: true,
},
{
title: '长度',
dataIndex: 'length',
editable: true,
},
{
title: '描述',
dataIndex: 'desc',
editable: true,
},
{
title: '操作',
dataIndex: 'action',
render: (_, record) => {
const editable = isEditing(record);
return editable ? (
<>
<Typography.Link className='mr-3' disabled={editingKey === ''} onClick={() => save()}>
保存
</Typography.Link>
<Typography.Link disabled={editingKey === ''} onClick={() => {cancel()}}>
取消
</Typography.Link>
</>
) : (
<>
<Typography.Link className='mr-3' disabled={editingKey !== ''} onClick={() => edit(record)}>
编辑
</Typography.Link>
<Typography.Link disabled={editingKey !== ''} onClick={() => remove(record)}>
删除
</Typography.Link>
</>
);
},
},
];
const mergedColumns = columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
inputType: 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
const dragRow = dataRef.current[dragIndex];
const newData = update(dataRef.current, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragRow],
],
});
setData(newData);
dataRef.current = newData;
},
[data],
);
return ( return (
<> <>
<Alert <Alert
...@@ -322,81 +67,8 @@ const ImportAction = () => { ...@@ -322,81 +67,8 @@ const ImportAction = () => {
closable closable
className='mb-3' className='mb-3'
/> />
<div className='d-flex mb-3'> <ImportActionTable data={tableData} onChange={onTableChange} />
<Button type="primary" onClick={onAddClick} style={{ marginLeft: 'auto' }} disabled={ editingKey!=='' } >新增行</Button> <ImportActionIndex data={indexData} fileds={fileds} onChange={onIndexChange} />
</div>
<DndProvider backend={HTML5Backend} >
<Form form={form} component={false} onValuesChange={onValuesChange}>
<Table
components={{
body: {
cell: EditableCell,
//编辑状态下不允许拖动
row: editingKey===''?DragableBodyRow:null,
},
}}
onRow={(record, index) => ({
index,
moveRow,
})}
dataSource={data}
columns={mergedColumns}
size='small'
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 className='mt-3'>{suggest.name||''}</div>
)
})
}
</div>
<Button className='mb-3 ml-7' type='primary' onClick={constraintSave}>强制保存</Button>
</>
)
}
{
recommends && recommends.length>0 && (
<>
<Divider orientation="left">智能推荐</Divider>
<Radio.Group onChange={onRecommendChange} className='mb-3 ml-7'>
{
recommends && recommends.map((recommend, index) => {
return (
<Radio key={index} value={index} className='mt-3' style={{ display: 'block' }}>
{`${recommend.name||''}`}
</Radio>
)
})
}
</Radio.Group>
</>
)
}
</>
}
</>
),
expandIcon: ({ expanded, onExpand, record }) => {
return <></>;
},
rowExpandable: record => (editingKey!==''&&((recommends||[]).length>0||(suggests||[]).length>0)),
expandedRowKeys: [editingKey]
}}
/>
</Form>
</DndProvider>
</> </>
); );
}; };
......
import React, { useState, useCallback, useRef } from 'react';
import { Table, Input, Form, Typography, Divider, Button, Select } from 'antd';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
const { Option } = Select;
const type = 'DragableBodyRow';
const modes = [
'主键索引',
'唯一索引',
'普通索引',
'组合索引',
'全文索引'
];
const EditableCell = ({
editing,
dataIndex,
title,
inputType,
fileds,
record,
index,
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>
)
}
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{
margin: 0,
}}
rules={[
{
required: true,
message: `请输入${title}!`,
},
]}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
const ref = useRef();
const [{ isOver, dropClassName }, drop] = useDrop(
() => ({
accept: type,
collect: monitor => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
};
},
drop: item => {
if (moveRow) {
moveRow(item.index, index);
}
},
}),
[index],
);
const [, drag] = useDrag(
() => ({
type,
item: { index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
}),
[index],
);
drop(drag(ref));
return (
<tr
ref={ref}
className={`${className}${isOver ? dropClassName : ''}`}
style={{ cursor: 'move', ...style }}
{...restProps}
/>
);
};
const ImportActionIndex = (props) => {
const { data, fileds, onChange } = props;
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
const [suggests, setSuggests] = useState([]);
const isEditing = (record) => record.key === editingKey;
const onAddClick = () => {
const newData = [{}, ...data];
(newData||[]).forEach((item, index) => {
item.key = index.toString();
})
onChange && onChange(newData);
edit(newData[0]);
}
const edit = (record) => {
form.setFieldsValue({
name: '',
mode: '',
fileds: [],
...record,
});
setEditingKey(record.key);
};
const remove = (record) => {
const newData = [...data];
const index = newData.findIndex((item) => record.key === item.key);
newData.splice(index, 1);
onChange && onChange(newData);
}
const cancel = () => {
const newData = [...data];
const item = newData[editingKey];
if (!item.name || item.name==='') {
newData.splice(editingKey, 1);
onChange && onChange(newData);
}
setEditingKey('');
};
const save = async () => {
try {
await form.validateFields();
const _suggests = [
{
name: '建议1'
},
{
name: '建议2'
}
];
setSuggests(_suggests)
if (_suggests.length === 0) {
constraintSave();
}
} 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 columns = [
{
title: '序号',
dataIndex: 'key',
editable: false,
render: (text, record, index) => {
return (index+1).toString();
}
},
{
title: '索引名称',
dataIndex: 'name',
editable: true,
},
{
title: '索引类型',
dataIndex: 'mode',
editable: true,
},
{
title: '索引字段列表',
dataIndex: 'fileds',
editable: true,
},
{
title: '操作',
dataIndex: 'action',
render: (_, record) => {
const editable = isEditing(record);
return editable ? (
<>
<Typography.Link className='mr-3' disabled={editingKey === ''} onClick={() => save()}>
保存
</Typography.Link>
<Typography.Link disabled={editingKey === ''} onClick={() => {cancel()}}>
取消
</Typography.Link>
</>
) : (
<>
<Typography.Link className='mr-3' disabled={editingKey !== ''} onClick={() => edit(record)}>
编辑
</Typography.Link>
<Typography.Link disabled={editingKey !== ''} onClick={() => remove(record)}>
删除
</Typography.Link>
</>
);
},
},
];
const mergedColumns = columns.map((col) => {
if (!col.editable) {
return col;
}
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||[]
}),
};
});
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
const dragRow = data[dragIndex];
const newData = update(data, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragRow],
],
});
onChange && onChange(newData);
},
//eslint-disable-next-line react-hooks/exhaustive-deps
[data],
);
return (
<>
<Divider>数据表索引</Divider>
<div className='d-flex mb-3'>
<Button type="primary" onClick={onAddClick} style={{ marginLeft: 'auto' }} disabled={ editingKey!=='' } >新增行</Button>
</div>
<DndProvider backend={HTML5Backend} >
<Form form={form} component={false}>
<Table
components={{
body: {
cell: EditableCell,
//编辑状态下不允许拖动
row: editingKey===''?DragableBodyRow:null,
},
}}
onRow={(record, index) => {
if (editingKey!=='') return null;
return {
index,
moveRow
}
}}
dataSource={data}
columns={mergedColumns}
size='small'
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>
</>
);
};
export default ImportActionIndex;
\ No newline at end of file
import React, { useState, useCallback, useRef } from 'react';
import { Table, Input, InputNumber, Form, Typography, Radio, Divider, Button, Checkbox, Popconfirm } from 'antd';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
const type = 'DragableBodyRow';
const EditableCell = ({
editing,
dataIndex,
title,
inputType,
record,
index,
children,
...restProps
}) => {
const inputNode = inputType === 'check' ? <Checkbox /> : ( inputType === 'number' ? <InputNumber /> : <Input /> )
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{
margin: 0,
}}
valuePropName={(inputType==='check')? 'checked': 'value'}
rules={[
{
required: (inputType==='check')? false: true,
message: `请输入${title}!`,
},
]}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
const ref = useRef();
const [{ isOver, dropClassName }, drop] = useDrop(
() => ({
accept: type,
collect: monitor => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
};
},
drop: item => {
if (moveRow) {
moveRow(item.index, index);
}
},
}),
[index],
);
const [, drag] = useDrag(
() => ({
type,
item: { index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
}),
[index],
);
drop(drag(ref));
return (
<tr
ref={ref}
className={`${className}${isOver ? dropClassName : ''}`}
style={{ cursor: 'move', ...style }}
{...restProps}
/>
);
};
const ImportActionTable = (props) => {
const { data, onChange } = props;
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
const [recommends, setRecommends] = useState([]);
const [suggests, setSuggests] = useState([]);
const isEditing = (record) => record.key === editingKey;
const onAddClick = () => {
const newData = [{}, ...data];
onChange && onChange(newData);
edit(newData[0]);
}
const edit = (record) => {
form.setFieldsValue({
name: '',
cnName: '',
type: '',
length: 0,
desc: '',
...record,
});
setEditingKey(record.key);
};
const remove = (record) => {
const newData = [...data];
const index = newData.findIndex((item) => record.key === item.key);
newData.splice(index, 1);
onChange && onChange(newData);
}
const cancel = () => {
const newData = [...data];
const item = newData[editingKey];
if (!item.name || item.name==='') {
newData.splice(editingKey, 1);
onChange && onChange(newData);
}
setEditingKey('');
setRecommends([]);
};
const save = () => {
try {
setRecommends([]);
const _suggests = [
{
name: '建议1'
},
{
name: '建议2'
}
];
setSuggests(_suggests)
if (_suggests.length === 0) {
constraintSave();
}
} 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);
setRecommends([
{
name: 'trade_id',
cnName: '流水交易',
type: 'varchar',
length: 32,
desc: '流水交易'
},
{
name: 'trade_id',
cnName: '流水交易交易',
type: 'varchar',
length: 32,
desc: '流水单号'
}
])
};
const onRecommendChange = (e) => {
form.setFieldsValue({
...recommends[e.target.value]
});
setRecommends([]);
};
const columns = [
{
title: '序号',
dataIndex: 'key',
editable: false,
render: (text, record, index) => {
return (index+1).toString();
}
},
{
title: '中文名称',
dataIndex: 'cnName',
editable: true,
},
{
title: '英文名称',
dataIndex: 'name',
editable: true,
},
{
title: '类型',
dataIndex: 'type',
editable: true,
},
{
title: '长度',
dataIndex: 'length',
editable: true,
},
{
title: '描述',
dataIndex: 'desc',
editable: true,
},
{
title: '非空',
dataIndex: 'nonnull',
editable: true,
render: (text, record, index) => {
return text ? '是': '否';
}
},
{
title: '主键',
dataIndex: 'primaryKey',
editable: true,
render: (text, record, index) => {
return text ? '是': '否';
}
},
{
title: '分布键',
dataIndex: 'distributionKey',
editable: true,
render: (text, record, index) => {
return text ? '是': '否';
}
},
{
title: '分区',
dataIndex: 'zone',
editable: true,
render: (text, record, index) => {
return text ? '是': '否';
}
},
{
title: '操作',
dataIndex: 'action',
render: (_, record) => {
const editable = isEditing(record);
return editable ? (
<>
<Typography.Link className='mr-3' disabled={editingKey === ''} onClick={() => save()}>
保存
</Typography.Link>
<Typography.Link disabled={editingKey === ''} onClick={() => {cancel()}}>
取消
</Typography.Link>
</>
) : (
<>
<Typography.Link className='mr-3' disabled={editingKey !== ''} onClick={() => edit(record)}>
编辑
</Typography.Link>
<Popconfirm disabled={editingKey !== ''} title="删除字段会删除相关的索引,您确定删除吗?" onConfirm={() => remove(record)}>
<a href="">删除</a>
</Popconfirm>
</>
);
},
},
];
const mergedColumns = columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
inputType: (col.dataIndex==='nonnull'||col.dataIndex==='primaryKey'||col.dataIndex==='distributionKey'||col.dataIndex==='zone') ? 'check' : (col.dataIndex==='length'?'number':'text'),
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
const dragRow = data[dragIndex];
const newData = update(data, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragRow],
],
});
onChange && onChange(newData);
},
//eslint-disable-next-line react-hooks/exhaustive-deps
[data],
);
return (
<>
<Divider>数据表结构</Divider>
<div className='d-flex mb-3'>
<Button type="primary" onClick={onAddClick} style={{ marginLeft: 'auto' }} disabled={ editingKey!=='' } >新增行</Button>
</div>
<DndProvider backend={HTML5Backend} >
<Form form={form} component={false} onValuesChange={onValuesChange}>
<Table
components={{
body: {
cell: EditableCell,
//编辑状态下不允许拖动
row: editingKey===''?DragableBodyRow:null,
},
}}
onRow={(record, index) => {
if (editingKey!=='') return null;
return {
index,
moveRow
}
}}
dataSource={data}
columns={mergedColumns}
size='small'
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>
</>
)
}
{
recommends && recommends.length>0 && (
<>
<Divider orientation="left">智能推荐</Divider>
<Radio.Group onChange={onRecommendChange} className='mb-3 ml-7'>
{
recommends && recommends.map((recommend, index) => {
return (
<Radio key={index} value={index} className='mt-3' style={{ display: 'block' }}>
{`${recommend.name||''}`}
</Radio>
)
})
}
</Radio.Group>
</>
)
}
</>
}
</>
),
expandIcon: ({ expanded, onExpand, record }) => {
return <></>;
},
rowExpandable: record => (editingKey!==''&&((recommends||[]).length>0||(suggests||[]).length>0)),
expandedRowKeys: [editingKey]
}}
/>
</Form>
</DndProvider>
</>
);
};
export default ImportActionTable;
\ No newline at end of file
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