You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
9.7 KiB
255 lines
9.7 KiB
import React, { useEffect, useState } from 'react'
|
|
import { Input, Tooltip, Empty, Pagination, Popover, message } from 'antd'
|
|
import { InsertRowBelowOutlined, DatabaseOutlined, FileOutlined, PullRequestOutlined, KeyOutlined } from '@ant-design/icons';
|
|
import { useFsRequest, ApiTable } from '$utils';
|
|
import { connect } from 'react-redux';
|
|
import { downloadImg, markRedKeywords } from '../utils/index'
|
|
import KeyModal from '../components/keyModal';
|
|
import xlsx from 'xlsx';
|
|
const METADTA_TYPE = {
|
|
'库': 'databases',
|
|
'表': 'databases',
|
|
'文件': 'files',
|
|
'接口': 'restapis',
|
|
}
|
|
|
|
const METADTA_TYPE_NAMES = {
|
|
'库': '库表/库(Schema)',
|
|
'表': '库表/表(Table)',
|
|
'文件': '文件(File)',
|
|
'接口': '接口(Api)',
|
|
}
|
|
|
|
import './style.less';
|
|
function Retrieval(props) {
|
|
const { user, catalogs, dispatch, actions } = props;
|
|
const [keywords, setKeywords] = useState()
|
|
const [firstInput, setFirstInput] = useState()
|
|
const [page, setPage] = useState(1)
|
|
const [currentData, setCurrentData] = useState(null)
|
|
const [searchDataId, setSearchDataId] = useState(null)
|
|
|
|
const formRef = React.createRef();
|
|
// const { data: catalogs = [] } = useFsRequest({
|
|
// url: ApiTable.getResourceCatalog,
|
|
// });
|
|
useEffect(() => {
|
|
dispatch(actions.metadataManagement.getResourceCatalog())
|
|
}, [])
|
|
|
|
//检索元数据
|
|
const { data: result = {} } = useFsRequest({
|
|
url: ApiTable.searchMetadata,
|
|
query: {
|
|
keywords: keywords
|
|
},
|
|
refreshDeps: [keywords],
|
|
ready: !!keywords
|
|
});
|
|
|
|
//用户申请资源列表
|
|
const { data: approveList = {} } = useFsRequest({
|
|
url: ApiTable.approveList,
|
|
query: {
|
|
applyById: user?.id
|
|
},
|
|
refreshDeps: [user?.id],
|
|
ready: !!(user?.id)
|
|
});
|
|
|
|
//检索元数据
|
|
const { data: tableData = {} } = useFsRequest({
|
|
url: 'meta/table/data',
|
|
query: {
|
|
id: searchDataId
|
|
},
|
|
refreshDeps: [searchDataId],
|
|
ready: !!searchDataId,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (tableData?.rslt && tableData?.metaDataChildren) handleTableExport()
|
|
}, [tableData])
|
|
|
|
const handleTableExport = () => {
|
|
const metaData = result?.rows?.find(s => s.id == searchDataId)
|
|
if (!metaData) return;
|
|
let excelTitle = tableData?.metaDataChildren.map(s => {
|
|
return { k: s, v: s }
|
|
});
|
|
let workBook = {
|
|
SheetNames: [], //sheet名称
|
|
Sheets: {} //根据SheetNames名称顺序依次添加每个sheet数据
|
|
};
|
|
if (tableData?.rslt && tableData?.rslt?.length > 0) {
|
|
let sheetName = '元数据列表';
|
|
let sheetDataMap = new Map();
|
|
let sheetData = [excelTitle];
|
|
let index = 1;
|
|
tableData?.rslt.map(data => {
|
|
const arr = []
|
|
tableData?.metaDataChildren.map(s => {
|
|
arr.push(
|
|
{ k: s, v: JSON.stringify(data[s]) },
|
|
);
|
|
});
|
|
sheetData.push(arr)
|
|
index = index + 1;
|
|
})
|
|
|
|
sheetDataMap.set(sheetName, sheetData);
|
|
sheetDataMap.forEach((values, key) => {
|
|
// 写入excel
|
|
workBook.Sheets[key] = xlsx.utils.aoa_to_sheet(values);
|
|
workBook.Sheets[key]['!cols'] = [{ wpx: 50 }, { wpx: 150 }, { wpx: 180 }, { wpx: 230 }, { wpx: 230 }, { wpx: 230 }]
|
|
})
|
|
workBook.SheetNames = [sheetName];
|
|
// 将workBook写入文件
|
|
xlsx.writeFile(workBook, `${metaData?.name}-data.xlsx`);
|
|
} else {
|
|
message.info('当前资源暂无数据!')
|
|
}
|
|
|
|
}
|
|
|
|
const renderIcon = (type) => {
|
|
switch (type) {
|
|
case '库':
|
|
return <DatabaseOutlined />
|
|
case '表':
|
|
return <InsertRowBelowOutlined />
|
|
case '文件':
|
|
return <FileOutlined />
|
|
case '接口':
|
|
return <PullRequestOutlined />
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const renderCatalog = (catalogId, rslt = '') => {
|
|
let resource = rslt
|
|
let catalog = catalogs.find(s => s.id == catalogId)
|
|
if (catalog?.parent) {
|
|
resource += renderCatalog(catalog.parent, catalog?.name + '/')
|
|
} else {
|
|
resource += catalog?.name
|
|
}
|
|
return resource
|
|
}
|
|
|
|
const renderText = (text) => {
|
|
return text?.length > 16 ? <Tooltip placement="top" title={text}>
|
|
{text.substring(0, 16) + '...'}
|
|
</Tooltip> : text
|
|
}
|
|
|
|
const renderName = (text) => {
|
|
function createMarkup(textFilter) {
|
|
return { __html: textFilter.length == text.length ? markRedKeywords(textFilter, keywords) : markRedKeywords(textFilter, keywords) + '...' };
|
|
}
|
|
return text?.length > 16 ?
|
|
<Popover placement="topRight" title={null} content={<span dangerouslySetInnerHTML={createMarkup(text)} />}>
|
|
<span dangerouslySetInnerHTML={createMarkup(text.substring(0, 16))} />
|
|
</Popover>
|
|
: <span dangerouslySetInnerHTML={createMarkup(text)} />
|
|
}
|
|
|
|
const downloadData = (s) => {
|
|
if (s?.type == '文件') {
|
|
const suffix = s?.fileName?.substring(s?.fileName?.length - 3, s?.fileName?.length)
|
|
if (suffix == 'png' || suffix == 'jpg') {
|
|
downloadImg(s.fileName)
|
|
} else {
|
|
window.open('/assets/files/common/' + s.fileName)
|
|
}
|
|
} else {
|
|
setSearchDataId(null)
|
|
setTimeout(() => {
|
|
setSearchDataId(s.id)
|
|
}, 10);
|
|
}
|
|
}
|
|
|
|
return !result?.rows ? <div className='search-container'>
|
|
<div className='title'>数据资源检索</div>
|
|
<Input addonAfter={<div onClick={() => { setKeywords(firstInput) }} style={{ color: '#fff', cursor: 'pointer' }}>搜索一下</div>}
|
|
style={{ width: '40%', marginLeft: 10, marginBottom: 30 }}
|
|
onChange={e => { setFirstInput(e.target.value) }}
|
|
onPressEnter={e => { setKeywords(e.target.value) }}
|
|
/>
|
|
</div> :
|
|
<>
|
|
<Input.Search defaultValue={keywords} style={{ width: '30%', marginLeft: 10, marginBottom: 30 }}
|
|
|
|
onSearch={value => {
|
|
setPage(1)
|
|
setKeywords(value)
|
|
}}
|
|
/>
|
|
{result?.rows?.slice((page - 1) * 10, (page - 1) * 10 + 10).map(s => {
|
|
const catalogText = renderCatalog(s?.catalog).split('/').reverse().toString().replaceAll(',', '/')
|
|
const tagText = s?.tagDatabases?.map(x => x.tag.name).toString() || '-'
|
|
return <div className='result-row'>
|
|
<div className='column1'>
|
|
{renderIcon(s?.type)}
|
|
{s?.code && <span> 代码:{renderText(s?.code)}</span>}
|
|
<span> 名称:{renderName(s?.name)}</span>
|
|
<span> 类型:{METADTA_TYPE_NAMES[s?.type]}</span>
|
|
<span> 路径:{renderText(catalogText)}</span>
|
|
<span> 标签:{renderText(tagText)}</span>
|
|
</div>
|
|
<div className='column2'>相关操作:<a onClick={() => { window.open(`/metadataManagement/latestMetadata?type=${METADTA_TYPE[s.type]}&treeId=${s?.catalog}&resourceId=${s.id}&catalogKey=${s.catalogKey}`) }}>定位</a>
|
|
{s?.type == '表' || s?.type == '文件' ?
|
|
user?.username !== 'SuperAmin' ?
|
|
<a onClick={() => {
|
|
const isOrgAdmin = user?.role == '系统管理员' && user?.orgId == s?.resourceCatalog?.organization?.id
|
|
if (isOrgAdmin) {
|
|
downloadData(s)
|
|
} else {
|
|
const token = approveList?.rows?.find(x => x.resourceId == s.id)?.token
|
|
if (!token) {
|
|
message.warning('您暂未申请该数据资源,请先申请该数据资源')
|
|
} else {
|
|
setCurrentData(s)
|
|
}
|
|
}
|
|
}}>下载</a> :
|
|
<a onClick={() => { downloadData(s) }}>下载</a> : ''}
|
|
</div>
|
|
</div>
|
|
})}
|
|
{result?.rows?.length > 0 && <Pagination
|
|
key={keywords}
|
|
defaultCurrent={1} total={result?.rows?.length}
|
|
style={{ float: 'right', paddingRight: '5%' }}
|
|
showTotal={total => `共 ${total} 条数据`}
|
|
showSizeChanger={false}
|
|
onChange={(page, pageSize) => {
|
|
setPage(page)
|
|
}}
|
|
/>}
|
|
{!result?.rows?.length > 0 && <Empty />}
|
|
|
|
{currentData && <KeyModal
|
|
resourceId={currentData?.id}
|
|
approveList={approveList}
|
|
onFinish={() => {
|
|
downloadData(currentData)
|
|
setCurrentData(null)
|
|
}}
|
|
onCancel={() => { setCurrentData(null) }}
|
|
/>}
|
|
</>
|
|
}
|
|
|
|
function mapStateToProps(state) {
|
|
const { auth, resourceCatalog, global } = state;
|
|
return {
|
|
catalogs: resourceCatalog?.data || [],
|
|
user: auth.user,
|
|
actions: global.actions,
|
|
};
|
|
}
|
|
export default connect(mapStateToProps)(Retrieval)
|
|
|
|
|