Commit aedda061 by zhaochengxiang

资产管理页面调整

parent c657a0f2
import React,{ useState, useEffect, useRef } from "react";
import { Card, Checkbox, Button, List, Pagination, Space, Modal, Switch, Tooltip, Popover, Input, Spin } from "antd";
import { EditOutlined, ReconciliationOutlined, DeleteOutlined, UndoOutlined } from '@ant-design/icons';
import { Card, Button, Pagination, Space, Modal, Input, Table, Divider, Dropdown, Menu, Tooltip } from "antd";
import { DownOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
import VList from 'react-virtualized/dist/commonjs/List';
import { CellMeasurer, CellMeasurerCache } from 'react-virtualized/dist/commonjs/CellMeasurer';
import SmoothScroll from 'smooth-scroll';
import ImportElement from './ImportElement';
import FilterElement from './FilterElement';
import FilterElementModal from './FilterElementModal';
import AssetMount from '../../AssetRecycle/Component/AssetMount';
import AttributeRelationModal from "./AttributeRelationModal";
import ImportAsset from './ImportAsset';
import AssetEdit from './AssetEdit';
import AssetDetail from "./AssetDetail";
import AssetItem from './AssetItem';
import MetadataInfo from './MetadataInfo';
import { dispatch, dispatchLatestHomepage } from '../../../../model';
import { showMessage, showNotifaction, getQueryParam, inputWidth } from '../../../../util';
import { showMessage, showNotifaction, getQueryParam, inputWidth, isSzseEnv } from '../../../../util';
import { AnchorId, AnchorTimestamp } from '../../../../util/constant';
import "./AssetTable.less";
......@@ -24,12 +22,10 @@ const AssetTable = (props) => {
const { readOnly = false, className, nodeId } = props;
const [ loading, setLoading ] = useState(false);
const [filterElementsGroup, setFilterElementsGroup] = useState([]);
const [ assetNames, setAssetNames ] = useState([]);
const [ columns, setColumns ] = useState([]);
const [ assets, setAssets ] = useState([]);
const [ total, setTotal ] = useState(0);
const [ selectedKeys, setSelectedKeys ] = useState([]);
const [ checkAllValue, setCheckAllValue ] = useState(false);
const [ importAssetVisible, setImportAssetVisible ] = useState(false);
const [ importElementVisible, setImportElementVisible ] = useState(false);
const [ filterElementVisible, setFilterElementVisible ] = useState(false);
......@@ -42,20 +38,57 @@ const AssetTable = (props) => {
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const { pageNum, pageSize } = pagination;
const [ keyword, setKeyword ] = useState('');
const [ scrollToIndex, setScrollToIndex ] = useState(-1);
const [ batchCatalogChange, setBatchCatalogChange ] = useState(false);
const actionColumn = {
title: '操作',
key: 'action',
width: 140,
fixed: 'right',
render: (_,record) => {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Button
type='link'
size='small'
onClick={() => { editAsset(record); }}
style={{ padding: 0 }}
>
编辑
</Button>
<div>
<Divider type='vertical' />
</div>
<Button
type='link'
size='small'
onClick={() => { deleteAsset(record); }}
style={{ padding: 0 }}
>
删除
</Button>
<div>
<Divider type='vertical' />
</div>
<Button
type='link'
size='small'
onClick={() => { mountAsset(record); }}
style={{ padding: 0 }}
>
变更
</Button>
</div>
)
}
};
const [ modal, contextHolder ] = Modal.useModal();
const anchorId = getQueryParam(AnchorId, props.location.search);
const timestamp = getQueryParam(AnchorTimestamp, props.location.search);
const shouldScrollRef = useRef(false);
const cellCache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 50,
});
useEffect(() => {
if ((nodeId||'') !== '' ) {
......@@ -72,7 +105,7 @@ const AssetTable = (props) => {
setKeyword('');
setSelectedKeys([]);
setCheckAllValue(false);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ nodeId ])
......@@ -88,15 +121,25 @@ const AssetTable = (props) => {
useEffect(() => {
if ((nodeId||'') !== '' ) {
if (filterElementsGroup.length === 0) {
getFilterElementsGroupThenGetDataAssets();
} else {
getDataAssets();
}
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ keyword, pagination ])
useEffect(() => {
if (shouldScrollRef.current) {
SmoothScroll('a[href*="#"]');
var scroll = new SmoothScroll();
var anchor = document.querySelector(`#data-asset-${anchorId}`);
if (anchor) {
scroll.animateScroll(anchor);
shouldScrollRef.current = false;
}
}
})
const getDataAssetLocation = () => {
setLoading(true);
dispatch({
......@@ -107,8 +150,6 @@ const AssetTable = (props) => {
callback: data => {
const anchorLocation = data.offset;
const _pageNum = parseInt(anchorLocation/pageSize + ((anchorLocation%pageSize===0)?0:1));
const _index = anchorLocation - (_pageNum - 1)*pageSize - 1;
setScrollToIndex(_index);
setPagination({ ...pagination, pageNum: _pageNum });
},
error: () => {
......@@ -119,9 +160,7 @@ const AssetTable = (props) => {
}
const changeCurrent = (page,size) => {
setCheckAllValue(false);
setSelectedKeys([]);
setScrollToIndex(-1);
setPagination({ pageNum: page, pageSize: size });
}
......@@ -130,8 +169,53 @@ const AssetTable = (props) => {
dispatch({
type: 'assetmanage.listFilterElementsGroupByType',
callback: data => {
setFilterElementsGroup(data||[]);
getDataAssets(data||[]);
const _columns = [];
let index = 0;
(data||[]).forEach(group => {
(group.names||[]).forEach((name, i) => {
index += (i+1);
const params = {
title: name,
dataIndex: `element${index}`,
ellipsis: true,
width: 150
};
if (name === '编号') {
params.width = 80;
params.fixed = 'left';
} else if (name === '中文名称') {
params.fixed = 'left';
} else if (name === '英文名称') {
params.width = isSzseEnv?250:150;
params.fixed = 'left';
params.render = (text, record) => {
return (
<a onClick={()=>{detailAsset(record);}}>
{text||''}
</a>
);
}
} else if (name === '资产项') {
params.width = isSzseEnv?250:150;;
params.fixed = 'right';
params.render = (text, _) => {
return (
<MetadataInfo config={false} value={text||''} />
);
}
}
_columns.push(params);
})
})
setColumns([..._columns, actionColumn]);
getDataAssets();
},
error: () => {
setLoading(false)
......@@ -139,7 +223,7 @@ const AssetTable = (props) => {
})
}
const getDataAssets = (elmentsGroup = filterElementsGroup) => {
const getDataAssets = () => {
setLoading(true);
dispatchLatestHomepage({
type: 'assetmanage.listDataAssetsByPage',
......@@ -150,28 +234,23 @@ const AssetTable = (props) => {
keyword: keyword
},
callback: data => {
const _assetNames = [];
(data.data||[]).forEach((asset, index) => {
const _assets = [];
asset.elementsGroup = [];
(elmentsGroup||[]).forEach((elementGroup, _index) => {
let _processElementGroup = { type: elementGroup.type, elements: [] };
(data.data||[]).forEach(asset => {
((elementGroup||[]).names||[]).forEach((name, __index) => {
_processElementGroup.elements.push({ name, value: asset?.elementValues[_index].values[__index] });
let _asset = {...asset}, index = 0;
(asset.elementValues||[]).forEach((elementValue) => {
if (name === '中文名称') {
_assetNames.push(`${index+1}. ${asset?.elementValues[_index].values[__index]||''}`);
}
(elementValue.values||[]).forEach((value, i) => {
index += (i+1);
_asset[`element${index}`] = value;
});
asset.elementsGroup.push(_processElementGroup);
})
_assets.push(_asset);
})
setAssetNames(_assetNames);
setAssets(data.data||[]);
setAssets(_assets);
setTotal(data.total||0);
setLoading(false);
},
......@@ -203,6 +282,7 @@ const AssetTable = (props) => {
}
const mountAsset = (item) => {
setBatchCatalogChange(false);
setCurrentAssetId(item.id);
setAssetMountVisible(true);
}
......@@ -234,7 +314,6 @@ const AssetTable = (props) => {
newSelectedKeys.splice(index, 1);
setSelectedKeys(newSelectedKeys);
}
setCheckAllValue(false);
},
error: () => {
}
......@@ -247,6 +326,10 @@ const AssetTable = (props) => {
setImportAssetVisible(true);
}
const onFilterElementClick = () => {
setFilterElementVisible(true);
}
const exportAsset = () => {
if ((selectedKeys||[]).length === 0) {
showMessage('warn', '请先选择资产');
......@@ -264,36 +347,6 @@ const AssetTable = (props) => {
setAssetDetailVisible(false);
}
const onCheckAll =(checked)=>{
setCheckAllValue(checked);
if (checked) {
const ids = [];
(assets||[]).forEach(asset => {
ids.push(asset.id||'');
})
setSelectedKeys(ids);
} else {
setSelectedKeys([]);
}
}
const onAssetCheckboxChange = (e) => {
let newSelectedKeys = [...selectedKeys];
if (e.target.checked) {
newSelectedKeys = [...newSelectedKeys, e.target.value];
} else {
const index = newSelectedKeys.findIndex((key) => key === e.target.value);
newSelectedKeys.splice(index, 1);
}
setSelectedKeys(newSelectedKeys||[]);
setCheckAllValue((newSelectedKeys||[].length!==0) && (newSelectedKeys||[]).length===(assets||[]).length);
}
const deleteAssets = () => {
if ((selectedKeys||[]).length > 0) {
modal.confirm({
......@@ -313,7 +366,6 @@ const AssetTable = (props) => {
showMessage("success","删除成功");
getDataAssets();
setSelectedKeys([]);
setCheckAllValue(false);
},
error: () => {
}
......@@ -341,8 +393,8 @@ const AssetTable = (props) => {
}
}
const onFilterElementVisibleChange = (visible = false, refresh = false) => {
setFilterElementVisible(visible);
const onFilterElementModalCancel = (refresh = false) => {
setFilterElementVisible(false);
refresh && getFilterElementsGroupThenGetDataAssets();
}
......@@ -358,10 +410,64 @@ const AssetTable = (props) => {
setAttributeRelationModalVisible(true);
}
const onBatchCatalogChangeBtnClick = () => {
setBatchCatalogChange(true);
setAssetMountVisible(true);
}
const onAttributeRelationModalCancel = () => {
setAttributeRelationModalVisible(false);
}
const onSelectChange = keys => {
setSelectedKeys(keys);
};
const rowSelection = {
selectedRowKeys: selectedKeys,
onChange: onSelectChange,
};
const onMoreMenuClick = (e) => {
const { key } = e;
if (key === 'element-import') {
onImportElementBtnClick();
} else if (key === 'element-export') {
onExportElementBtnClick();
} else if (key === 'attribute-relation') {
onAttributeRelationBtnClick();
} else if (key === 'catalog-change') {
onBatchCatalogChangeBtnClick();
} else if (key === 'delete') {
deleteAssets();
}
}
const moreMenu = () => {
return <Menu onClick={onMoreMenuClick}>
<Menu.Item key='element-import'>
导入要素
</Menu.Item>
<Menu.Item key='element-export'>
导出要素
</Menu.Item>
<Menu.Item key='attribute-relation'>
元数据属性关联
</Menu.Item>
<Menu.Item key='catalog-change' disabled={(selectedKeys||[]).length===0}>
<Tooltip title={(selectedKeys||[]).length===0?'请先选择资产':''}>
变更目录
</Tooltip>
</Menu.Item>
<Menu.Item key='delete' disabled={(selectedKeys||[]).length===0}>
<Tooltip title={(selectedKeys||[]).length===0?'请先选择资产':''}>
删除
</Tooltip>
</Menu.Item>
</Menu>
}
const classes = classNames('asset-list', className, {
'asset-list-read-only': readOnly
});
......@@ -370,12 +476,22 @@ const AssetTable = (props) => {
<Card
bordered={false}
className={classes}
bodyStyle={{ padding: '0 10px' }}
bodyStyle={{ padding: '10px 10px 0' }}
headStyle={{ padding: 10 }}
title={
<div className='d-flex' style={{ justifyContent: 'space-between' }}>
<Space>
<span>资产搜索:</span>
<Button onClick={addAsset}>新增</Button>
<Button onClick={importAsset} >导入</Button>
<Tooltip title={(selectedKeys||[]).length===0?'请先选择资产':''}>
<Button onClick={exportAsset} disabled={(selectedKeys||[]).length===0} >导出</Button>
</Tooltip>
<Button onClick={onFilterElementClick}>要素过滤</Button>
<Dropdown overlay={moreMenu} trigger={['click']} placement="bottomLeft">
<Button>更多<DownOutlined /></Button>
</Dropdown>
</Space>
<Space>
<Input
placeholder="请输入资产要素值"
allowClear
......@@ -387,133 +503,29 @@ const AssetTable = (props) => {
</div>
}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: 52,
padding: '10px 0',
borderBottom:'1px solid #f0f0f0'
}} >
<Switch
checkedChildren="全不选"
unCheckedChildren="全选"
checked={ checkAllValue }
onChange={ onCheckAll }
/>
<div>
<Space>
<span>资产要素:</span>
{
!readOnly && <>
<Button type="primary" onClick={onImportElementBtnClick}>导入</Button>
<Button type="primary" onClick={onExportElementBtnClick}>导出</Button>
</>
}
<Popover
placement="bottom"
content={<FilterElement onCancel={onFilterElementVisibleChange} />}
title='资产要素过滤'
visible={filterElementVisible}
onVisibleChange={onFilterElementVisibleChange}
trigger="hover">
<Button type="primary">过滤</Button>
</Popover>
<Button type='primary' onClick={onAttributeRelationBtnClick}>属性关联</Button>
</Space>
<Space className='ml-5'>
<span>资产:</span>
{
!readOnly && <>
<Button type="primary" onClick={ addAsset }>新增</Button>
<Button type="danger" onClick={ deleteAssets } >删除</Button>
<Button type="primary" onClick={importAsset} >导入</Button>
</>
}
<Button type="primary" onClick={exportAsset} >导出</Button>
</Space>
</div>
</div>
<Spin spinning={loading}>
{
loading && <div className='data-asset-spin' />
}
{
!loading && (
<>
<Checkbox.Group
style={{ width: '100%' }}
value={ selectedKeys }
>
<AutoSizer>
{({width, height}) => (
<VList
deferredMeasurementCache={cellCache}
overscanRowCount={2}
height={height}
width={width}
rowCount={(assets||[]).length}
rowHeight={cellCache.rowHeight}
scrollToIndex={scrollToIndex}
rowRenderer={({index, key, parent, style}) => {
const item = assets[index];
return (
<CellMeasurer
cache={cellCache}
columnIndex={0}
key={key}
rowIndex={index}
parent={parent}>
{({measure, registerChild}) => (
<List.Item
onLoad={measure}
id={`data-asset-${item.id||''}`}
style={{ ...style, backgroundColor: (item.id===anchorId)?'#e7f7ff':'transparent' }}
>
<List.Item.Meta
title={
<>
<div className='d-flex mt-3 mb-1' style={{ alignItems: 'center' }}>
<div className='textOverflow' style={{ flex: 1 }}>
<Checkbox value={item.id} onChange={onAssetCheckboxChange}></Checkbox>
<span title={assetNames[index]||''} style={{ marginLeft:8, fontSize: 18, fontWeight: 'bold' }}>{assetNames[index]||''}</span>
</div>
<Space className='m-3' style={{ marginLeft: 'auto' }} size='small'>
{
!readOnly && <Tooltip placement='bottom' title={'修改'}>
<Button icon={<EditOutlined />} size='small' onClick={() => { editAsset(item); }} />
</Tooltip>
}
<Tooltip placement='bottom' title={'详情'}>
<Button icon={<ReconciliationOutlined />} size='small' onClick={() => { detailAsset(item); }} />
</Tooltip>
{
!readOnly && <Tooltip placement='bottom' title={'挂载'}>
<Button icon={<UndoOutlined />} size='small' onClick={() => { mountAsset(item); }} />
</Tooltip>
}
{
!readOnly && <Tooltip placement='bottom' title={'删除'}>
<Button icon={<DeleteOutlined />} size='small' onClick={() => { deleteAsset(item); }} />
</Tooltip>
<Table
rowSelection={rowSelection}
onRow={(record) => {
return {
id: `data-asset-${record?.id}`,
}
</Space>
</div>
<div style={{ width: '100%', height: 2, backgroundColor: '#ededed' }} />
</>
}}
rowClassName={(record, index) => {
if (record?.id===anchorId) {
return 'highlight-row';
}
description={ <AssetItem data={item} /> }
/>
</List.Item>
)}
</CellMeasurer>
);
return '';
}}
loading={loading}
columns={columns}
rowKey='id'
dataSource={assets}
pagination={false}
size='default'
scroll={{ x: 1500, y: 'calc(100vh - 64px - 30px - 57px - 39px - 10px - 42px)' }}
/>
)}
</AutoSizer>
</Checkbox.Group>
<Pagination
size="small"
className="text-center m-3"
......@@ -527,9 +539,7 @@ const AssetTable = (props) => {
total={total}
showTotal={total => `共 ${total} 条`}
/>
</>
)}
</Spin>
<AssetEdit
visible={assetEditVisible}
action={assetEditAction}
......@@ -553,7 +563,7 @@ const AssetTable = (props) => {
/>
<AssetMount
visible={ assetMountVisible }
id={ currentAssetId }
ids={ batchCatalogChange?selectedKeys:[currentAssetId] }
onCancel={ onAssetMountCancel }
{...props}
/>
......@@ -562,6 +572,10 @@ const AssetTable = (props) => {
readOnly={ readOnly }
onCancel={ onAttributeRelationModalCancel }
/>
<FilterElementModal
visible={ filterElementVisible }
onCancel={ onFilterElementModalCancel}
/>
{contextHolder}
</Card>
)
......
@import '../../../../variables.less';
.asset-list {
.yy-list-item-action {
text-align: right;
}
.yy-card-head-title {
font-weight: normal;
font-size: 14px;
padding: 0;
}
.yy-list-vertical .yy-list-item-action > li {
padding: 0 ;
}
.data-asset-spin {
height: calc(100vh - @header-height - @pm-4 - 53px - 52px + 1px);
}
.yy-checkbox-group {
height: calc(100vh - @header-height - @pm-4 - 53px - 52px - 53px) !important;
overflow: auto !important;
}
.yy-divider-horizontal {
margin: 0 !important;
}
.yy-list-vertical {
.yy-list-item-meta, .yy-list-item-meta-title {
margin-bottom: 0 !important;
}
.highlight-row {
.yy-table-cell {
background-color: #e7f7ff !important;
}
.yy-list-item {
padding: 0 !important;
}
}
\ No newline at end of file
......@@ -76,6 +76,7 @@ const AssetTree = (props) => {
},
callback: data => {
setCheckedKeys(data.dirIds||[]);
onCheck && onCheck(data.dirIds||[]);
},
})
}
......
import React, { useEffect, useState } from 'react';
import { Row, Col, Checkbox, Typography, Space, Button, Switch } from 'antd';
import { Row, Col, Checkbox, Typography, Button, Switch, Modal } from 'antd';
import { dispatch } from '../../../../model';
import './FilterElement.less';
import './FilterElementModal.less';
const FilterElement = (props) => {
const FilterElementModal = (props) => {
const { onCancel } = props;
const { visible, onCancel } = props;
const [ elements, setElements ] = useState([]);
const [ typesOfElements, setTypesOfElements ] = useState([]);
const [ selectedKeys, setSelectedKeys ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false);
useEffect(() => {
......@@ -87,19 +88,56 @@ const FilterElement = (props) => {
const onOk = () => {
setConfirmLoading(true);
dispatch({
type: 'assetmanage.setupFilterElementIds',
payload: {
data: selectedKeys
},
callback: () => {
onCancel && onCancel(false, true);
reset();
onCancel && onCancel(true);
},
error: () => {
reset();
}
})
}
const cancel = () => {
reset();
onCancel && onCancel();
}
const reset = () => {
setConfirmLoading(false);
}
return (
<div className='filter-element' style={{ width: 500 }}>
<Modal
forceRender
visible={visible}
title='资产要素过滤'
width={520}
onCancel={cancel}
footer={[
<Button
key="0"
onClick={cancel}
>
取消
</Button>,
<Button
key="1"
type="primary"
onClick={onOk}
loading={confirmLoading}
>
确定
</Button>,
]}
>
<div className='d-flex'>
<Switch
checkedChildren="全不选"
......@@ -108,13 +146,13 @@ const FilterElement = (props) => {
style={{ marginLeft: 'auto' }}
/>
</div>
<div style={{ maxHeight: 450, overflow: 'auto' }}>
<div className='mt-3' style={{ maxHeight: 450, overflow: 'auto' }}>
{
(typesOfElements||[]).map((typeOfElements, index) => {
const _type = typeOfElements.type||'';
return (
<div>
<div key={index}>
<div className='flex' style={{ alignItems: 'center', padding: '15px 0' }}>
<div style={{ width: 3, height: 14, backgroundColor: '#0069AC', marginRight: 5 }} />
<span style={{ fontWeight: 'bold', color: '#464646' }}>{_type||''}</span>
......@@ -141,16 +179,8 @@ const FilterElement = (props) => {
})
}
</div>
<div className='mt-3 d-flex pt-3' style={{ borderTop: '1px solid rgba(0, 0, 0, 0.06)' }} >
<Space style={{ marginLeft: 'auto' }}>
<Button onClick={() => {
onCancel && onCancel();
}}>取消</Button>
<Button type='primary' onClick={onOk} >确定</Button>
</Space>
</div>
</div>
</Modal>
);
}
export default FilterElement;
\ No newline at end of file
export default FilterElementModal;
\ No newline at end of file
......@@ -19,7 +19,7 @@ const MetadataInfo = ({ value = '', config = true }) => {
value => <span>
{
(typeof metadata==='string') ? <span style={{ marginRight: 5 }}>{metadata||''}</span> : <a onClick={() => {
value?.setGlobalState({
value?.setGlobalState && value?.setGlobalState({
message: 'data-govern-show-metadata-message',
data: metadata
})
......
......@@ -7,7 +7,7 @@ import { showMessage } from '../../../../util';
const AssetMount = (props) => {
const { onCancel, visible, id, recycleIds, refrence = 'asset-manage' } = props;
const { onCancel, visible, ids, refrence = 'asset-manage' } = props;
const [ dirIds, setDirIds ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false);
......@@ -28,7 +28,7 @@ const AssetMount = (props) => {
params: {
dirId: dirIds.join(","),
},
data: (refrence==='asset-recycle') ? recycleIds : [id||'']
data: ids
},
callback: data => {
setConfirmLoading(false);
......@@ -48,7 +48,7 @@ const AssetMount = (props) => {
return(
<Modal
title='挂载详情'
title='变更目录详情'
visible={ visible }
width={ 400 }
confirmLoading={ confirmLoading }
......@@ -63,7 +63,7 @@ const AssetMount = (props) => {
checkable={true}
showCustom={false}
onCheck={onCheck}
tableId={refrence==='asset-manage'?id:''}
tableId={(refrence==='asset-manage'&&(ids||[].length>0))?ids[0]:''}
reference='mount'
{...props}
/>
......
......@@ -248,7 +248,7 @@ const AssetRecycle = (props) => {
<AssetMount
refrence='asset-recycle'
visible={ assetMountVisible }
recycleIds={ batchMount ? selectedRowKeys : [ currentAssetId ] }
ids={ batchMount ? selectedRowKeys : [ currentAssetId ] }
onCancel={ onAssetMountCancel }
{...props}
/>
......
......@@ -753,7 +753,7 @@ const ImportActionTable = (props) => {
}
{
record?.isPossibleNewTerm?.possible && <Typography.Link className='mr-3' onClick={() => {
value?.setGlobalState({
value?.setGlobalState && value?.setGlobalState({
message: 'data-govern-show-standard-create',
data: {
column: record,
......
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