wenlele 1 year ago
parent
commit
321481284a
  1. 38
      api/app/lib/controllers/metadataSearch/index.js
  2. BIN
      web/client/assets/images/background/searchbg.png
  3. 25
      web/client/index.ejs
  4. 31
      web/client/index.html
  5. 15
      web/client/src/sections/auth/containers/login.js
  6. 26
      web/client/src/sections/metadataManagement/nav-item.js
  7. 106
      web/client/src/sections/resourceRetrieval/containers/retrieval.js
  8. 46
      web/client/src/sections/resourceRetrieval/containers/style.less
  9. 2
      web/client/src/sections/resourceRetrieval/routes.js
  10. 5
      web/client/src/utils/webapi.js

38
api/app/lib/controllers/metadataSearch/index.js

@ -6,7 +6,9 @@ function searchMeta(opts) {
try {
const models = ctx.fs.dc.models;
const { keywords } = ctx.query;
const where = {};
const where = {
};
if (keywords) {
where['$or'] = [{ name: { $iLike: `%${keywords}%` } }
@ -20,12 +22,42 @@ function searchMeta(opts) {
model: models.Tag,
}]
}],
where: {
...where,
type: { $in: ['表', '库'] }
}
}
const findObj2 = {
include: [
{
model: models.TagFile,
include: [{
model: models.Tag,
}]
}],
where: where
}
const findObj3 = {
include: [
{
model: models.TagRestapi,
include: [{
model: models.Tag,
}]
}],
where: where
}
const rslt = await models.MetadataDatabase.findAndCountAll(findObj);
const rslt1 = await models.MetadataDatabase.findAndCountAll(findObj);
const rslt2 = await models.MetadataFile.findAndCountAll(findObj2);
const rslt3 = await models.MetadataRestapi.findAndCountAll(findObj3);
const fileRslt = rslt2.rows.map(s => { return { ...s.dataValues, type: '文件', tagDatabases: s.tagFiles } })
const restapiRslt = rslt3.rows.map(s => { return { ...s.dataValues, type: '接口', tagDatabases: s.tagRestapis } })
ctx.status = 200;
ctx.body = rslt;
ctx.body = {
count: rslt1.count + rslt2.count + rslt3.count,
rows: rslt1.rows.concat(fileRslt).concat(restapiRslt)
};
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;

BIN
web/client/assets/images/background/searchbg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

25
web/client/index.ejs

@ -1,17 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="/assets/font_sc/iconfont.css">
</head>
<style>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="/assets/font_sc/iconfont.css">
</head>
<style>
@font-face {
font-family: YouSheBiaoTiHei;
src: url("/assets/font_sc/YouSheBiaoTiHei-2.ttf");
}
</style>
</style>
<body style="background: transparent">
<div id='App'></div>
</body>
<body style="background: transparent">
<div id='App'></div>
</body>
</html>

31
web/client/index.html

@ -1,20 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="/assets/font_sc/iconfont.css">
</head>
<style>
</style>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="/assets/font_sc/iconfont.css">
</head>
<style>
@font-face {
font-family: YouSheBiaoTiHei;
src: url("/assets/font_sc/YouSheBiaoTiHei-2.ttf");
}
</style>
<body>
<div id='App'></div>
<script type="text/javascript" src="http://localhost:5401/client/build/vendor.js"></script>
<script type="text/javascript" src="http://localhost:5401/client/build/app.js"></script>
</body>
<body>
<div id='App'></div>
<script type="text/javascript" src="http://localhost:5401/client/build/vendor.js"></script>
<script type="text/javascript" src="http://localhost:5401/client/build/app.js"></script>
</body>
</html>

15
web/client/src/sections/auth/containers/login.js

@ -114,7 +114,7 @@ const Login = props => {
</FormItem>
<FormItem>
<Input
style={{ marginTop: 30 }}
style={{ marginTop: 30 }}
placeholder='请输入密码'
className='loginInp'
type="password"
@ -126,6 +126,19 @@ const Login = props => {
/>
</FormItem>
</Form>
<Row style={{
paddingLeft: '10%'
}}>
{
inputChanged || !error ?
<span style={{
visibility: 'hidden'
}}>-</span> :
<span>
<ExclamationCircleOutlined style={{ color: 'red' }} />{error}
</span>
}
</Row>
<Button style={{ borderRadius: 28 }} type="primary" className='loginBtn' loading={isRequesting} onClick={doLogin}>登录</Button>
</div>
</div>

26
web/client/src/sections/metadataManagement/nav-item.js

@ -4,21 +4,27 @@ import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem() {
export function getNavItem(user) {
return (
<SubMenu key="metadataManagement" icon={<SettingOutlined />} title='元数据管理'>
<Menu.Item key="latestMetadata">
<Link to="/metadataManagement/latestMetadata">最新元数据</Link>
</Menu.Item>
<Menu.Item key="businessMetadata">
<Link to="/metadataManagement/businessMetadata">业务元数据管理</Link>
</Menu.Item>
<Menu.Item key="metaModelManagement">
<Link to="/metadataManagement/metaModelManagement">模型管理</Link>
</Menu.Item>
<Menu.Item key="tagManagement">
<Link to="/metadataManagement/tagManagement">标签管理</Link>
</Menu.Item>
{user?.role == '系统管理员' &&
<>
<Menu.Item key="businessMetadata">
<Link to="/metadataManagement/businessMetadata">业务元数据管理</Link>
</Menu.Item>
<Menu.Item key="metaModelManagement">
<Link to="/metadataManagement/metaModelManagement">模型管理</Link>
</Menu.Item>
<Menu.Item key="tagManagement">
<Link to="/metadataManagement/tagManagement">标签管理</Link>
</Menu.Item>
</>
}
</ SubMenu >
);
}

106
web/client/src/sections/resourceRetrieval/containers/retrieval.js

@ -1,50 +1,90 @@
import React, { useEffect, useState } from 'react'
import { Input } from 'antd'
import { Input, Tooltip, Empty } from 'antd'
import { InsertRowBelowOutlined, DatabaseOutlined, FileOutlined, PullRequestOutlined } from '@ant-design/icons';
import { useFsRequest, ApiTable } from '$utils';
import './style.less';
function Retrieval(props) {
const renderMetaData = (type) => {
const [keywords, setKeywords] = useState()
const [firstInput, setFirstInput] = useState()
const { data: catalogs = [] } = useFsRequest({
url: ApiTable.getResourceCatalog,
});
const { data: result = {} } = useFsRequest({
url: ApiTable.searchMetadata,
query: {
keywords: keywords
},
refreshDeps: [keywords],
ready: !!keywords
});
console.log(result)
const renderIcon = (type) => {
switch (type) {
case '库':
return <div className='result-row'>
<div className='column1'>
<InsertRowBelowOutlined /> 代码T29E_34AC 名称政务信息变更 类型库表/Table 路径/demoABI/govern_metadata/ 标签XX
</div>
<div className='column2'>相关操作<a>定位</a></div>
</div>
return <DatabaseOutlined />
case '表':
return <div className='result-row'>
<div className='column1'>
<DatabaseOutlined /> 代码init_metadata 名称政务大数据平台 类型库表/Schema 路径/demoABI 标签XX
</div>
<div className='column2'>相关操作<a>定位</a><a></a></div>
</div>
return <InsertRowBelowOutlined />
case '文件':
return <div className='result-row'>
<div className='column1'>
<FileOutlined /> 名称政务大数据平台 类型文件File 路径/demoABI 标签XX
</div>
<div className='column2'>相关操作<a>定位</a><a></a></div>
</div>
return <FileOutlined />
case '接口':
return <div className='result-row'>
<div className='column1'>
<PullRequestOutlined /> 名称政务大数据平台 类型接口Api 路径/demoABII 标签XX
</div>
<div className='column2'>相关操作<a>定位</a></div>
</div>
return <PullRequestOutlined />
default:
break;
}
}
return <>
<Input.Search style={{ width: '30%', marginLeft: 10, marginBottom: 30 }} />
{renderMetaData('库')}
{renderMetaData('表')}
{renderMetaData('文件')}
{renderMetaData('接口')}
</>
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
}
return !result?.rows ? <div className='search-container'>
<div className='title'>数据资源检索</div>
<Input addonAfter={<div onClick={() => { setKeywords(firstInput) }} style={{ color: '#fff' }}>搜索一下</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 => { setKeywords(value) }}
/>
{result?.rows?.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> 名称{renderText(s?.name)}</span>
<span> 类型{s?.type}</span>
<span> 路径{renderText(catalogText)}</span>
<span> 标签{renderText(tagText)}</span>
</div>
<div className='column2'>相关操作<a>定位</a>
{s?.type == '表' || s?.type == '文件' ? <a>下载</a> : ''}
</div>
</div>
})}
{!result?.rows?.length > 0 && <Empty />}
</>
}
export default Retrieval

46
web/client/src/sections/resourceRetrieval/containers/style.less

@ -1,11 +1,12 @@
.result-row {
display: flex;
border-bottom: 1px solid #E8E8E8;
width: 90%;
width: 95%;
padding: 10px;
margin-bottom: 15px;
font-size: 15px;
.column1 {
display: flex;
@ -15,6 +16,10 @@
}
width: calc(100% - 180px);
span {
margin-right: 10px;
}
}
.column2 {
@ -26,4 +31,43 @@
margin-right: 8px;
}
}
}
.search-container {
background-image: url(/assets/images/background/searchbg.png);
background-size: 100% 100%;
height: 85vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.title {
width: 100%;
font-family: YouSheBiaoTiHei;
font-size: 80px;
color: #0066DC;
letter-spacing: 0;
text-align: center;
margin-bottom: 68px;
}
.ant-input-group>.ant-input:last-child,
.ant-input-group-addon:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
background-image: linear-gradient(90deg, #1F5BF3 0%, #8FBCFF 100%);
color: #fff;
}
.ant-input-group .ant-input {
float: left;
width: 100%;
margin-bottom: 0;
text-align: inherit;
border: 1px solid #6D96FF;
height: 44px;
}
}

2
web/client/src/sections/resourceRetrieval/routes.js

@ -5,7 +5,7 @@ export default [{
route: {
path: '/resourceRetrieval',
key: 'resourceRetrieval',
breadcrumb: '资源消费',
breadcrumb: '资源检索',
component: Retrieval
}
}];

5
web/client/src/utils/webapi.js

@ -76,11 +76,14 @@ export const ApiTable = {
getLogs: "meta/acq/logs",
//资源消费
approveList:'resource/approve',
approveList: 'resource/approve',
//用户管理
getUserList: 'meta/members',
addUser: 'meta/member',
modifyUser: 'meta/member/{id}',
//元数据检索
searchMetadata: "meta/data/search",
};
export const RouteTable = {

Loading…
Cancel
Save