Commit a24d28d4 by zhaochengxiang

行展开

parent 2ec9b0ad
...@@ -182,6 +182,7 @@ const EditModel = (props) => { ...@@ -182,6 +182,7 @@ const EditModel = (props) => {
setActionData({ ...actionData, ...{ action: 'detail', modelerId: data.id||'', editable: data?.editable||false, stateId: data?.state?.id||'' } }); setActionData({ ...actionData, ...{ action: 'detail', modelerId: data.id||'', editable: data?.editable||false, stateId: data?.state?.id||'' } });
actionRef.current = 'detail'; actionRef.current = 'detail';
LocalStorage.set('modelId', data.id||'');
LocalStorage.set('modelChange', !(LocalStorage.get('modelChange')||false)); LocalStorage.set('modelChange', !(LocalStorage.get('modelChange')||false));
}, },
error: () => { error: () => {
...@@ -199,6 +200,7 @@ const EditModel = (props) => { ...@@ -199,6 +200,7 @@ const EditModel = (props) => {
setActionData({ ...actionData, ...{ action: (_action==='flow')?'flow':'detail', modelerId: data.id||'', stateId: data?.state?.id||'', permitCheckOut: data?.permitCheckOut||false, editable: data?.editable||false } }); setActionData({ ...actionData, ...{ action: (_action==='flow')?'flow':'detail', modelerId: data.id||'', stateId: data?.state?.id||'', permitCheckOut: data?.permitCheckOut||false, editable: data?.editable||false } });
actionRef.current = (_action==='flow')?'flow':'detail'; actionRef.current = (_action==='flow')?'flow':'detail';
LocalStorage.set('modelId', data.id||'');
LocalStorage.set('modelChange', !(LocalStorage.get('modelChange')||false)); LocalStorage.set('modelChange', !(LocalStorage.get('modelChange')||false));
} }
}, },
......
...@@ -7,11 +7,13 @@ import { Resizable } from 'react-resizable'; ...@@ -7,11 +7,13 @@ import { Resizable } from 'react-resizable';
import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify"; import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify";
import ResizeObserver from 'rc-resize-observer'; import ResizeObserver from 'rc-resize-observer';
import { MenuItem, SubMenu } from 'react-contextmenu'; import { MenuItem, SubMenu } from 'react-contextmenu';
import LocalStorage from 'local-storage';
import DataGrid from '../../VirtualTable/test-table'; import DataGrid from '../../VirtualTable/test-table';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import { showMessage, getQueryParam, paginate, isSzseEnv, formatDate, getDataModelerRole } from '../../../../util'; import { showMessage, getQueryParam, paginate, isSzseEnv, formatDate, getDataModelerRole } from '../../../../util';
import { AnchorId, AnchorTimestamp, Action, CatalogId, ModelerId, DataModelerRoleReader } from '../../../../util/constant'; import { AnchorId, AnchorTimestamp, Action, CatalogId, ModelerId, DataModelerRoleReader } from '../../../../util/constant';
import SubModelTable from "./SubModelTable";
// import Tag from "../../Tag"; // import Tag from "../../Tag";
import './ModelTable.less'; import './ModelTable.less';
...@@ -239,13 +241,11 @@ const ModelTable = (props) => { ...@@ -239,13 +241,11 @@ const ModelTable = (props) => {
useEffect(() => { useEffect(() => {
if ((modelId||'') !== '') {
window?.addEventListener("storage", modelEventChange); window?.addEventListener("storage", modelEventChange);
return () => { return () => {
window?.removeEventListener("storage", modelEventChange); window?.removeEventListener("storage", modelEventChange);
} }
}
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
...@@ -314,7 +314,7 @@ const ModelTable = (props) => { ...@@ -314,7 +314,7 @@ const ModelTable = (props) => {
const modelEventChange = (e) => { const modelEventChange = (e) => {
if (e.key === 'modelChange') { if (e.key === 'modelChange') {
getCheckoutDataModel(); subDataMap.delete(LocalStorage.get('modelId'));
} }
} }
...@@ -483,10 +483,10 @@ const ModelTable = (props) => { ...@@ -483,10 +483,10 @@ const ModelTable = (props) => {
return ( return (
<div style={{ padding: 10 }}> <div style={{ padding: 10 }}>
<ModelTable <SubModelTable
modelId={row?.checkedOutId} modelId={row?.checkedOutId}
modelPid={row?.id} modelPid={row?.id}
globalSelectRows={[...subSelectedRowKeys, ...selectedRowKeys]} checked={subSelectedRowKeys.indexOf(row?.checkedOutId)!==-1}
dataMap={subDataMap} dataMap={subDataMap}
onSubSelect={onSubSelectChange} onSubSelect={onSubSelectChange}
onDataMapChange={onSubDataMapChange} onDataMapChange={onSubDataMapChange}
...@@ -593,6 +593,7 @@ const ModelTable = (props) => { ...@@ -593,6 +593,7 @@ const ModelTable = (props) => {
columns={columns} columns={columns}
rows={modelId?(subData||[]):(data||[])} rows={modelId?(subData||[]):(data||[])}
rowHeight={modelId?39: 51} rowHeight={modelId?39: 51}
expandedRowHeight={100}
rowExpandable={(row) => { rowExpandable={(row) => {
return row?.alreadyCheckedOut; return row?.alreadyCheckedOut;
}} }}
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
display: none; display: none;
} }
.yy-table-tbody > tr:not(.yy-table-measure-row)> td { .yy-table-tbody tr:not(.yy-table-measure-row) td {
padding: 8px 8px !important; padding: 8px 8px !important;
} }
} }
......
import React, { useState, useEffect, useRef } from "react";
import { Tooltip, Modal, Pagination, Table, Typography } from 'antd';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import SmoothScroll from 'smooth-scroll';
import classnames from 'classnames';
import { Resizable } from 'react-resizable';
import ResizeObserver from 'rc-resize-observer';
import { ContextMenu, ContextMenuTrigger } from 'react-contextmenu';
import { nanoid } from 'nanoid';
import { createPortal } from 'react-dom';
import { MenuItem, SubMenu } from 'react-contextmenu';
import { dispatch } from '../../../../model';
import { showMessage, getQueryParam, paginate, isSzseEnv, formatDate, getDataModelerRole } from '../../../../util';
import { AnchorId, AnchorTimestamp, Action, CatalogId, ModelerId, DataModelerRoleReader } from '../../../../util/constant';
// import Tag from "../../Tag";
import './ModelTable.less';
import 'react-contexify/dist/ReactContexify.css';
const { Text } = Typography;
const { Column } = Table;
const ModelNameColumn = (props) => {
const { text, record, detailItem } = props;
const [ data, setData ] = useState(record);
const cols = [
{
title: '序号',
dataIndex: 'key',
render: (text, record, index) => {
return (index+1).toString();
},
width: 60,
ellipsis: true,
},
{
title: '字段中文名称',
width: 160,
dataIndex: 'cnName',
editable: true,
ellipsis: true,
},
{
title: '字段英文名称',
width: 160,
dataIndex: 'name',
editable: true,
ellipsis: true,
},
];
let _textComponent = <span style={{ color: '#000' }}>{text}</span>;
if (data.digest) {
_textComponent = <div style={{ width: 500, maxHeight: 300, overflow: 'auto' }}>
<Table
rowKey='name'
dataSource={data.digest.attributeDigests||[]}
columns={cols}
loading={false}
pagination={false}
size='small'
rowClassName={(record, index) => {
if (record?.primaryKey) {
return 'primary-row';
}
return '';
}}
/>
</div>;
}
return (
<Tooltip
title={_textComponent}
overlayClassName='tooltip-common'
onVisibleChange={(visible) => {
if (visible && !record.digest) {
dispatch({
type: 'datamodel.getDataModelDigest',
payload: {
id: record.id
},
callback: _data => {
record.digest = _data;
setData({...record});
}
})
}
}}
>
<a onClick={()=>{detailItem(record);}}>
{text||''}
</a>
</Tooltip>
);
}
const ResizeableHeaderCell = props => {
const { onResize, width, onClick, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th
onClick={onClick}
{...restProps}
/>
</Resizable>
);
};
const SubModelTable = (props) => {
const { data, onChange, onItemAction, onSelect, onHistory, catalogId, keyword, onAutoCreateTable, offset = null, modelId = null, modelPid = null, view, selectModelerIds, onSubSelect, modelState, user, checked, dataMap, onDataMapChange } = props;
const [ tableWidth, setTableWidth ] = useState(0);
const [ selectedRowKeys, setSelectedRowKeys ] = useState([]);
const [ subSelectedRowKeys, setSubSelectedRowKeys ] = useState([]);
// const [ mouseEnterKey, setMouseEnterKey ] = useState(null);
const [ sortRule, setSortRule ] = useState(null);
const [ filterData, setFilterData ] = useState([]);
const [ subData, setSubData ] = useState([]);
const cols = [
{
title: '模型名称',
dataIndex: 'name',
width: isSzseEnv?360:160,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (text, record, index) => {
return (<ModelNameColumn text={text} record={record} detailItem={detailItem} />);
}
},
{
title: '中文名称',
dataIndex: 'cnName',
width: isSzseEnv?420:160,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<Text ellipsis={true}>{text||''}</Text>
</Tooltip>
)
}
},
{
title: '状态',
dataIndex: 'state',
width: 100,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (_, record) => {
let color = '';
if (record?.state?.id === '1') {
color = '#DE7777';
} else if (record?.state?.id === '2') {
color = '#779BDE';
} else if (record?.state?.id === '4') {
color = '#77DEBF';
}
return (
<span>
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 5, marginRight: 5, backgroundColor: color }}></span>
<span>{record?.state?.cnName||''}</span>
</span>
);
}
},
{
title: '创建人',
dataIndex: 'editor',
width: 100,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
},
{
title: '版本号',
dataIndex: 'modifiedTs',
width: 170,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (_,record) => {
return `V_${formatDate(record.modifiedTs)}`;
}
},
// {
// title: '标签',
// dataIndex: 'tag',
// width: 200,
// onCell: (record) => ({
// onMouseEnter: event => {
// setMouseEnterKey(record.id);
// },
// onMouseLeave: event => {
// setMouseEnterKey(null);
// },
// }),
// render: (_,record) => {
// return (
// record.id===mouseEnterKey?<Tag styleType='complex' id={record.id} />:<Tag id={record.id} />
// );
// }
// },
{
title: '模型描述',
dataIndex: 'remark',
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (text, _, __) => {
return (
<Tooltip title={text||''} overlayClassName='tooltip-common'>
<Text ellipsis={true}>{text||''}</Text>
</Tooltip>
);
}
},
];
const pathColumn = {
title: '路径',
dataIndex: 'path',
width: 120,
ellipsis: true,
sorter: true,
sortDirections: ['ascend', 'descend'],
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<Text ellipsis={true}>{text||''}</Text>
</Tooltip>
)
}
};
const [ columns, setColumns ] = useState([]);
const [ currentItem, setCurrentItem ] = useState(null);
const [modal, contextHolder] = Modal.useModal();
const anchorId = getQueryParam(AnchorId, props.location.search);
const anchorTimestamp = getQueryParam(AnchorTimestamp, props.location.search);
const shouldScrollRef = useRef(false);
useEffect(() => {
setSelectedRowKeys(checked?[modelId]:[]);
if (dataMap.has(modelId)) {
setSubData([dataMap.get(modelId)]);
} else {
getCheckoutDataModel();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
if (tableWidth>0 && columns.length===0) {
let newColumns = [...cols];
if (view==='state' || (keyword||'')!=='') {
newColumns.splice(3, 0, pathColumn);
}
newColumns.forEach((column, index) => {
if (!column.width) {
const rowWidth = (newColumns.reduce((preVal, col) => (col.width?col.width:0) + preVal, 0)) + 97;
if (tableWidth - rowWidth > 200) {
column.width = tableWidth-rowWidth;
} else {
newColumns.width = 200;
}
}
});
setColumns([ ...newColumns, <Column key='auto' />]);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ tableWidth ])
const getCheckoutDataModel = () => {
dispatch({
type: 'datamodel.getCheckoutDataModel',
payload: {
id: modelPid
},
callback: data => {
setSubData(data?[data]:[]);
onDataMapChange && onDataMapChange(modelId, data);
},
error: () => {
}
})
}
const editItem = (record) => {
onItemAction && onItemAction(record, 'edit');
}
const detailItem = (record) => {
onItemAction && onItemAction(record, 'detail', getDataModelerRole(user)===DataModelerRoleReader);
}
const deployAction = (record) => {
onAutoCreateTable && onAutoCreateTable(record);
}
const stateAction = (record, action) => {
modal.confirm({
title: '提示!',
content: `您确定要${action.cnName||''}该模型吗?`,
onOk: () => {
dispatch({
type: 'datamodel.nextState',
payload: {
easyDataModelerDataModelId: record.id,
actionId: action.id
},
callback: () => {
showMessage('success', `模型${action.cnName||''}成功`);
if (action.id === '2') {
onChange && onChange();
} else {
getCheckoutDataModel();
}
}
})
}
});
}
const deleteItem = (record) => {
modal.confirm({
title: '提示!',
content: '您确定要删除该模型吗?',
onOk: () => {
dispatch({
type: 'datamodel.deleteDataModel',
payload: {
params: {
id: record.id
}
},
callback: () => {
showMessage('success', '模型删除成功');
onChange && onChange();
}
})
}
});
}
const historyItem = (record) => {
onHistory && onHistory(record.id);
}
const onSelectChange = keys => {
setSelectedRowKeys(keys);
onSubSelect && onSubSelect(keys, subData[0].id);
};
const handleResize = index => (e, { size }) => {
let nextColumns = [...columns];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
setColumns(nextColumns);
};
const rowSelection = {
selectedRowKeys,
onChange: onSelectChange,
hideSelectAll: true,
};
const classes = classnames('model-table', {
'model-table-sub': modelId
});
const handleItemClick = (e, data) => {
const { key } = data;
if (key === 'edit') {
editItem(currentItem);
} else if (key === 'delete') {
deleteItem(currentItem);
} else if (key === 'history') {
historyItem(currentItem);
} else if (key === 'copy') {
window.open(`/data-govern/data-model-action?${Action}=add&${CatalogId}=${(view==='dir')?(catalogId||''):''}&${ModelerId}=${currentItem.id}`);
} else if (key === 'createTable') {
deployAction(currentItem);
} else if (key.indexOf('action') !== -1) {
const index = (key.split('-'))[1];
const action = currentItem?.state?.supportedActions[index];
stateAction(currentItem, action);
}
}
const mergedColumns = () => {
let newColumns = [...columns];
return (
newColumns.map((col, index) => ({
...col,
onHeaderCell: column => ({
width: column.width,
onResize: handleResize(index),
}),
}))
);
}
let disableEdit = false, disableDelete = false, editTip = '', deleteTip = '', editMenuTitle = '编辑';
if (!currentItem?.editable && currentItem?.state?.id!=='4') {
disableEdit = true;
if (currentItem?.state?.id === '2') {
editTip = '待发布的模型不允许编辑';
}
}
if (!currentItem?.permitCheckOut && currentItem?.state?.id==='4') {
disableEdit = true;
editTip = `${currentItem.holder||''}正在编辑中, 不允许再编辑`;
editMenuTitle = `编辑(${currentItem.holder||''}正在编辑中)`;
}
if (!currentItem?.deletable) {
disableDelete = true;
if (currentItem?.state?.id === '2') {
deleteTip = '待发布的模型不允许删除';
} else if (currentItem?.state?.id === '4') {
deleteTip = '已发布的模型不允许删除';
}
}
const contextMenuId = nanoid()
return (
<div className={classes}>
<ResizeObserver
onResize={({ width }) => {
setTableWidth(width);
}}
>
<Table
rowSelection={rowSelection}
components={{
header: {
cell: ResizeableHeaderCell,
},
body: {
row: (props) => {
return (
<ContextMenuTrigger id={contextMenuId} >
<tr {...props} />
</ContextMenuTrigger>
)
},
},
}}
columns={mergedColumns()}
rowKey={'id'}
dataSource={subData||[]}
pagination={false}
size='small'
onRow={(record, index) => {
return {
onContextMenu: event => {
setCurrentItem(record);
},
}
}}
/>
</ResizeObserver>
{createPortal(
<ContextMenu id={contextMenuId} rtl={false}>
{
(getDataModelerRole(user)!==DataModelerRoleReader) && <MenuItem data={{ key: 'edit' }} disabled={disableEdit} onClick={handleItemClick}>
<Tooltip title={editTip}>
{ editMenuTitle }
</Tooltip>
</MenuItem>
}
{
(getDataModelerRole(user)!==DataModelerRoleReader) && <MenuItem data={{ key: 'delete' }} disabled={disableDelete} onClick={handleItemClick}>
<Tooltip title={deleteTip}>
删除
</Tooltip>
</MenuItem>
}
<MenuItem data={{ key: 'history' }} onClick={handleItemClick}>
历史版本
</MenuItem>
{
(getDataModelerRole(user)!==DataModelerRoleReader) && <MenuItem data={{ key: 'copy' }} onClick={handleItemClick}>
复制模型
</MenuItem>
}
{
getDataModelerRole(user)!==DataModelerRoleReader && (currentItem?.state?.supportedActions||[]).length>0 && currentItem?.state?.supportedActions.map((item, index) => {
return (
<MenuItem data={{ key: `action-${index}` }} onClick={handleItemClick}>
{item.cnName||''}
</MenuItem>
);
})
}
{
getDataModelerRole(user)!==DataModelerRoleReader &&currentItem?.deployable && <MenuItem id='createTable' onClick={handleItemClick}>
建表
</MenuItem>
}
</ContextMenu>
,
document.body
)}
{ contextHolder }
</div>
);
}
export default SubModelTable;
\ No newline at end of file
...@@ -203,7 +203,7 @@ function FC<Row extends RowData, SR, K extends React.Key = React.Key>(props: Dat ...@@ -203,7 +203,7 @@ function FC<Row extends RowData, SR, K extends React.Key = React.Key>(props: Dat
} }
return columns return columns
}, [columns, expandRow, _rows, checkable, checkAll, setCheckAll]) }, [columns, expandRow, checkable])
// 取得选中 // 取得选中
const getSelected = useCallback(() => { const getSelected = useCallback(() => {
...@@ -266,7 +266,7 @@ function FC<Row extends RowData, SR, K extends React.Key = React.Key>(props: Dat ...@@ -266,7 +266,7 @@ function FC<Row extends RowData, SR, K extends React.Key = React.Key>(props: Dat
sortColumns={sortColumns} sortColumns={sortColumns}
onSortColumnsChange={setSortColumns} onSortColumnsChange={setSortColumns}
selectedRows={_selectedRows} selectedRows={new Set(selectedRows||[])}
onSelectedRowsChange={(values: Set<any>) => { onSelectedRowsChange={(values: Set<any>) => {
console.log('values', values); console.log('values', values);
onSelectedRowsChange && onSelectedRowsChange(Array.from(values)); onSelectedRowsChange && onSelectedRowsChange(Array.from(values));
......
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