Commit 6eab010a by zhaochengxiang

移植迈瑞资产多模版

parent 2cd87653
......@@ -12,10 +12,14 @@ module.exports = {
'@ant-prefix': 'yy',
// '@border-color-split': 'hsv(0, 0, 85%)',
'@border-radius-base': '4px',
'@primary-color': '#196AD2',
'@primary-color': '#ffbc00',
'@success-color': '#8FC31F',
'@warning-color': '#F7AB00',
'@error-color': '#E94848',
'@error-color': '#db0007',
'@menu-dark-bg': '#00477F',
'@menu-dark-submenu-bg': '#013D6C',
'@menu-dark-inline-submenu-bg': '#013D6C',
'@layout-header-background': '#00477F',
},
javascriptEnabled: true,
},
......
......@@ -75,6 +75,6 @@
"last 1 safari version"
]
},
"proxy": "http://139.198.127.28:17277",
"proxy": "http://139.198.126.96:8089",
"homepage": "http://myhost/data-govern"
}
......@@ -4,7 +4,7 @@ import {
Route, Switch
} from 'react-router-dom';
import { $hostParams, ContextPath } from './util';
import { $hostParams, ContextPath, generateUUID } from './util';
import Signin from './view/Signin';
import Home from './view/Home';
import Manage from './view/Manage';
......@@ -32,6 +32,8 @@ import { AssetMountReference } from './util/constant';
export const AppContext = React.createContext();
export const appId = generateUUID();
export class App extends React.Component {
constructor() {
super();
......
import { GetJSON } from "../util/axios"
import { GetJSON, PostJSON, callFetchRaw, PostFile } from "../util/axios"
export function queryDatabase(payload) {
return GetJSON("/metadatarepo/rest/tag/getDatabaseBySystemCode", payload);
......@@ -14,4 +14,28 @@ export function queryAllDataTable(payload) {
export function queryAllFields(payload) {
return GetJSON("/metadatarepo/rest/metadata/getChild", payload);
}
export function getSystemAllGraph(payload) {
return GetJSON("/metadatarelation/getSystemAllGraph", payload);
}
export function getAttributes(payload) {
return PostJSON("/metadatarepo/rest/metadata/getByIdList", payload);
}
export function exportAttributes(payload) {
return callFetchRaw('post', '/metadatarepo/rest/download/exportAssetInfo', payload)
}
export function importAttributes(payload) {
return PostFile("/metadatarepo/rest/import/importAssetInf", payload);
}
export function listImportAssetLog(payload) {
return GetJSON('/metadatarepo/rest/import/listImportAssertLog', payload);
}
export function getImportAssetLogDetail(payload) {
return GetJSON('/metadatarepo/rest/import/getImportAssertLog', payload);
}
\ No newline at end of file
import axios from 'axios';
import LocalStorage from 'local-storage';
import { appId } from '../App';
import { IsArr, showMessage, isSzseEnv } from './index';
......@@ -101,12 +102,16 @@ const callback = resp => {
return resp.data || resp;
}
export function getTemplateType() {
return LocalStorage.get(`templateType-${appId}`)
}
export function Get(url, params) {
const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv;
return textplain.get(url, {
params: {...params, env}, cancelToken
params: {...params, env, templateType: getTemplateType()}, cancelToken
}).then(
callback
)
......@@ -117,7 +122,7 @@ export function GetJSON(url, params) {
const env = LocalStorage.get('assetsEnv')??debugEnv;
return instance.get(url, {
params: {...params, env}, cancelToken,
params: {...params, env, templateType: getTemplateType()}, cancelToken,
validateStatus: false
}).then(
callback
......@@ -129,7 +134,7 @@ export function Delete(url, params) {
const env = LocalStorage.get('assetsEnv')??debugEnv;
return instance.delete(url, {
params: {...params, env}, cancelToken,
params: {...params, env, templateType: getTemplateType()}, cancelToken,
}).then(
callback
)
......@@ -141,11 +146,11 @@ export function PostJSON(url, payload) {
const env = LocalStorage.get('assetsEnv')??debugEnv;
return IsArr(data) ? instance.post(url, data, {
params: {...params, env}, cancelToken
params: {...params, env, templateType: getTemplateType()}, cancelToken
}).then(
callback
) : instance.post(url, null, {
params: {...params, env}, data, cancelToken
params: {...params, env, templateType: getTemplateType()}, data, cancelToken
}).then(
callback
)
......@@ -157,7 +162,7 @@ export function Post(url, payload) {
const env = LocalStorage.get('assetsEnv')??debugEnv;
return textplain.post(url, null, {
params: {...params, env}, data, cancelToken
params: {...params, env, templateType: getTemplateType()}, data, cancelToken
}).then(
callback
)
......@@ -172,7 +177,7 @@ export function PostFile(url, payload, fileName='file') {
formData.append(fileName, file);
});
return fileplain.post(url, formData, { params: {...params, env} } ).then(
return fileplain.post(url, formData, { params: {...params, env, templateType: getTemplateType()} } ).then(
callback
)
}
......@@ -191,6 +196,7 @@ export const callFetchRaw = (method, url, options) => {
Object.keys(params||[]).forEach(key => {
bodyFormData.append(key, params[key]);
});
bodyFormData.append('templateType', getTemplateType());
return axios.request({
method,
......
import { AxiosResponse } from "axios"
export default function (res: AxiosResponse<any>) {
const blob = res.data
const headers = res.headers
let tempName = headers["content-disposition"]
?.split(";")?.[1]
?.split("filename=")?.[1];
tempName = decodeURI(tempName);
// const blob = new Blob([content], { type: 'application/octet-stream' })
var url = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(blob) : window.webkitURL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', tempName); //or any other extension
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href) // 释放URL 对象
document.body.removeChild(link)
}
\ No newline at end of file
import { useEffect, useState, useContext } from 'react';
import { useEffect, useState } from 'react';
import { Radio } from 'antd';
import { dispatch } from '../../../../model';
import Relation from './Relation';
import Thermodynamic from './Thermodynamic';
import { AssetBrowseReference, ResourceBrowseReference } from '../../../../util/constant';
import { AppContext } from '../../../../App';
const relationTypes = [
{
......@@ -15,6 +14,10 @@ const relationTypes = [
{
title: '热力图',
key: 'thermodynamic'
},
{
title: '数据地图',
key: 'map',
}
]
......@@ -22,8 +25,6 @@ const RelationContainer = (props) => {
const { nodeParams, onChange, reference, resize } = props;
const { env } = useContext(AppContext);
const [ type, setType ] = useState('relation');
const [ dirs, setDirs ] = useState([]);
const [ nodes, setNodes ] = useState([]);
......@@ -33,7 +34,7 @@ const RelationContainer = (props) => {
useEffect(() => {
getDirectoryData();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ env ])
}, [])
useEffect(() => {
if (type === 'relation') {
......@@ -45,7 +46,7 @@ const RelationContainer = (props) => {
setRelationData(null);
}
} else {
if ((nodeParams?.centerId||'')!== '' && (dirs||[]).length>0) {
if ((nodeParams?.centerId||'')!=='' && (dirs||[]).length>0) {
generateThermodynamicData();
} else {
setThermodynamicData(null);
......@@ -184,8 +185,10 @@ const RelationContainer = (props) => {
let nodeLevelId = `${node.levelId}-`;
let currentLevelId = `${currentNode.levelId}-`;
if ((node.levelId.split('-').length===currentNode.levelId.split('-').length+1) && node.levelId.slice(0, currentLevelId.length)===currentLevelId) {
node.show = true;
if (currentNode.levelId.split('-').length > 1) {
if ((node.levelId.split('-').length===currentNode.levelId.split('-').length+1) && node.levelId.slice(0, currentLevelId.length)===currentLevelId) {
node.show = !node.show;
}
}
if (node.levelId.split('-').length >2) {
......@@ -201,6 +204,7 @@ const RelationContainer = (props) => {
}
})
console.log('data', data)
setNodes(data);
generateCenterNodeRelationData(data);
......@@ -261,7 +265,11 @@ const RelationContainer = (props) => {
}
const onTypeChange = (e) => {
setType(e.target.value);
if (e.target.value === 'map') {
window?.open('/center-home/menu/asset-map');
} else {
setType(e.target.value);
}
}
return (
......
import React from 'react';
import * as echarts from 'echarts';
import { dispatch } from '../../../../model'
//https://echarts.apache.org/examples/zh/editor.html?c=treemap-show-parent&lang=js
class Thermodynamic extends React.Component {
componentDidMount() {
const { data, onClick } = this.props;
if (data) {
if (data && data.length>0 && (data[0].children||[]).length>0) {
this.graph = init(this)(this.elem, data, onClick);
this.getScore();
}
}
......@@ -17,37 +16,20 @@ class Thermodynamic extends React.Component {
const { data, onClick, resize } = this.props;
if (data !== prevProps.data) {
this.graph?.dispose();
this.graph = init(this)(this.elem, data, onClick);
this.getScore();
} else if (resize !== prevProps.resize) {
this.graph?.resize();
}
}
getScore() {
const { data } = this.props;
dispatch({
type: 'assetmanage.getScore',
payload: {
dirId: (data?.length>0) ? data[0]?.nodeId: ''
},
callback: newScore => {
this.graph?.setOption({
title: {
subtext: `价值评分 ${newScore}`,
itemGap: 2,
}
})
}
})
}
elem = undefined;
graph = undefined;
render() {
return (
<div ref={ref => this.elem = ref} style={{ position: 'relative', width: '100%', height: '100%' }}></div>
<div ref={ref => this.elem = ref} style={{ position: 'relative', width: '100%', height: '100%' }}>
</div>
);
}
}
......@@ -61,7 +43,7 @@ const init = (ctx) => function (container, data, onClick) {
return [
{
itemStyle: {
borderColor: '#777',
borderColor: ((data||[]).length>0)&&(data[0].dataAssetAndSubDirCount===0||(data[0].children||[]).reduce((preVal, item) => item?.dataAssetAndSubDirCount + preVal, 0)===0) ? '#97aeed' : '#777',
borderWidth: 0,
gapWidth: 1
},
......@@ -96,9 +78,9 @@ const init = (ctx) => function (container, data, onClick) {
title: {
text: ((data||[]).length>0) ? `${data[0]?.text||''} (${data[0]?.dataAssetAndSubDirCount})` : '',
left: 'center',
bottom: 0,
bottom: 10,
textStyle: {
fontSize: 14
fontSize: 16
}
},
tooltip: {
......@@ -117,20 +99,22 @@ const init = (ctx) => function (container, data, onClick) {
},
series: [
{
name: ((data||[]).length>0) ? `${data[0]?.text||''} (${data[0]?.dataAssetAndSubDirCount})` : '',
type: 'treemap',
height: '85%',
visibleMin: 0,
childrenVisibleMin: 0,
visibleMin: 1,
childrenVisibleMin: 1,
label: {
show: true,
formatter: '{b}'
},
upperLabel: {
show: true,
height: 30
height: 30,
color: '#fff',
},
itemStyle: {
borderColor: '#fff'
borderColor: '#555'
},
breadcrumb: {
show: false,
......@@ -144,7 +128,6 @@ const init = (ctx) => function (container, data, onClick) {
myChart.setOption(option);
myChart.on('click', function (params) {
console.log(params);
const { data } = params;
if (onClick && data && data.nodeId) {
......
import React, { useState } from 'react';
import classNames from 'classnames';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import { ResizableBox } from 'react-resizable';
import AssetTree from '../AssetManage/Component/AssetTree';
import AssetDirectory from '../AssetManage/Component/AssetDirectory';
......@@ -16,16 +17,17 @@ const AssetBrowse = (props) => {
const { reference = AssetBrowseReference } = props;
const [ nodeParams, setNodeParams ] = useState({ centerId: '', expandId: '' });
const [ nodeParams, setNodeParams ] = useState({ centerId: null, expandId: '', nodeType: '' });
const [ expandTree, setExpandTree ] = useState(true);
const [ expandRelation, setExpandRelation ] = useState(true);
const [ assetCount, setAssetCount ] = useState(0);
const [ resizeRelation, setResizeRelation ] = useState(false);
const [ assetFullScreen, setAssetFullScreen ] = useState(false);
const { centerId, expandId } = nodeParams;
const onTreeSelect = (value, type) => {
setNodeParams({ centerId: value||'', expandId: '' });
setNodeParams({ centerId: value, expandId: '', nodeType: type });
}
const treeToggleClick = () => {
......@@ -46,48 +48,55 @@ const AssetBrowse = (props) => {
setAssetCount(count);
}
let nodeId = '';
if ((expandId||'') !== '') {
nodeId = expandId;
} else {
nodeId = centerId;
const onFullScreenChange = (value) => {
setAssetFullScreen(value);
}
const classes = classNames('asset-browse', {
'asset-browse-tree-collapse': !expandTree,
'asset-browse-relation-collapse': !expandRelation,
});
});
const rightClasses = classNames('right', {
'right-fullscreen': assetFullScreen
});
return (
<div className={classes}>
<div className='left'>
<ResizableBox
className='left'
width={230}
height={Infinity}
axis='x'
minConstraints={[230, Infinity]} maxConstraints={[Infinity, Infinity]}
>
<AssetTree centerId={centerId} onSelect={onTreeSelect} reference={reference} {...props} />
</div>
</ResizableBox>
{
expandTree && <Separate width={15} />
}
<div className='right'>
<AssetDirectory id={nodeId} assetCount={assetCount} reference={reference} />
<Separate height={15} />
<div className={rightClasses}>
{/* <AssetDirectory id={centerId} assetCount={assetCount} reference={reference} nodeType={nodeParams.nodeType} />
<Separate height={15} /> */}
<div className='flex' style={{ flex: 1, height: '100%', overflow: 'hidden' }}>
{
expandRelation && <React.Fragment>
<div style={{ flex: 1, height: '100%', overflow: 'hidden' }}>
<RelationContainer reference={reference} nodeParams={nodeParams} onChange={onRelationChange} resize={resizeRelation} />
</div>
<Separate width={15} />
</React.Fragment>
// expandRelation && <React.Fragment>
// <div style={{ flex: 1, height: '100%', overflow: 'hidden' }}>
// <RelationContainer reference={reference} nodeParams={nodeParams} onChange={onRelationChange} resize={resizeRelation} />
// </div>
// <Separate width={15} />
// </React.Fragment>
}
<div style={{ flex: 1, overflow: 'hidden' }}>
<AssetTable nodeId={nodeId} reference={reference} onCountChange={onAssetCountChange} {...props} />
<AssetTable nodeId={centerId} nodeType={nodeParams.nodeType} reference={reference} onCountChange={onAssetCountChange} onFullScreenChange={onFullScreenChange} {...props} />
</div>
</div>
</div>
<div className='tree-toggle' onClick={treeToggleClick}>
<div className='tree-toggle' onClick={treeToggleClick}>
{ expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div>
<div className='relation-toggle' onClick={relationToggleClick}>
{ expandRelation ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div>
{/* <div className='relation-toggle' onClick={relationToggleClick}>
{ expandRelation ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div> */}
</div>
</div>
)
......
......@@ -5,7 +5,7 @@
height: 100%;
.left {
width: 230px;
flex: 0 0 auto;
border-right: 1px solid #EFEFEF;
overflow: hidden;
}
......@@ -18,7 +18,7 @@
right: 0;
background: #f2f5fc;
position: absolute;
left: 245px;
left: 0px;
top: calc(50% - 40px);
width: 12px;
height: 80px;
......@@ -29,18 +29,24 @@
}
.right {
position: relative;
display: flex;
width: calc(100% - 245px);
flex: 1;
overflow: hidden;
flex-direction: column;
}
.right-fullscreen {
position: static;
}
.relation-toggle {
display: flex;
justify-content: center;
align-items: center;
background: #f2f5fc;
position: absolute;
left: calc(50% + 130px);
left: calc(50% + 7.5px);
top: calc(50% - 40px);
width: 12px;
height: 80px;
......@@ -52,24 +58,20 @@
&.asset-browse-relation-collapse {
.relation-toggle {
left: 245px;
left: 0px;
}
}
}
.asset-browse-tree-collapse {
.left {
width: 0;
width: 0 !important;
}
.tree-toggle {
left: 0;
}
.right {
width: 100%;
}
.relation-toggle {
left: calc(50% + 7.5px);
}
......
......@@ -45,15 +45,31 @@ const AddAssetModel = (props) => {
setConfirmLoading(true);
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
type: 'assetmanage.checkCodeIsExist',
payload: {
params,
data: { elements: newElements }
},
callback: () => {
setConfirmLoading(false);
showMessage("success","新增成功");
onCancel && onCancel(true);
callback: isExist => {
if (isExist === 'true') {
setConfirmLoading(false);
showMessage('warn', '已存在相同的资产目录编号,请重新输入');
} else {
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
payload: {
params,
data: { elements: newElements }
},
callback: () => {
setConfirmLoading(false);
showMessage("success","新增成功");
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
}
},
error: () => {
setConfirmLoading(false);
......@@ -70,7 +86,7 @@ const AddAssetModel = (props) => {
<Modal
forceRender
className='asset-add'
title='新增资产信息'
title='新增资产目录信息'
visible={ visible }
width={ 600 }
onCancel={() => { onCancel && onCancel(); } }
......
.asset-action {
.yy-table-tbody > tr > td {
padding: 8px 8px !important;
}
}
\ No newline at end of file
import { useState } from "react";
import { Modal, Radio, Popover } from "antd";
import { QuestionCircleOutlined } from '@ant-design/icons';
const AssetDeleteModal = (props) => {
const { visible, onCancel, onDelete, onDeleteAll } = props;
const [ value, setValue ] = useState(1);
const onChange = (e) => {
setValue(e.target.value);
};
return (
<Modal
title="删除资产目录"
visible={visible}
width={530}
onCancel={
() => {
onCancel && onCancel();
}
}
onOk={
() => {
if (value === 1) {
onDelete && onDelete();
} else {
onDeleteAll && onDeleteAll();
}
}
}
>
<Radio.Group onChange={onChange} value={value}>
<Radio value={1}>
<span>删除本目录下的资产目录</span>
&nbsp;
<Popover content={
<span>
当要删除的资产目录,只存在一个目录时,删除该资产目录并且设置为未分类资产目录;<br/>
存在多个目录时,只删除该目录下的这项资产目录,不会设置为未分类资产目录
</span>
}
>
<QuestionCircleOutlined className='pointer' />
</Popover>
</Radio>
<Radio value={2}>
<span>删除该资产目录(所有目录下的资产目录均删除)</span>
&nbsp;
<Popover content='该资产目录所在的目录内,均删除该项资产目录,并设置为未分类资产目录' >
<QuestionCircleOutlined className='pointer' />
</Popover>
</Radio>
</Radio.Group>
</Modal>
);
}
export default AssetDeleteModal;
\ No newline at end of file
......@@ -4,33 +4,70 @@ import { Spin, Descriptions, Divider } from "antd";
import MetadataInfo from './MetadataInfo';
import { highlightSearchContentByTerms } from '../../../../util';
import { dispatch } from '../../../../model';
import StandardName from './StandardName';
import IndexCode from './IndexCode';
// import SelectUser from '../../Model/Component/SelectUsers';
const AssetDetail = (props)=>{
const { id, dirId, terms } = props;
const { id, dirId, terms, reference = '' } = props;
const [ asset, setAsset ] = useState('');
const [ types, setTypes ] = useState([]);
const [ loading, setLoading ] = useState(false);
const [ users, setUsers ] = useState([]);
useEffect(() => {
if ((id||'') !== '') {
getAssetThenGetAssetName();
// getUserElements();
getUsers();
getAsset([]);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ id ])
const getAssetThenGetAssetName = () => {
const getUserElements = () => {
setLoading(true);
dispatch({
type: 'assetmanage.listUserElements',
callback: data => {
getAsset(data||[]);
},
error: () => {
setLoading(false);
}
})
}
const getUsers = () => {
dispatch({
type: 'pds.getOwners',
callback: (data) => {
setUsers(data);
}
})
}
const getAsset = (userElements) => {
setLoading(true);
dispatch({
type: 'assetmanage.getDataAssetDetail',
payload: {
dataAssetId: id,
dirId: dirId||''
dirId: dirId||'',
checkPermission: true
},
callback: data => {
setLoading(false);
// const userElementIds = [];
// (userElements||[]).forEach(element => {
// userElementIds.push(element?.id);
// })
// const filterElements = (data?.elements||[]).filter(element => userElementIds.indexOf(element?.id) !== -1);
// data = { ...data, elements: filterElements }
setAsset(data);
const _types = [];
......@@ -48,6 +85,26 @@ const AssetDetail = (props)=>{
})
}
const elementItemComponent = (item) => {
if (item.name === '资产项') {
return <MetadataInfo config={false} value={item.value||''} terms={terms} />;
}
if (item.name === '指标标准编码') {
return <IndexCode value={item.value||''} terms={terms} />;
}
// if (item.name==='数据关键用户' || item.name==='业务部门负责人' || item.name?.toLowerCase()==='it责任人' || item.name==='创建人' || item.name==='更新人') {
// return <SelectUser
// type='detail'
// users={users}
// value={item.value||''}
// />
// }
return <span className='text-color'>{highlightSearchContentByTerms(item.value||'', terms)}</span>;
}
return(
<Spin spinning={loading}>
{
......@@ -67,7 +124,7 @@ const AssetDetail = (props)=>{
return (
<div key={index}>
<div className='flex' style={{ alignItems: 'center', padding: '15px 0' }}>
<div className='flex' style={{ alignItems: 'center', padding: (reference==='full-search'&&index===0)?'0 0 15px':'15px 0' }}>
<div style={{ width: 3, height: 14, backgroundColor: '#0069AC', marginRight: 5 }} />
<span style={{ fontWeight: 'bold', color: '#464646' }}>{type||''}</span>
</div>
......@@ -77,15 +134,7 @@ const AssetDetail = (props)=>{
(_currentValues||[]).map((item, index) => {
return (
<Descriptions.Item label={item.name||''} key={index}>
{
item.name==='资产项' && <MetadataInfo config={false} value={item.value||''} terms={terms} />
}
{
item.name==='关联标准' && <StandardName value={item.value||''} terms={terms} />
}
{
item.name!=='资产项' && item.name!=='关联标准' && <span className='text-color'>{highlightSearchContentByTerms(item.value||'', terms)}</span>
}
{ elementItemComponent(item) }
</Descriptions.Item>
);
})
......
......@@ -4,24 +4,26 @@ import { Drawer, Form } from 'antd';
import AssetAction from './AssetAction';
const AssetDetailDrawer = (props) => {
const { onCancel, visible, id, dirId } = props;
const { onCancel, visible, id, dirId, reference } = props;
const [ form ] = Form.useForm();
return (
<Drawer
forceRender
visible={ visible }
title='资产详情'
width={600}
title='资产目录详情'
width='80%'
placement="right"
closable={ true }
destroyOnClose
onClose={() => {
onCancel && onCancel();
}}
>
{
visible && <AssetAction form={form} id={id} dirId={dirId} action='detail' readOnly />
visible && <AssetAction reference={reference} form={form} id={id} dirId={dirId} action='detail' onChange={() => {
onCancel?.(true);
}} />
}
</Drawer>
)
......
import React, { useEffect, useState } from "react";
import LocalStorage from 'local-storage';
import { Spin } from "antd";
import AssetDetail from './AssetDetail';
import AssetAction from "./AssetAction";
import { getQueryParam } from '../../../../util';
import { dispatch } from '../../../../model';
import './AssetDetailPage.less';
import { getDataAssetDetail } from "../../../../service/dataassetmanager";
import { appId } from "../../../../App";
const AssetDetailPage = (props)=>{
const [ data, setData ] = useState({ id: '', dirId: '' });
const [loading, setLoading] = useState(false)
const [templateType, setTemplateType] = useState()
const { id, dirId } = data;
useEffect(() => {
......@@ -16,20 +22,48 @@ const AssetDetailPage = (props)=>{
const _id = getQueryParam('id', props.location.search);
const _dirId = getQueryParam('dirId', props.location.search);
setData({ id: _id, dirId: _dirId });
getDataAssetDetail()
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const getDataAssetDetail = () => {
const _id = getQueryParam('id', props.location.search);
const _dirId = getQueryParam('dirId', props.location.search);
dispatch({
type: 'assetmanage.getDataAssetDetail',
payload: {
dataAssetId: _id,
dirId: _dirId||'',
checkPermission: true
},
callback: data => {
setLoading(false);
LocalStorage.set(`templateType-${appId}`, data?.templateType)
setTemplateType(data?.templateType)
},
error: () => {
setLoading(false);
}
})
}
return(
<div className='asset-detail position-relative'>
<div className='detail-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>资产详情</span>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#000' }}>资产目录详情</span>
</div>
<div className='detail-container'>
<div className='detail-container-card'>
<AssetDetail id={id} dirId={dirId} />
<Spin spinning={loading}>
<div className='detail-container'>
<div className='detail-container-card'>
{
templateType && <AssetAction id={id} dirId={dirId} action='detail' />
}
</div>
</div>
</div>
</Spin>
</div>
)
}
......
......@@ -4,7 +4,7 @@
width: 100%;
height: 44px;
padding: 0 15px;
background-color: #464d6e;
background-color: white;
align-items: center;
position: fixed;
justify-content: space-between;
......
import React, { useEffect, useState } from 'react';
import { Spin, Tooltip, Typography, Dropdown, Menu } from 'antd';
import LocalStorage from 'local-storage';
import { dispatch } from '../../../../model';
import { AssetManageReference, ResourceBrowseReference } from '../../../../util/constant';
import { AssetBrowseReference, AssetManageReference, ResourceBrowseReference } from '../../../../util/constant';
import ImportElement from './ImportElement';
import ExportElement from './ExportElement';
import AttributeRelationModal from "./AttributeRelationModal";
import FilterElementModal from './FilterElementModal';
import { showNotifaction } from '../../../../util';
......@@ -14,15 +12,15 @@ import Separate from './Separate';
import record from '../Assets/record.png';
import './AssetDirectory.less';
import { getTemplateType } from '../../../../util/axios';
const { Paragraph, Text } = Typography;
const AssetDirectory = (props) => {
const { id, directoryChanged, assetCount, reference = AssetManageReference, onElementsChange } = props;
const { id, directoryChanged, assetCount, reference = AssetManageReference, onElementsChange, nodeType } = props;
const [ dir, setDir ] = useState(null);
const [ loading, setLoading ] = useState(false);
const [ importElementVisible, setImportElementVisible ] = useState(false);
const [ exportElementVisible, setExportElementVisible ] = useState(false);
const [ attributeRelationModalVisible, setAttributeRelationModalVisible ] = useState(false);
const [ filterElementVisible, setFilterElementVisible ] = useState(false);
const [ resourceState, setResourceState ] = useState(null);
......@@ -47,14 +45,21 @@ const AssetDirectory = (props) => {
useEffect(() => {
if ((id||'')!=='') {
getDirectory();
} else {
setDir()
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ id, directoryChanged ])
const getDirectory = () => {
let url = 'assetmanage.getDirectoryById';
if ((reference===AssetBrowseReference|| reference===ResourceBrowseReference) && nodeType==='custom') {
url = 'assetmanage.getPersonalCustomDirectoryById';
}
setLoading(true);
dispatch({
type: 'assetmanage.getDirectoryById',
type: url,
payload: {
dirId: id
},
......@@ -93,7 +98,7 @@ const AssetDirectory = (props) => {
}
const onExportElementBtnClick = () => {
setExportElementVisible(true);
window.open(`/api/dataassetmanager/elementApi/export?templateType=${getTemplateType()}`);
}
const onFilterElementClick = () => {
......@@ -116,10 +121,6 @@ const AssetDirectory = (props) => {
}
}
const onExportElementCancel = () => {
setExportElementVisible(false);
}
const onFilterElementModalCancel = () => {
setFilterElementVisible(false);
}
......@@ -132,22 +133,22 @@ const AssetDirectory = (props) => {
<Menu>
<Menu.Item>
<div className='text-center' onClick={onImportElementBtnClick}>
导入资产属性
导入资产目录属性
</div>
</Menu.Item>
<Menu.Item>
<div className='text-center' onClick={onExportElementBtnClick}>
导出资产属性
导出资产目录属性
</div>
</Menu.Item>
<Menu.Item>
<div className='text-center' onClick={onFilterElementClick}>
资产属性管理
资产目录浏览管理
</div>
</Menu.Item>
<Menu.Item>
<div className='text-center' onClick={onAttributeRelationBtnClick}>
资产属性关联
资产目录属性关联
</div>
</Menu.Item>
</Menu>
......@@ -185,7 +186,7 @@ const AssetDirectory = (props) => {
</Tooltip>
</Paragraph>
</div>
<div className={(reference===AssetManageReference || reference===ResourceBrowseReference)?'mb-common':''}>
<div className={(reference===AssetManageReference)?'mb-common':''}>
<Paragraph>
<Tooltip title={dir?.desc||''}>
<Text className='title-color' ellipsis={true}>
......@@ -196,50 +197,46 @@ const AssetDirectory = (props) => {
</Paragraph>
</div>
{
(reference===AssetManageReference || reference===ResourceBrowseReference) && <div className='flex'>
{
(reference === AssetManageReference) && <React.Fragment>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={dir?.remarks||''}>
<Text className='title-color' ellipsis={true}>
备注:
<Text className='text-color'>{dir?.remarks||''}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
</React.Fragment>
}
{
(reference===ResourceBrowseReference || resourceState) && <React.Fragment>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.relatedAsset}>
<Text className='title-color' ellipsis={true}>
资产化数量:
<Text className='text-color'>{resourceState?.relatedAsset}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.notRelatedAsset}>
<Text className='title-color' ellipsis={true}>
非资产数量:
<Text className='text-color'>{resourceState?.notRelatedAsset}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.uncombed}>
<Text className='title-color' ellipsis={true}>
未梳理数量:
<Text className='text-color'>{resourceState?.uncombed}</Text>
</Text>
</Tooltip>
</Paragraph>
</React.Fragment>
}
(reference===AssetManageReference) && <div className='flex'>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={dir?.remarks||''}>
<Text className='title-color' ellipsis={true}>
备注:
<Text className='text-color'>{dir?.remarks||''}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
{/* {
resourceState && <React.Fragment>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.relatedAsset}>
<Text className='title-color' ellipsis={true}>
资产化数量:
<Text className='text-color'>{resourceState?.relatedAsset}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.notRelatedAsset}>
<Text className='title-color' ellipsis={true}>
非资产数量:
<Text className='text-color'>{resourceState?.notRelatedAsset}</Text>
</Text>
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.uncombed}>
<Text className='title-color' ellipsis={true}>
未梳理数量:
<Text className='text-color'>{resourceState?.uncombed}</Text>
</Text>
</Tooltip>
</Paragraph>
</React.Fragment>
} */}
</div>
}
</div>
......@@ -266,7 +263,7 @@ const AssetDirectory = (props) => {
</div>
</div>
{
{/* {
(reference===AssetManageReference) && <Dropdown overlay={elementManageMenu} placement="bottomCenter">
<div
className='flex more-container'
......@@ -281,7 +278,7 @@ const AssetDirectory = (props) => {
<span style={{ fontSize: 10 }}>更多</span>
</div>
</Dropdown>
}
} */}
</div>
......@@ -290,11 +287,6 @@ const AssetDirectory = (props) => {
onCancel={onImportElementCancel}
/>
<ExportElement
visible={exportElementVisible}
onCancel={onExportElementCancel}
/>
<FilterElementModal
visible={ filterElementVisible }
onCancel={ onFilterElementModalCancel}
......
......@@ -2,20 +2,20 @@
.asset-directory {
// .more-container {
// cursor: pointer;
// color: @icon-normal-color;
.more-container {
cursor: pointer;
color: @icon-normal-color;
// .icon {
// fill: @icon-normal-color !important;
// }
.icon {
fill: @icon-normal-color !important;
}
// &:hover {
// color: @icon-hover-color;
&:hover {
color: @icon-hover-color;
// .icon {
// fill: @icon-hover-color !important;
// }
// }
// }
.icon {
fill: @icon-hover-color !important;
}
}
}
}
\ No newline at end of file
@import '../../../../variables.less';
.asset-manage-tree {
.yy-card-head-title {
padding: 0;
}
.yy-tree{
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 57px - 62px) !important;
overflow: auto !important;
}
// .root {
// display: flex;
// position: relative;
// width: 100%;
// background-color: #e7f2ff;
// margin-bottom: 3px;
// padding: 5px;
// align-items: center;
// .yy-tree-switcher {
// display: block;
// position: absolute;
// opacity: 0 !important;
// left: 0;
// top: 0;
// width: 100%;
// height: 100%;
// }
// .yy-tree-node-content-wrapper {
// margin-left: 20px;
// }
// }
// .yy-tree-indent .yy-tree-indent-unit:first-child {
// opacity: 0 !important;
// }
.site-tree-search-value {
color: #f50;
}
}
.asset-manage-tree-read-only {
.yy-tree {
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 62px) !important;;
overflow: auto !important;
}
}
.asset-manage-tree-asset-mount-reference {
.yy-tree {
height: 400px !important;
overflow: auto !important;
}
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { Modal, Form, Input, Space, Button, Select } from 'antd';
import { dispatch } from '../../../../model';
import { AppContext } from '../../../../App';
import MetadataInfo from './MetadataInfo';
import { showMessage } from '../../../../util';
const { Option } = Select;
......@@ -48,7 +49,7 @@ const DirsSelect = ({ value = [], data = [], onChange, ...restProps }) => {
const AssetTagModal = (props) => {
const { onCancel, visible, id, keyword, creator } = props;
const { onCancel, visible, id, tag, creator } = props;
const [ form ] = Form.useForm();
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ elements, setElements ] = useState([]);
......@@ -69,12 +70,12 @@ const AssetTagModal = (props) => {
useEffect(() => {
if (visible && (keyword||'')!=='') {
getDirectoryByName(keyword);
if (visible && (tag?.name||'')!=='') {
getDirectoryByName(tag?.name);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ visible, keyword ])
}, [ visible ])
const getAsset = () => {
dispatch({
......@@ -140,7 +141,7 @@ const AssetTagModal = (props) => {
});
const params = {
dirId: row.tags.join(','),
dirId: (row.dirs||[]).join(','),
}
if ((metadataId||'')!=='') {
......@@ -150,50 +151,51 @@ const AssetTagModal = (props) => {
setConfirmLoading(true);
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
type: 'assetmanage.checkCodeIsExist',
payload: {
params,
data: { ...asset, elements: newElements }
data: { ...asset, elements: newElements }
},
callback: () => {
const nameList = [];
(dirs||[]).forEach(dir=>{
if (row.tags.indexOf(dir.id) !== -1) {
nameList.push(dir.path);
}
})
dispatch({
type: 'tag.getTagIdListByNameList',
payload: {
params: {
nameList: nameList.join(',')
}
},
callback: data => {
dispatch({
type: 'tag.batchAddTagResourceByTagList',
payload: {
params: {
tagIds: (data||[]).join(','),
resourceIds: id,
type: 'dataAsset',
creator
}
},
callback: () => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
callback: isExist => {
if (isExist === 'true') {
setConfirmLoading(false);
showMessage('warn', '已存在相同的资产目录编号,请重新输入');
} else {
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
payload: {
params,
data: { ...asset, elements: newElements }
},
callback: data => {
const ids = [id];
if (data && ((data?.id||'')!=='') && (data?.id!==id)) {
ids.push(data?.id);
}
})
},
error: () => {
setConfirmLoading(false);
}
})
dispatch({
type: 'tag.batchAddTagResourceByTagList',
payload: {
params: {
tagIds: tag?.tagId,
resourceIds: ids.join(','),
type: 'dataAsset',
creator
}
},
callback: () => {
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
},
error: () => {
setConfirmLoading(false);
}
})
}
},
error: () => {
setConfirmLoading(false);
......@@ -226,7 +228,7 @@ const AssetTagModal = (props) => {
<Modal
forceRender
className='asset-add'
title='资产打标签'
title='资产目录打标签'
visible={ visible }
width={ 600 }
onCancel={() => {
......@@ -259,8 +261,14 @@ const AssetTagModal = (props) => {
return (
<>
<Form.Item
label='资产标签'
name='tags'
label='资产目录标签'
name='tag'
>
<span>{tag?.name||''}</span>
</Form.Item>
<Form.Item
label='挂载目录'
name='dirs'
required={true}
>
<DirsSelect data={dirs||[]} />
......
......@@ -7,25 +7,11 @@
}
.yy-tree{
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 40px - 62px) !important;
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 57px - 62px) !important;
overflow: auto !important;
}
.site-tree-search-value {
color: #f50;
}
}
.asset-tree-read-only {
.yy-tree {
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 62px) !important;;
overflow: auto !important;
}
}
.asset-tree-asset-mount-reference {
.yy-tree {
height: 400px !important;
overflow: auto !important;
}
}
\ No newline at end of file
......@@ -389,7 +389,7 @@ const AttributeRelationModal = (props) => {
return (
<Modal
forceRender
title={'资产属性关联'}
title={'资产目录属性关联'}
visible={visible}
width={1000}
onCancel={() => {
......
import React, { useEffect, useState, useMemo } from 'react';
import { Modal, Checkbox, Row, Input, Col, Space, Button } from 'antd';
import { dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
import { AssetActionSubject } from './AssetAction';
const FC = (props) => {
const { visible, onCancel } = props;
const [keyword, setKeyword] = useState('');
const [elements, setElements] = useState([]);
const [checkedValues, setCheckedValues] = useState([]);
const [confirmLoading, setConfirmLoading] = useState(false);
useEffect(() => {
if (visible) {
getComputableElements();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const filterElements = useMemo(() => {
return elements?.filter(item => (item.name||'').indexOf(keyword)!==-1);
}, [keyword, elements])
const getComputableElements = () => {
dispatch({
type: 'assetmanage.listComputableElements',
callback: data => {
setElements(data);
}
})
}
const onSearchChange = (e) => {
setKeyword(e.target.value);
}
const onCheckboxChange = (newValues) => {
setCheckedValues(newValues);
}
const reset = () => {
setConfirmLoading(false);
setKeyword('');
setCheckedValues([]);
setElements([]);
}
const onOk = () => {
if ((checkedValues||[]).length === 0) {
showMessage('info', '请先选择要素');
return;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.computeBySelectElements',
payload: {
data: checkedValues
},
callback: data => {
setConfirmLoading(false);
reset();
AssetActionSubject.next({type: 'update'})
onCancel?.();
},
error: () => {
setConfirmLoading(false);
}
})
}
return (
<Modal
forceRender
title='资产价值评分'
visible={visible}
width={800}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel?.();
}}
footer={
<Space>
<Button onClick={() => onCancel?.() }>取消</Button>
<Button type="primary" onClick={ onOk } loading={ confirmLoading }>计算</Button>
</Space>
}
>
<div>
<div className='mb-3'>资产要素</div>
<Input
value={keyword}
allowClear
placeholder='请输入资产要素'
style={{ marginBottom:10 }}
onChange={(e) => { onSearchChange(e) }}
/>
<div style={{ maxHeight: 500, overflow: 'auto' }}>
<Checkbox.Group value={checkedValues} onChange={onCheckboxChange} style={{ width: '100%' }}>
<Row>
{
elements?.map((item, index) => {
const exsit = filterElements?.some(filterItem => filterItem.id === item.id);
return (
<Col key={index} span={6} style={{ display: exsit?'':'none' }}>
<Checkbox value={item.id||''}>{item.name||''}</Checkbox>
</Col>
);
})
}
</Row>
</Checkbox.Group>
</div>
</div>
</Modal>
);
}
export default FC;
\ No newline at end of file
import React, { useEffect, useState, useCallback } from 'react';
import { Modal, Checkbox, Row, Col, Form, Input, Empty } from 'antd';
import { Modal, Checkbox, Row, Col, Form, Input, Empty, Select } from 'antd';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
......@@ -7,11 +7,18 @@ import update from 'immutability-helper';
import DragTag from './DragTag';
import PreviewTree from './PreviewTree';
import { dispatch, dispatchLatest } from '../../../../model';
import { AssetManageReference, AssetBrowseReference, ResourceBrowseReference } from '../../../../util/constant';
import { showMessage } from '../../../../util';
const resourceTypes = [
{ key: 'innerSource', name: '内部资源' },
{ key: 'outerSource', name: '外部资源' },
{ key: 'dataAsset', name: '资产' },
]
const CustomDirectoryModal = (props) => {
const { visible, onCancel, action, dirId } = props;
const { visible, onCancel, action, dirId, reference = AssetManageReference } = props;
const [ keyword, setKeyword ] = useState('');
const [ data, setData ] = useState([]);
......@@ -67,11 +74,33 @@ const CustomDirectoryModal = (props) => {
}
const getPreviewTreeData = () => {
if ((checkedValues||[]).length === 0) {
return;
}
let url = 'assetmanage.previewTreeByCustomElements';
let payload = {
data: checkedValues
}
if (reference===AssetManageReference) {
payload.params = {
resourceTypes: (form.getFieldValue('resourceTypes')||[]).join(',')
};
}
if (reference===AssetBrowseReference || reference===ResourceBrowseReference) {
url = 'assetmanage.previewTreeByCustomElementsAndResourceType';
payload.params = {
resourceType: (reference===ResourceBrowseReference)?'resource':'dataAsset',
}
}
dispatchLatest({
type: 'assetmanage.previewTreeByCustomElements',
payload: {
data: checkedValues
},
type: url,
payload,
callback: data => {
setPreviewTreeData((data||[]).length>0?data[0]:{});
}
......@@ -98,6 +127,12 @@ const CustomDirectoryModal = (props) => {
setCheckedData(_checkedData);
}
const onValuesChange = (changedValues, allValues) => {
if (changedValues.hasOwnProperty('resourceTypes')) {
getPreviewTreeData();
}
}
const reset = () => {
setConfirmLoading(false);
setKeyword('');
......@@ -127,8 +162,23 @@ const CustomDirectoryModal = (props) => {
payload.params = {...payload.params, dirId};
}
let url = 'assetmanage.saveTreeByCustomElements';
if (reference===AssetManageReference) {
payload.params = {
...payload.params,
resourceTypes: (row.resourceTypes||[]).join(',')
}
}
if (reference===AssetBrowseReference || reference===ResourceBrowseReference) {
url = 'assetmanage.saveTreeByCustomElementsAndResourceType';
payload.params = { ...payload.params, resourceType: (reference===ResourceBrowseReference)?'resource':'dataAsset' };
}
dispatch({
type: 'assetmanage.saveTreeByCustomElements',
type: url,
payload,
callback: data => {
setConfirmLoading(false);
......@@ -194,6 +244,7 @@ const CustomDirectoryModal = (props) => {
title={action==='add'?'新增自定义':'修改自定义'}
visible={visible}
width={800}
centered
confirmLoading={confirmLoading}
onCancel={() => {
reset();
......@@ -203,10 +254,10 @@ const CustomDirectoryModal = (props) => {
>
<Row gutter={30}>
<Col span={8} style={{ borderRight: '1px solid #EFEFEF' }}>
<div className='mb-3'>资产要素</div>
<div className='mb-3'>资产目录要素</div>
<Input
value={keyword}
placeholder='请输入资产要素'
placeholder='请输入资产目录要素'
style={{ marginBottom:10 }}
onChange={(e) => { onSearchChange(e) }}
/>
......@@ -244,7 +295,7 @@ const CustomDirectoryModal = (props) => {
</div>
</>
}
<Form className='mt-5' {...formItemLayout} form={form}>
<Form className='mt-5' {...formItemLayout} form={form} onValuesChange={onValuesChange}>
<Form.Item
label='命名'
name='name'
......@@ -252,6 +303,20 @@ const CustomDirectoryModal = (props) => {
>
<Input />
</Form.Item>
{
reference===AssetManageReference && <Form.Item
label='数据范围'
name='resourceTypes'
>
<Select mode="multiple" allowClear>
{
resourceTypes.map((item,index) => {
return <Select.Option key={item.key}>{item.name}</Select.Option>
})
}
</Select>
</Form.Item>
}
<Form.Item
label='描述或原因'
name='desc'
......
import React, { useEffect, useState } from 'react';
import { Button, Upload, Modal, Select, Space, Spin } from 'antd';
import { DownloadOutlined, UploadOutlined } from '@ant-design/icons';
import LocalStorage from 'local-storage';
import { dispatchLatest, dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
const FC = (props) => {
const { onCancel, visible } = props;
const [ resourceTypes, setResourceTypes ] = useState(undefined);
const [ currentResourceType, setCurrentResourceType ] = useState(undefined);
const [ loading, setLoading ] = useState(false);
useEffect(() => {
if (visible) {
getResourceTypes();
}
}, [visible])
const getResourceTypes = () => {
setLoading(true);
dispatch({
type: 'assetmanage.getResourceTypes',
callback: data => {
setLoading(false);
setResourceTypes(data);
if ((data||[]).length > 0) {
setCurrentResourceType(data[0].code);
}
},
error: () => {
setLoading(false);
}
})
}
const handleOk = () => {
if (!currentResourceType) {
showMessage('info', '请先选择资产类型');
return;
}
const env = LocalStorage.get('assetsEnv');
window.open(`/api/dataassetmanager/elementApi/export?env=${env}&resourceType=${currentResourceType}`);
reset();
onCancel();
}
const reset = () => {
setLoading(false);
}
return (
<Modal
forceRender
visible={visible}
title='资产属性导出'
width={520}
onCancel={() => {
reset();
onCancel && onCancel();
}}
onOk={handleOk}
>
<Spin spinning={loading} >
<div className='mb-3 flex'>
<Select
value={currentResourceType}
placeholder='请选择资产类型'
onChange={(val) => {
setCurrentResourceType(val);
}}
style={{ width: 200 }}
>
{
resourceTypes?.map((item,index) => {
return <Select.Option key={item.code}>{item.name}</Select.Option>
})
}
</Select>
</div>
</Spin>
</Modal>
)
}
export default FC;
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { Row, Col, Checkbox, Typography, Button, Switch, Modal } from 'antd';
import { dispatch } from '../../../../model';
import './FilterElementModal.less';
import { AssetManageReference } from '../../../../util/constant';
//type
//global 全局设置
......@@ -11,7 +12,7 @@ import './FilterElementModal.less';
const FilterElementModal = (props) => {
const { visible, onCancel, type = 'global' } = props;
const { visible, onCancel, type = 'global', reference = AssetManageReference } = props;
const [ elements, setElements ] = useState([]);
const [ typesOfElements, setTypesOfElements ] = useState([]);
const [ selectedKeys, setSelectedKeys ] = useState([]);
......@@ -28,14 +29,19 @@ const FilterElementModal = (props) => {
const getAllFilterElementIdsThenGetAllElements = () => {
let url = '';
let payload = null;
if (type === 'global') {
url = 'assetmanage.listFilterElementIdsConfig';
} else {
url = 'assetmanage.listFilterElementIds';
payload = {
isAdmin: (reference===AssetManageReference)
}
}
dispatch({
type: url,
payload,
callback: data => {
setSelectedKeys(data||[]);
getAllElements();
......@@ -123,7 +129,10 @@ const FilterElementModal = (props) => {
dispatch({
type: url,
payload: {
data: selectedKeys
data: selectedKeys,
params: {
isAdmin: (reference===AssetManageReference)
}
},
callback: () => {
reset();
......@@ -148,7 +157,7 @@ const FilterElementModal = (props) => {
<Modal
forceRender
visible={visible}
title={type==='global'?'资产属性管理':'可见列设置'}
title={type==='global'?'资产目录浏览管理':'可见列设置'}
width={520}
onCancel={cancel}
footer={[
......
import React, { useState, useEffect } from 'react';
import { Button, Upload, Drawer, Table, Pagination, Divider, Form, Select } from 'antd';
import React, { useState, useEffect, useContext } from 'react';
import { Button, Upload, Drawer, Table, Pagination, Divider, Form, Typography } from 'antd';
import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
import LocalStorage from 'local-storage';
import { dispatch } from '../../../../model';
import { showMessage, formatDate } from '../../../../util';
import { AppContext } from '../../../../App';
import { getTemplateType } from '../../../../util/axios';
const ImportAssetDrawer = (props) => {
const { onCancel, onSuccess, visible, nodeId } = props;
......@@ -16,8 +17,9 @@ const ImportAssetDrawer = (props) => {
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const { pageNum, pageSize } = pagination;
const [ total, setTotal ] = useState(0);
const [ resourceTypes, setResourceTypes ] = useState(undefined);
const [ currentResourceType, setCurrentResourceType ] = useState(undefined);
const [ users, setUsers ] = useState([]);
const app = useContext(AppContext);
const columns = [
{
......@@ -60,6 +62,14 @@ const ImportAssetDrawer = (props) => {
dataIndex: 'operator',
width: 100,
ellipsis: true,
render: (text, record) => {
const user = users?.filter((user)=>user.pernr===text);
if (user && user.length > 0) {
return user[0].nachn?`${user[0].nachn}(${user[0].pernr})`:user[0].pernr;
}
return text;
}
},
{
title: '导入状态',
......@@ -72,41 +82,15 @@ const ImportAssetDrawer = (props) => {
if (visible) {
setPagination({ pageNum: 1, pageSize: 20 });
getUsers();
getLogs();
getResourceTypes();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const getResourceTypes = () => {
dispatch({
type: 'assetmanage.getResourceTypes',
callback: data => {
setResourceTypes(data);
if ((data||[]).length > 0) {
setCurrentResourceType(data[0].code);
}
}
})
}
const downloadTemplate = () => {
if (!currentResourceType) {
showMessage('warn', '请先选择资产类型');
return;
}
const env = LocalStorage.get('assetsEnv');
dispatch({
type: 'assetmanage.getDirectoryById',
payload: {
dirId: nodeId
},
callback: data => {
window.open(`/api/dataassetmanager/dataAssetApi/getImportTemplate?env=${env}&resourceType=${currentResourceType}`);
}
})
window.open(`/api/dataassetmanager/dataAssetApi/getImportTemplate?templateType=${getTemplateType()}`);
}
const getLogs = (p = 1, s = 20) => {
......@@ -128,6 +112,15 @@ const ImportAssetDrawer = (props) => {
})
}
const getUsers = () => {
dispatch({
type: 'pds.getOwners',
callback: (data) => {
setUsers(data);
}
})
}
const uploadProps = {
onRemove: file => {
......@@ -154,30 +147,27 @@ const ImportAssetDrawer = (props) => {
};
const handleOk = () => {
if (!currentResourceType) {
showMessage('warn', '请先选择资产类型');
return;
}
if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模上传');
showMessage('info', '请先选择模上传');
return;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.assetImport',
payload: { fileList: fileList, params: { dirId: nodeId, resourceType: currentResourceType } },
payload: { fileList: fileList, params: { env: `${app?.env?.domainId}` } },
callback: data => {
setConfirmLoading(false);
setFileList([]);
getLogs(pageNum, pageSize);
getLogs(pageNum, pageSize);
showMessage('success', '导入动作完成,详情查看日志');
onSuccess && onSuccess(data||'');
},
error: () => {
setConfirmLoading(false);
}
});
});
}
const reset = () => {
......@@ -189,7 +179,7 @@ const ImportAssetDrawer = (props) => {
<Drawer
forceRender
visible={ visible }
title='资产导入'
title='资产目录导入'
width={900}
placement="right"
closable={ true }
......@@ -198,11 +188,11 @@ const ImportAssetDrawer = (props) => {
onCancel && onCancel();
}}
>
<div className='mt-3 flex' style={{ justifyContent: 'space-between' }}>
<div className='mt-3'>
<Form layout='inline'>
<Form.Item label='Word上传:'>
<Form.Item label='Excel导入:'>
<Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }>
下载
下载
</Button>
<Upload style={{ display: 'inline' }} {...uploadProps }>
<Button icon={
......@@ -214,21 +204,8 @@ const ImportAssetDrawer = (props) => {
<Form.Item>
<Button type='primary' onClick={handleOk} loading={confirmLoading}>确定导入</Button>
</Form.Item>
<Button onClick={() => getLogs()} style={{ marginLeft: 'auto' }}>刷新日志</Button>
</Form>
<Select
value={currentResourceType}
placeholder='请选择资产类型'
onChange={(val) => {
setCurrentResourceType(val);
}}
style={{ width: 200 }}
>
{
resourceTypes?.map((item,index) => {
return <Select.Option key={item.code}>{item.name}</Select.Option>
})
}
</Select>
</div>
<Divider orientation="left">导入日志</Divider>
<Table
......@@ -239,7 +216,11 @@ const ImportAssetDrawer = (props) => {
pagination={false}
loading={loading}
expandable={{
expandedRowRender: record => <p style={{ margin: 0 }}>{record.message||''}</p>
expandedRowRender: record => <React.Fragment>
{record.message?.split('<br/>').map((info, index) => {
return <Typography.Paragraph key={index}>{info}</Typography.Paragraph>
})}
</React.Fragment>
}}
sticky
/>
......
......@@ -4,6 +4,7 @@ import { UploadOutlined } from '@ant-design/icons';
import { dispatch } from '../../../../model';
import { showNotifaction } from '../../../../util';
import { getTemplateType } from '../../../../util/axios';
const ImportDirectory = (props) => {
const { visible, onCancel, dirId } = props;
......@@ -41,7 +42,7 @@ const ImportDirectory = (props) => {
}
const download = () => {
window.open("/data-govern/docs/AssetThemeModel.xlsx");
window.open(`/api/dataassetmanager/directoryApi/getImportTemplate?templateType=${getTemplateType()}`);
}
const upload = async (ignoreRepeatPath = false) => {
......@@ -51,23 +52,12 @@ const ImportDirectory = (props) => {
setUploading(true);
let payload;
if (row.type === 'root') {
payload = {
params: {
ignoreRepeatPath
},
fileList: fileList,
};
} else {
payload = {
params: {
ignoreRepeatPath,
parentPath: dir.path
},
fileList: fileList
};
}
const payload = {
params: {
ignoreRepeatPath
},
fileList: fileList,
};
dispatch({
type: 'assetmanage.directoryImport',
......@@ -143,7 +133,7 @@ const ImportDirectory = (props) => {
onCancel={() => { onCancel && onCancel() }}
footer={
<Space>
<Button type="primary" onClick={ download } >下载</Button>
<Button type="primary" onClick={ download } >下载</Button>
<Button type="primary" onClick={() => { upload(false); } } loading={uploading}>上传</Button>
<Button onClick={() => { onCancel && onCancel() }}>返回</Button>
</Space>
......@@ -152,13 +142,10 @@ const ImportDirectory = (props) => {
<Form {...formItemLayout} form={form}>
<Form.Item
label="挂载位置"
name="type"
// name="type"
rules={[{ required: true, message: '必填项'}]}
>
<Radio.Group >
<Radio value='root' >根节点导入</Radio>
<Radio value='self' disabled={ dirId===null }>选中节点导入</Radio>
</Radio.Group>
<span>根节点导入</span>
</Form.Item>
<Form.Item
label="文件"
......
import React, { useEffect, useState } from 'react';
import { Button, Upload, Modal, Select, Space, Spin } from 'antd';
import React, { useState } from 'react';
import { Button, Upload, Modal } from 'antd';
import { DownloadOutlined, UploadOutlined } from '@ant-design/icons';
import { dispatchLatest, dispatch } from '../../../../model';
import { dispatchLatest } from '../../../../model';
import { showMessage } from '../../../../util';
import { getTemplateType } from '../../../../util/axios';
const ImportElement = (props) => {
const { onCancel, visible } = props;
const [ fileList, setFileList ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ resourceTypes, setResourceTypes ] = useState(undefined);
const [ currentResourceType, setCurrentResourceType ] = useState(undefined);
const [ loading, setLoading ] = useState(false);
useEffect(() => {
if (visible) {
getResourceTypes();
}
}, [visible])
const getResourceTypes = () => {
setLoading(true);
dispatch({
type: 'assetmanage.getResourceTypes',
callback: data => {
setLoading(false);
setResourceTypes(data);
if ((data||[]).length > 0) {
setCurrentResourceType(data[0].code);
}
},
error: () => {
setLoading(false);
}
})
}
const downloadTemplate = () => {
window.open("/data-govern/docs/ElementModel.xlsx");
window.open(`/api/dataassetmanager/elementApi/getImportTemplate?templateType=${getTemplateType()}`);
}
const uploadProps = {
......@@ -61,19 +36,14 @@ const ImportElement = (props) => {
const handleOk = () => {
if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模上传');
showMessage('info', '请先选择模上传');
return;
}
setConfirmLoading(true);
dispatchLatest({
type: 'assetmanage.importElement',
payload: {
params: {
resourceType: currentResourceType
},
fileList
},
payload: { fileList },
callback: data => {
setConfirmLoading(false);
reset();
......@@ -87,15 +57,13 @@ const ImportElement = (props) => {
const reset = () => {
setFileList([]);
setLoading(false);
setConfirmLoading(false);
}
return (
<Modal
forceRender
visible={visible}
title='资产属性导入'
title='资产目录属性导入'
width={520}
confirmLoading={confirmLoading}
onCancel={() => {
......@@ -104,34 +72,18 @@ const ImportElement = (props) => {
}}
onOk={handleOk}
>
<Spin spinning={loading} >
<div className='mb-3 flex'>
<Select
value={currentResourceType}
placeholder='请选择资产类型'
onChange={(val) => {
setCurrentResourceType(val);
}}
style={{ width: 200 }}
>
{
resourceTypes?.map((item,index) => {
return <Select.Option key={item.code}>{item.name}</Select.Option>
})
}
</Select>
</div>
<Space>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模版下载
<div>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模板下载
</Button>
</div>
<div className='mt-3'>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />}>
选择文件上传
</Button>
</Space>
</Spin>
</Upload>
</div>
</Modal>
)
}
......
......@@ -9,7 +9,7 @@ const FC = ({ value = '', terms = [] }) => {
{
appValue => <a onClick={() => {
appValue?.setGlobalState && appValue?.setGlobalState({
message: 'data-govern-show-standard-detail-message',
message: 'data-govern-show-index-detail-message',
data: { name: value }
})
}}>
......@@ -20,4 +20,4 @@ const FC = ({ value = '', terms = [] }) => {
);
}
export default FC;
export default FC;
\ No newline at end of file
......@@ -2,9 +2,10 @@ import React from 'react';
import { Button } from 'antd';
import { SettingFilled } from '@ant-design/icons';
import { highlightSearchContentByTerms } from '../../../../util';
import { AppContext } from '../../../../App';
const MetadataInfo = ({ value = '', config = true }) => {
const MetadataInfo = ({ value = '', config = true, terms = [] }) => {
let metadata = {};
try {
......@@ -18,13 +19,13 @@ const MetadataInfo = ({ value = '', config = true }) => {
{
value => <div className='flex'>
{
(typeof metadata==='string') ? <span style={{ marginRight: 5 }}>{metadata||''}</span> : <div className='flex' style={{ flexDirection: 'column' }}>
(typeof metadata==='string') ? <span style={{ marginRight: 5 }}>{highlightSearchContentByTerms(metadata||'', terms)}</span> : <div className='flex' style={{ flexDirection: 'column' }}>
<a onClick={() => {
value?.setGlobalState && value?.setGlobalState({
message: 'data-govern-show-metadata-message',
data: metadata
})
}} style={{ marginRight: 5, marginTop: config?5:0 }}>{metadata?.tableName||''}
}} style={{ marginRight: 5, marginTop: config?5:0 }}>{highlightSearchContentByTerms(metadata?.tableName||'',terms)}
</a>
{
(metadata?.columnItems||[]).map((item, index) => {
......@@ -36,7 +37,7 @@ const MetadataInfo = ({ value = '', config = true }) => {
_content += item.metadataColumnName||'';
return <span key={index}>{_content}</span>
return <span key={index}>{highlightSearchContentByTerms(_content,terms)}</span>
})
}
</div>
......
import React, { useEffect, useState } from "react";
import { Modal, Button, Space, Tree, Row, Col, Table, Pagination, Select, Input, Divider, Spin } from "antd";
import { getAttributesByMetadataModel } from "../../../../service/dataassetmanager";
import { dispatch } from '../../../../model';
import { showMessage } from "../../../../util";
import useDebounce from "../../../../layout/useDebounce";
const options = [
{ name: '基础数据名称', key: 'name' },
{ name: '基础数据编码', key: 'code' },
]
const FC = (props) => {
const { visible, onCancel, id } = props;
const [treeData, setTreeData] = useState(undefined);
const [tableData, setTableData] = useState(undefined);
const [total, setTotal] = useState(0);
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const [loadingTree, setLoadingTree] = useState(false);
const [loadingTable,setLoadingTable] = useState(false);
const [currentTreeParams, setCurrentTreeParams] = useState({
treeId: undefined,
dataType: undefined,
});
const [queryProperty, setQueryProperty] = useState(options[0].key);
const [keyword, setKeyword] = useState(undefined);
const debouncedKeyword = useDebounce(keyword, 300);
const { pageNum, pageSize } = pagination;
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text, record, index) => {
return (index+1).toString();
},
width: 60,
ellipsis: true,
},
{
title: '基础数据编码',
dataIndex: 'code',
ellipsis: true,
render: (text, record) => {
return <a onClick={() => {
onOk(record._id);
}}>
{text}
</a>
}
},
{
title: '基础数据名称',
dataIndex: 'name',
ellipsis: true,
render: (text, record) => {
return <a onClick={() => {
onOk(record._id);
}}>
{text}
</a>
}
},
];
useEffect(() => {
if (visible) {
getTreeData();
}
}, [visible])
useEffect(() => {
if (visible && currentTreeParams.treeId) {
getTableData();
}
}, [visible, pagination, currentTreeParams, queryProperty, debouncedKeyword])
const getTreeData = () => {
setLoadingTree(true);
dispatch({
type: 'assetmanage.getStandardTree',
payload: {
namePath: 0,
needOrg: true,
parentClass: 'Catalog'
},
callback: data => {
setLoadingTree(false);
const filterData = data?.filter(item => item.name !== '草稿');
if (filterData && filterData.length > 0) {
if (filterData[0].cnName === '指标标准') {
setCurrentTreeParams({ treeId: filterData[0]._id, dataType: 'IndicatorStandard' });
} else if (filterData[0].cnName === '维度标准') {
setCurrentTreeParams({ treeId: filterData[0]._id, dataType: 'DimensionStandard' });
} else {
setCurrentTreeParams({ treeId: filterData[0]._id, dataType: filterData[0].type });
}
}
setTreeData(filterData);
},
error: () => {
setLoadingTree(false);
}
});
}
const getTableData = () => {
setLoadingTable(true);
dispatch({
type: 'assetmanage.getStandardList',
payload: {
params: {
page: pageNum,
size: pageSize,
parentId: currentTreeParams.treeId,
keyword: debouncedKeyword,
queryProperty: queryProperty,
},
data: [`Catalog,StandardCatalog,${currentTreeParams.dataType}`]
},
callback: data => {
setLoadingTable(false);
data.content?.forEach(item => {
item.children = null;
});
setTableData(data.content);
setTotal(data.totalElements);
},
error: () => {
setLoadingTable(false);
}
});
}
const onOk = (standardId) => {
dispatch({
type: 'assetmanage.getMetadataStandardList',
payload: {
data: {
metadataId: id
},
params: {
page: 1,
size: 999999
}
},
callback: data => {
if ((data.content||[]).length === 0) {
dispatch({
type: 'assetmanage.standardBatchMetadata',
payload: {
params: {
metadataIds: id,
standardId
},
},
callback: () => {
showMessage('success', '关联成功');
onCancel?.(true);
}
});
} else {
dispatch({
type: 'assetmanage.deleteStandardBatchMetadata',
payload: {
params: {
ids: data.content?.map(item => item.id).toString()
}
},
callback: () => {
dispatch({
type: 'assetmanage.standardBatchMetadata',
payload: {
params: {
metadataIds: id,
standardId
},
},
callback: () => {
showMessage('success', '关联成功');
onCancel?.(true);
}
});
}
});
}
},
});
}
const onTreeSelect = (keys, {node}) => {
if (keys.length > 0) {
let paths = node.namePath.split(',');
if(paths[1]==='指标标准'){
setCurrentTreeParams({ treeId: keys[0], dataType: 'IndicatorStandard' });
}else if(paths[1]==='维度标准'){
setCurrentTreeParams({ treeId: keys[0], dataType: 'DimensionStandard' });
}else{
setCurrentTreeParams({ treeId: keys[0], dataType: node.type });
}
}
}
const onSelectChange = (value) => {
setQueryProperty(value);
}
const onSearchChange = (e) => {
setKeyword(e.target.value);
}
const changeCurrent = (page,size) => {
setPagination({ pageNum: page, pageSize: size });
}
const reset = () => {
setLoadingTree(false);
setLoadingTable(false);
setCurrentTreeParams({
treeId: undefined,
dataType: undefined,
});
setQueryProperty(options[0].key);
setKeyword(undefined);
setPagination({ pageNum: 1, pageSize: 20 });
}
const loop = (data) =>
data?.map(item => {
if (item.children && item.children.length>0) {
return {...item, title: item.name, key: item._id, children: loop(item.children)};
}
return {...item, title: item.name, key: item._id, children: null};
});
return (
<Modal
title='选择标准'
visible={ visible }
centered
width='90%'
onCancel={() => {
reset();
onCancel?.();
}}
footer={
<Space>
<Button onClick={() => {
reset();
onCancel?.();
}}>取消</Button>
</Space>
}
bodyStyle={{ padding: '24px', height: '70vh', overflow: 'auto' }}
>
<Row>
<Col span={5}>
<Spin spinning={loadingTree}>
<Tree
showLine={true}
showIcon={false}
treeData={loop(treeData)}
onSelect={onTreeSelect}
selectedKeys={currentTreeParams.treeId ? [currentTreeParams.treeId]: undefined}
/>
</Spin>
</Col>
<Col span={1}>
<div style={{ width: 1, height: 'calc(70vh - 48px)', backgroundColor: '#f0f0f0' }} ></div>
</Col>
<Col span={18}>
<div>
<Space>
<Select
value={queryProperty}
onChange={onSelectChange}
style={{ width: 160 }}
>
{
options.map((option, index) => {
return (
<Select.Option key={index} value={option.key} >{option.name}</Select.Option>
)
})
}
</Select>
<Input value={keyword} onChange={onSearchChange} placeholder='请输入关键字' />
</Space>
</div>
<Divider style={{ margin: '10px 0' }} />
<Table
loading={loadingTable}
columns={columns}
dataSource={tableData||[]}
rowKey='_id'
pagination={false}
/>
<Pagination
className="m-3"
size='small'
position={['bottomLeft']}
onChange={changeCurrent}
onShowSizeChange={changeCurrent}
current={pageNum}
pageSize={pageSize}
defaultCurrent={1}
total={total}
showTotal={total => `共 ${total} 条`}
/>
</Col>
</Row>
</Modal>
)
}
export default FC;
\ No newline at end of file
import React, { useState, useContext } from 'react';
import { Modal, Form, Input } from 'antd';
import { dispatchLatest } from '../../../../model';
import { showNotifaction } from '../../../../util';
import { AppContext } from '../../../../App';
const StartFlowModal = (props) => {
const { visible, onCancel, ids } = props;
const [ form ] = Form.useForm();
const [ confirmLoading, setConfirmLoading ] = useState(false);
const app = useContext(AppContext);
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
};
const handleOk = async () => {
try {
const values = await form.validateFields();
setConfirmLoading(true);
dispatchLatest({
type: 'assetmanage.startFlow',
payload: {
params: {
env: app?.env?.domainId,
dataAssetIds: ids.join(','),
applyReason: values?.desc
}
},
callback: data => {
reset();
if (data) {
showNotifaction('申请提示', (data === 'ok')?'资产目录申请成功,请在我的流程》我的申请中查看详情':data, 5);
}
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
}
}
const reset = () => {
setConfirmLoading(false);
form.resetFields();
}
return (
<Modal
forceRender
visible={visible}
title='资产目录申请'
width={520}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel && onCancel();
}}
onOk={handleOk}
>
<Form
{...formItemLayout}
form={form}
>
<Form.Item
label="申请原因"
name="desc"
rules={[{ required: true, message: '请在申请原因栏中描述用途说明以及必要性(资产目录和服务权限申请需要说明请求字段)' }]}
>
<Input.TextArea rows={6} />
</Form.Item>
</Form>
</Modal>
);
}
export default StartFlowModal;
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useMemo } from 'react';
import { Modal, Form, Input, Space, Button, Radio, Select } from 'antd';
import { dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
// const resourceTypes = [
// { key: 'innerSource', name: '内部资源' },
// { key: 'outerSource', name: '外部资源' },
// // { key: 'innerSource', name: '内部资源' },
// // { key: 'outerSource', name: '外部资源' },
// { key: 'dataAsset', name: '资产' },
// { key: 'custom', name: '自定义' },
// ]
const CodeInput = ({ value = '', onChange, restrict = false, action }) => {
const onCodeChange = (e) => {
if (!restrict || (
restrict&&(e.target.value===''||/^[A-Z]+$/.test(e.target.value))
)) {
onChange?.(e.target.value);
}
}
return <Input
placeholder="请输入编号(只允许输入大写英文字母,并且最多3个)"
disabled={action!=='add'}
maxLength={restrict?3:null}
value={value}
onChange={onCodeChange}
/>
}
const UpdateDirectoryModal = (props) => {
const { visible, onCancel, dirId, action } = props;
......@@ -18,7 +38,6 @@ const UpdateDirectoryModal = (props) => {
const [ dir, setDir ] = useState(null);
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ isThemeAdd, setIsThemeAdd ] = useState(false);
const [ resourceTypes, setResourceTypes ] = useState(undefined);
useEffect(() => {
......@@ -26,7 +45,10 @@ const UpdateDirectoryModal = (props) => {
setDir(null);
form.resetFields();
getResourceTypes();
if (action === 'add') {
form.setFieldsValue({ resourceType: 'dataAsset' });
}
if ((dirId||'')!=='') {
getDirectory();
}
......@@ -35,14 +57,17 @@ const UpdateDirectoryModal = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ visible ])
const getResourceTypes = () => {
dispatch({
type: 'assetmanage.getResourceTypes',
callback: data => {
setResourceTypes(data);
}
})
}
const codeRestrict = useMemo(() => {
if (action === 'add') {
if (isThemeAdd) return true;
return (dir && dir.level === 1) ? true : false;
} else {
return (dir && dir.level <= 2) ? true : false;
}
return false;
}, [action, dir, isThemeAdd])
const getDirectory = () => {
setDir(null);
......@@ -55,7 +80,7 @@ const UpdateDirectoryModal = (props) => {
setDir(data);
if (action !== 'add') {
form.setFieldsValue({ code: data?.code, name: data?.name||'', desc: data?.desc||'', remarks: data?.remarks||'', resourceType: data?.resourceType });
form.setFieldsValue({ code: data?.code, name: data?.name||'', desc: data?.desc||'', remarks: data?.remarks||'' });
}
}
})
......@@ -73,7 +98,7 @@ const UpdateDirectoryModal = (props) => {
name: row.name,
desc: row.desc,
remarks: row.remarks,
resourceType: row.resourceType
resourceType: 'dataAsset',
}
};
......@@ -81,7 +106,7 @@ const UpdateDirectoryModal = (props) => {
if (row.type === 'directory') {
if (dir === null) {
showMessage('warn', '资产目录节点信息正在加载中...');
showMessage('warn', '资产目录的目录节点信息正在加载中...');
return;
}
......@@ -89,16 +114,19 @@ const UpdateDirectoryModal = (props) => {
parentPath: dir.path||''
}};
} else {
payload.data.resourceType = row.resourceType;
payload.data.resourceType = 'dataAsset';
}
} else {
if (dir === null) {
showMessage('warn', '资产目录节点信息正在加载中...');
showMessage('warn', '资产目录的目录节点信息正在加载中...');
return;
}
payload.data = { ...payload.data, ...{ order: dir.order, id: dirId } };
if (dir.level === 3) {
payload.data = { ...payload.data, code: dir.code };
}
const parentPath = dir.path.substring(0, dir.path.lastIndexOf("/"));;
payload = { ...payload, params: {
......@@ -111,6 +139,7 @@ const UpdateDirectoryModal = (props) => {
payload: payload,
callback: data => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true, data?.id||'');
},
error: () => {
......@@ -123,23 +152,33 @@ const UpdateDirectoryModal = (props) => {
}
}
const reset = () => {
setIsThemeAdd(false);
setDir(undefined);
form.resetFields();
}
const onReset = () => {
if(action === 'add') {
setIsThemeAdd(false);
form.resetFields();
} else {
if (dir === null) {
showMessage('warn', '资产目录节点信息正在加载中...');
showMessage('warn', '资产目录的目录节点信息正在加载中...');
return;
}
form.resetFields();
form.setFieldsValue({ code: dir?.code, name: dir?.name||'', desc: dir?.desc||'', remarks: dir?.remarks||'' });
}
}
const onValuesChange = (changedValues, allValues) => {
if (action==='add') {
if (changedValues.type === 'theme') {
setIsThemeAdd(true);
setIsThemeAdd(true);
// if (allValues.code?.length > 3 || !(/^[A-Z]+$/.test(allValues.code))) {
// form.setFieldsValue({ code: '' });
// }
} else if (changedValues.type === 'directory') {
setIsThemeAdd(false);
}
......@@ -160,15 +199,21 @@ const UpdateDirectoryModal = (props) => {
return (
<Modal
forceRender
title={'资产目录信息'}
title={'资产目录的目录信息'}
visible={visible}
width={600}
onCancel={() => { onCancel && onCancel() }}
onCancel={() => {
reset();
onCancel && onCancel();
}}
footer={
<Space>
<Button type="primary" onClick={onOk} loading={confirmLoading}>提交</Button>
<Button onClick={onReset} >重置</Button>
<Button onClick={() => onCancel && onCancel() }>返回</Button>
<Button onClick={() => {
reset();
onCancel && onCancel();
}}>返回</Button>
</Space>
}
>
......@@ -180,35 +225,21 @@ const UpdateDirectoryModal = (props) => {
rules={[{ required: true, message: '必填项' }]}
>
<Radio.Group>
<Radio value='theme'>主题</Radio>
<Radio value='directory' disabled={ dirId===null }>目录</Radio>
<Radio value='theme'>栏目</Radio>
<Radio value='directory' disabled={ !dirId }>目录</Radio>
</Radio.Group>
</Form.Item>
}
{
((action==='add'&&isThemeAdd) || action!=='add') && <Form.Item
label="资产类型"
name="resourceType"
placeholder='请选择资产类型'
codeRestrict && <Form.Item
label="编号"
name="code"
rules={[{ required: true, message: '必填项' }]}
>
<Select>
{
resourceTypes?.map((item,index) => {
return <Select.Option key={item.code}>{item.name}</Select.Option>
})
}
</Select>
</Form.Item>
<CodeInput restrict={codeRestrict} action={action} />
</Form.Item>
}
<Form.Item
label="编号"
name="code"
rules={[{ required: true, message: '必填项' }]}
>
<Input placeholder="请输入编号" />
</Form.Item>
<Form.Item
label="名称"
name="name"
rules={[{ required: true, message: '必填项' }]}
......
import React, { useState, useEffect } from 'react';
import { Drawer, Pagination, List } from 'antd';
import { dispatch } from '../../../../model';
const WorkbookDrawer = (props) => {
const { onCancel, visible, type = 'tableau' } = props;
const [ loading, setLoading ] = useState(false);
const [ workbooks, setWorkbooks ] = useState([]);
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const { pageNum, pageSize } = pagination;
const [ total, setTotal ] = useState(0);
useEffect(() => {
if (visible) {
setPagination({ pageNum: 1, pageSize: 20 });
getWorkbooks();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const getWorkbooks = (p = 1, s = 20) => {
setLoading(true);
const url = (type==='tableau') ? 'assetmanage.listWorkbooks' : 'assetmanage.listSmartBIWorkbooks';
dispatch({
type: url,
payload: {
pageNum: p,
pageSize: s
},
callback: data => {
setLoading(false);
setTotal(data.total);
setWorkbooks(data.data||[]);
},
error: () => {
setLoading(false);
}
})
}
// const onDeleteClick = (id) => {
// dispatch({
// type: 'assetmanage.deleteWorkbooks',
// payload: {
// data: [id]
// },
// callback: () => {
// getWorkbooks();
// }
// })
// }
const onDevelopClick = (id) => {
const url = (type==='tableau') ? 'assetmanage.getWorkbookUrl' : 'assetmanage.getSmartBIWorkbookUrl';
dispatch({
type: url,
payload: {
workbookId: id,
},
callback: data => {
window.open(data||'');
}
})
}
return (
<Drawer
forceRender
visible={ visible }
title={`${type}工作簿`}
width={900}
placement="right"
closable={ true }
onClose={() => {
onCancel && onCancel();
}}
>
<List
itemLayout="horizontal"
size="small"
loading={loading}
dataSource={workbooks}
renderItem={item => (
<List.Item
key={item.id}
actions={[<a key="develop" onClick={() => { onDevelopClick(item.id); }}>报表开发</a>]}
>
<List.Item.Meta
title={item.name}
/>
</List.Item>
)}
/>
<Pagination
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum||1, pageSize: _pageSize || 20 });
getWorkbooks(_pageNum||1, _pageSize||20);
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum || 1, pageSize: _pageSize || 20 });
getWorkbooks(_pageNum||1, _pageSize||20);
}}
current={pageNum}
pageSize={pageSize}
defaultCurrent={1}
total={total}
pageSizeOptions={[10,20]}
showTotal={total => `共 ${total} 条`}
/>
</Drawer>
)
}
export default WorkbookDrawer;
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { Modal, Form, Button, Space, Input, Checkbox, Row, Col, Typography } from 'antd';
import { dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
const WorksheetModal = (props) => {
const { onCancel, visible, id, type = 'tableau', metadata = {} } = props;
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ form ] = Form.useForm();
useEffect(() => {
if (visible) {
form?.setFieldsValue({ tableName: metadata?.tableName, columnNames: metadata?.columnItems?.map(item => item.metadataColumnName) });
}
}, [visible, type])
const onOk = async() => {
try {
const row = await form?.validateFields();
setConfirmLoading(true);
const url = (type === 'tableau') ? 'assetmanage.saveWorkbook': 'assetmanage.saveSmartBIWorkbook';
let metadataTableId = '';
metadata?.columnItems?.forEach(item => {
if (!metadataTableId) {
metadataTableId = item.metadataTableId;
}
})
dispatch({
type: url,
payload: {
data: {
dataAssetId:id,
metadataTableId,
...row,
}
},
callback: data => {
window.open(data);
reset();
setConfirmLoading(false);
showMessage("success","新增成功");
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
setConfirmLoading(false);
}
}
const reset = () => {
setConfirmLoading(false);
form?.resetFields();
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 21 },
},
};
return (
<Modal
forceRender
title='工作簿信息'
visible={ visible }
width={ 600 }
onCancel={() => {
reset();
onCancel && onCancel();
}}
footer={
<Space>
<Button onClick={() => {
reset();
onCancel && onCancel();
}}>取消</Button>
<Button type="primary" onClick={ onOk } loading={ confirmLoading }>确定</Button>
</Space>
}
>
<Form form={form} style={{ maxHeight: 600, overflow: 'auto' }} {...formItemLayout}>
<Form.Item
label="名称"
name="name"
rules={[{ required: true, message: '请填写工作簿名称!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="字段"
name="columnNames"
>
<Checkbox.Group>
<Row>
{
metadata?.columnItems?.map((item, index) => <Col key={index} span={8}>
<Checkbox
value={item.metadataColumnName}
style={{
lineHeight: '32px',
}}
>
<Typography.Text style={{ width: 120 }} ellipsis>{item.metadataColumnName}</Typography.Text>
</Checkbox>
</Col>)
}
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item
label="元数据"
name="tableName"
hidden={true}
>
<Input />
</Form.Item>
</Form>
</Modal>
);
}
export default WorksheetModal;
\ No newline at end of file
......@@ -2,8 +2,9 @@ import React, { useState } from 'react';
import classNames from 'classnames';
import { Form } from 'antd';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import { ResizableBox } from 'react-resizable';
import AssetTree from './Component/AssetTree';
import AssetTree from './Component/AssetManageTree';
import AssetDirectory from './Component/AssetDirectory';
import AssetTable from './Component/AssetTable';
import AssetAction from './Component/AssetAction';
......@@ -15,10 +16,12 @@ import './index.less';
const AssetManage = (props) => {
const [ nodeId, setNodeId ] = useState('');
const [ nodeId, setNodeId ] = useState(null);
const [ nodeType, setNodeType ] = useState('');
const [ nodeLevel, setNodeLevel ] = useState(null);
const [ assetParams, setAssetParams ] = useState({ assetId: '', assetDirId: '' })
const [ expandTree, setExpandTree ] = useState(true);
const [ assetFullScreen, setAssetFullScreen ] = useState(false);
const [ assetCount, setAssetCount ] = useState(0);
const [ directoryChanged, setDirectoryChanged ] = useState(false);
......@@ -28,9 +31,11 @@ const AssetManage = (props) => {
const { assetId, assetDirId } = assetParams;
const onTreeSelect = (value, type) => {
setNodeId(value||'');
const onTreeSelect = (value, type, level) => {
setNodeId(value);
setNodeType(type);
setNodeLevel(level);
}
const onTableSelect = (id, did) => {
......@@ -57,30 +62,44 @@ const AssetManage = (props) => {
setAssetCount(count);
}
const onFullScreenChange = (value) => {
setAssetFullScreen(value);
}
const classes = classNames('asset-manage', {
'asset-manage-collapse': !expandTree
});
const middleClasses = classNames('middle', {
'middle-fullscreen': assetFullScreen
});
return (
<div className={classes}>
<div className='left'>
<ResizableBox
className='left'
width={230}
height={Infinity}
axis='x'
minConstraints={[230, Infinity]} maxConstraints={[Infinity, Infinity]}
>
<AssetTree onSelect={onTreeSelect} onDirectoryChange={onDirectoryChange} {...props} />
</div>
</ResizableBox>
{
expandTree && <Separate width={15} />
}
<div className='middle'>
<AssetDirectory id={nodeId} assetCount={assetCount} directoryChanged={directoryChanged} onElementsChange={onElementsChange} />
<Separate height={15} />
<AssetTable nodeId={nodeId} nodeType={nodeType} reference={AssetManageReference} elementsChanged={elementsChanged} assetActionChanged={assetActionChanged} onSelect={onTableSelect} onCountChange={onAssetCountChange} {...props} />
<div className={middleClasses}>
{/* <AssetDirectory id={nodeId} assetCount={assetCount} directoryChanged={directoryChanged} onElementsChange={onElementsChange} />
<Separate height={15} /> */}
<AssetTable nodeId={nodeId} nodeType={nodeType} nodeLevel={nodeLevel} reference={AssetManageReference} elementsChanged={elementsChanged} assetActionChanged={assetActionChanged} onSelect={onTableSelect} onCountChange={onAssetCountChange} onFullScreenChange={onFullScreenChange} {...props} />
<div className='tree-toggle' onClick={treeToggleClick}>
{ expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div>
</div>
<Separate width='15px' />
{/* <Separate width='15px' />
<div className='right'>
<AssetAction form={form} id={assetId} dirId={assetDirId} action='detail' onChange={onAssetActionChange} />
</div>
<div className='tree-toggle' onClick={treeToggleClick}>
{ expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div>
</div> */}
</div>
)
}
......
......@@ -5,7 +5,7 @@
height: 100%;
.left {
width: 230px;
flex: 0 0 auto;
border-right: 1px solid #EFEFEF;
overflow: hidden;
}
......@@ -14,7 +14,7 @@
display: flex;
justify-content: center;
align-items: center;
left: 245px;
left: 0px;
right: 0;
background: #f2f5fc;
position: absolute;
......@@ -28,7 +28,13 @@
}
.middle {
width: calc(100% - 230px - 400px - 30px);
position: relative;
flex: 1;
overflow: hidden;
}
.middle-fullscreen {
position: static;
}
.right {
......@@ -39,14 +45,10 @@
.asset-manage-collapse {
.left {
width: 0;
width: 0 !important;
}
.tree-toggle {
left: 0;
}
.middle {
width: calc(100% - 400px - 30px);
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@ import React, { useState } from "react";
import { Modal } from "antd";
import { dispatch } from '../../../../model';
import AssetTree from '../../AssetManage/Component/AssetTree';
import AssetTree from '../../AssetManage/Component/AssetManageTree';
import { showMessage } from '../../../../util';
import { AssetManageReference, AssetRecycleReference, AssetMountReference } from "../../../../util/constant";
......@@ -19,7 +19,7 @@ const AssetMount = (props) => {
const onOk = () => {
if ((dirIds||[]).length === 0) {
showMessage('warn', '请先选择资产目录');
showMessage('warn', '请先选择资产目录的目录');
return;
}
......@@ -50,6 +50,7 @@ const AssetMount = (props) => {
return(
<Modal
forceRender
title={(reference===AssetRecycleReference)?'挂载目录详情':'变更目录详情'}
visible={ visible }
width={ 400 }
......@@ -60,12 +61,14 @@ const AssetMount = (props) => {
}}
onOk={ onOk }
>
<AssetTree
{
visible && <AssetTree
checkable={true}
onCheck={onCheck}
tableId={(reference===AssetManageReference&&(ids||[].length>0))?ids[0]:''}
reference={AssetMountReference}
/>
/>
}
</Modal>
)
}
......
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