Commit 7f4fb13a by zhaochengxiang

移植迈瑞多模版资产

parent 9a561ea1
......@@ -36,6 +36,7 @@
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-dom": "^17.0.1",
"react-loadable": "^5.5.0",
"react-redux": "^7.1.0",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.0.1",
......@@ -76,6 +77,6 @@
"last 1 safari version"
]
},
"proxy": "http://192.168.0.54:8888",
"proxy": "http://192.168.0.38:8089",
"homepage": "http://myhost/data-govern"
}
......@@ -3,35 +3,32 @@ import {
BrowserRouter as Router,
Route, Switch
} from 'react-router-dom';
import loadable from "./util/loadable";
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';
import Map from './view/Manage/Map';
import Model from './view/Manage/Model';
import ModelConfig from './view/Manage/ModelConfig';
import AssetManage from './view/Manage/AssetManage';
import AssetResourceBrowse from './view/Manage/AssetResourceBrowse';
import AssetBrowse from './view/Manage/AssetBrowse';
import AssetRecycle from './view/Manage/AssetRecycle';
import DatasourceManage from './view/Manage/DatasourceManage';
import AssetDetailPage from './view/Manage/AssetManage/Component/AssetDetailPage';
import AssetDetail from './view/Manage/AssetManage/Component/AssetDetail';
import ImportAction from './view/Manage/Model/Component/ImportAction';
import EditModel from './view/Manage/Model/Component/EditModel';
import EditTemplate from './view/Manage/ModelConfig/Component/EditTemplate';
import AssetTree from './view/Manage/AssetManage/Component/AssetTree';
import Asset1104Manage from './view/Manage/Asset1104Manage';
import AssetTask from './view/Manage/AssetTask';
import AssetEvaluate from './view/Manage/AssetEvaluate';
import DataService from './view/Manage/Pdata';
import DataServiceDetail from './view/Manage/Pdata/Component/ServiceDetail';
import GrantedDataServiceList from './view/Manage/Pdata/Component/GrantedList';
import { AssetMountReference } from './util/constant';
import { AssetBrowseReference, AssetDraftReference, AssetMountReference } from './util/constant';
const AssetAction = loadable(()=> import('./view/Manage/AssetManage/Component/AssetAction'));
export const AppContext = React.createContext();
export const appId = generateUUID();
export class App extends React.Component {
constructor() {
......@@ -94,10 +91,7 @@ export class App extends React.Component {
setGlobalState,
onGlobalStateChange
}}>
<AssetDetail
id={id}
terms={terms}
/>
<AssetAction reference={AssetBrowseReference} id={id} permissionId={hostParams?.permissionId} action='detail' terms={terms} readonly={true} />
</AppContext.Provider>
);
}
......@@ -207,25 +201,14 @@ export class App extends React.Component {
<Route path={'/center-home/view/datasource-manage'} component={DatasourceManage} exact />
<Route path={'/center-home/view/data-model'} component={Model} exact />
<Route path={'/center-home/view/model-config'} component={ModelConfig} exact />
<Route path={'/center-home/view/asset-map'} component={Map} exact />
<Route path={'/center-home/view/asset-manage'} component={AssetManage} exact />
<Route path={'/center-home/view/asset-resource-browse'} component={AssetResourceBrowse} exact />
<Route path={'/center-home/view/asset-browse'} component={AssetBrowse} exact />
<Route path={'/center-home/view/asset-recycle'} component={AssetRecycle} exact />
<Route path={'/center-home/view/asset-task'} component={AssetTask} exact />
<Route path={'/center-home/view/asset-1104-manage'} component={Asset1104Manage} exact />
<Route path={'/center-home/menu/datasource-manage'} component={DatasourceManage} exact />
<Route path={'/center-home/menu/data-model'} component={Model} exact />
<Route path={'/center-home/menu/model-config'} component={ModelConfig} exact />
<Route path={'/center-home/menu/asset-map'} component={Map} exact />
<Route path={'/center-home/menu/asset-manage'} component={AssetManage} exact />
<Route path={'/center-home/menu/asset-resource-browse'} component={AssetResourceBrowse} exact />
<Route path={'/center-home/menu/asset-browse'} component={AssetBrowse} exact />
<Route path={'/center-home/menu/asset-recycle'} component={AssetRecycle} exact />
<Route path={'/center-home/menu/asset-task'} component={AssetTask} exact />
<Route path={'/center-home/menu/asset-evaluate'} component={AssetEvaluate} exact />
<Route path={'/center-home/menu/asset-1104-manage'} component={Asset1104Manage} exact />
<Route path={'/center-home/menu/data-service'} component={DataService} exact />
<Route path={'/center-home/data-model-action'} component={EditModel} exact />
<Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact />
......
......@@ -6,13 +6,12 @@ import { Connect, showErrorNotifaction } from '../util';
import { reducers } from './reducer';
import * as user from './user';
import * as datasource from './datasource';
import * as map from './map';
import * as datamodel from './datamodel';
import * as assetmanage from './assetmanage';
import * as tag from './tag';
import * as pds from './pds';
const funcs = Connect({ user, datamodel, map, assetmanage, datasource, tag, pds })
const funcs = Connect({ user, datamodel, assetmanage, datasource, tag, pds })
function* request(args) {
const { type, payload, callback, error } = args.args;
......
import * as service from '../service/map';
import { call } from 'redux-saga/effects';
export function* getAllTopics() {
return yield call(service.getAllTopics);
}
export function* getTableModelByDirIid(payload) {
return yield call(service.getTableModelByDirId, payload);
}
export function* getTreeTotalLevel(payload) {
return yield call(service.getTreeTotalLevel, payload);
}
export function* queryAllDirectoryAsTreeByDirLevel(payload) {
return yield call(service.queryAllDirectoryAsTreeByDirLevel, payload);
}
export function* getTableModelInfoByDirIdAndBeginIndex(payload) {
return yield call(service.getTableModelInfoByDirIdAndBeginIndex, payload);
}
import { GetJSON, Get } from "../util/axios"
export function getAllTopics() {
return GetJSON("/dataassetmanager/countApi/queryAllTopics");
}
export function getTableModelByDirId(payload) {
return GetJSON("/dataassetmanager/countApi/getTableModelInfoByDirIdAnotherType", payload);
}
export function getTreeTotalLevel(payload) {
return Get("/dataassetmanager/countApi/getTreeTotalLevel", payload);
}
export function queryAllDirectoryAsTreeByDirLevel(payload) {
return GetJSON("/dataassetmanager/countApi/queryAllDirectoryAsTreeByDirLevel", payload);
}
export function getTableModelInfoByDirIdAndBeginIndex(payload) {
return GetJSON("/dataassetmanager/countApi/getTableModelInfoByDirIdAndBeginIndex", payload);
}
......@@ -106,7 +106,7 @@ export function getSmartBiUrl(payload) {
}
export function getOwners() {
return GetJSON("/informationmanagement/userData/findAll")
// return GetJSON("/informationmanagement/userData/findAll")
}
export function getDepartments() {
......
import axios from 'axios';
import qs from 'qs';
import LocalStorage from 'local-storage';
import { IsArr, showMessage, isSzseEnv } from './index';
import { IsArr, showMessage, isSzseEnv, generateUUID } from './index';
import { appId } from '../App';
const CancelToken = axios.CancelToken;
const baseURL = '/api/';
const debugEnv = 475629450;
const instance = axios.create({
baseURL,
timeout: 15000,
timeout: 300000,
headers: {
//'X-Custom-Header': 'rest',
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
},
responseType: 'json', // default
validateStatus: (status) => {
return true;
}
});
const instanceRow = axios.create({
baseURL: '',
timeout: 300000,
headers: {
//'X-Custom-Header': 'rest',
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
......@@ -23,7 +37,7 @@ const instance = axios.create({
const textplain = axios.create({
baseURL,
timeout: 15000,
timeout: 300000,
headers: {
//'X-Custom-Header': 'rest',
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
......@@ -69,7 +83,7 @@ textplain.interceptors.request.use(
const fileplain = axios.create({
baseURL,
timeout: 15000,
timeout: 300000,
headers:{'Content-Type':'multipart/form-data'},
processData:false,
validateStatus: (status) => {
......@@ -90,7 +104,7 @@ const callback = resp => {
if (resp.status === 401) {
showMessage('warn', 'session过期,请重新登录!');
window.location.href = isSzseEnv?"/api/auth/login":"/center-home/view/login"
window.location.href = isSzseEnv?`/api/auth/oauth/login?redirecturl=${window.location.href}`:"/center-home/view/login"
return null;
}
......@@ -101,12 +115,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;
const templateType = getTemplateType();
return textplain.get(url, {
params: {...params, env}, cancelToken
params: {templateType, ...params}, cancelToken
}).then(
callback
)
......@@ -114,10 +132,22 @@ export function Get(url, params) {
export function GetJSON(url, params) {
const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv;
const templateType = getTemplateType();
return instance.get(url, {
params: {...params, env}, cancelToken,
params: {templateType, ...params}, cancelToken,
validateStatus: false
}).then(
callback
)
}
export const GetJSONRaw = (url, params) => {
const cancelToken = __source ? __source.token : null;
const templateType = getTemplateType();
return instanceRow.get(url, {
params: {templateType, ...params}, cancelToken,
validateStatus: false
}).then(
callback
......@@ -126,10 +156,10 @@ export function GetJSON(url, params) {
export function Delete(url, params) {
const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv;
const templateType = getTemplateType();
return instance.delete(url, {
params: {...params, env}, cancelToken,
params: {templateType, ...params}, cancelToken,
}).then(
callback
)
......@@ -138,14 +168,14 @@ export function Delete(url, params) {
export function PostJSON(url, payload) {
const { params = null, data = null } = payload||{};
const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv;
const templateType = getTemplateType();
return IsArr(data) ? instance.post(url, data, {
params: {...params, env}, cancelToken
params: {templateType, ...params}, cancelToken
}).then(
callback
) : instance.post(url, null, {
params: {...params, env}, data, cancelToken
params: {templateType, ...params}, data, cancelToken
}).then(
callback
)
......@@ -154,43 +184,68 @@ export function PostJSON(url, payload) {
export function Post(url, payload) {
const { params = null, data = null } = payload||{};
const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv;
const templateType = getTemplateType();
return textplain.post(url, null, {
params: {...params, env}, data, cancelToken
params: {templateType, ...params}, data, cancelToken
}).then(
callback
)
}
export function PostFile(url, payload, fileName='file') {
export function PostFile(url, payload) {
const { fileList = null, params = null } = payload||{};
const env = LocalStorage.get('assetsEnv')??debugEnv;
const templateType = getTemplateType();
let formData = new FormData();
(fileList||[]).forEach(file=> {
formData.append(fileName, file);
formData.append('file', file);
});
return fileplain.post(url, formData, { params: {...params, env} } ).then(
return fileplain.post(url, formData, { params: {templateType, ...params} } ).then(
callback
)
}
let config = {
export const callFetchRaw = (method, url, options) => {
const { params, ...reqConfig } = options;
const config = {
baseURL,
timeout: 300000,
cache: 'no-cache',
}
const templateType = getTemplateType();
return axios.request({
method,
url,
params: {templateType, ...params},
...config,
...reqConfig
})
}
export const callFetchRawByFormData = (method, url, options) => {
const { params, ...reqConfig } = options;
const config = {
baseURL,
timeout: 300000,
headers: {
'Content-Type': 'multipart/form-data',
},
cache: 'no-cache',
}
}
const templateType = getTemplateType();
export const callFetchRaw = (method, url, options) => {
const { params, ...reqConfig } = options;
var bodyFormData = new FormData();
Object.keys(params||[]).forEach(key => {
Object.keys(params||{}).forEach(key => {
bodyFormData.append(key, params[key]);
});
bodyFormData.append('templateType', templateType);
return axios.request({
method,
......
......@@ -3,6 +3,7 @@ export const AnchorId = 'id';
export const AnchorTimestamp = 'timestamp';
export const AnchorDirId = 'did';
export const AnchorRequireId = 'rid';
export const AnchorTemplateType = 'templateType';
//编辑模型
export const Action = 'action';
......@@ -26,8 +27,8 @@ export const DataModelerRoleReader = 'reader';
//资产
export const AssetManageReference = 'asset-manage';
export const Asset1104ManageReference = 'asset-1104-manage';
export const AssetBrowseReference = 'asset-browse';
export const ResourceBrowseReference = 'resource-browse';
export const AssetRecycleReference = 'asset-recycle';
export const AssetMountReference = 'asset-mount';
export const AssetDraftReference = 'asset-draft';
\ No newline at end of file
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
......@@ -397,3 +397,7 @@ export function getDataModelerRole(user) {
return '';
}
export function isPostAsset(templateType) {
return templateType === 'post'
}
\ No newline at end of file
import React from "react"
import Loadable from 'react-loadable';
import { Spin } from "antd";
const loadingComponent =()=>{
return (
<div style={{ width: '100%', textAlign: 'center' }}>
<Spin spinning={true} style={{marginTop:'45vh'}} tip="数据加载中......" />
</div>
)
}
const loadable = ( loaders ) => {
return Loadable( {
loader: loaders,
delay: 10000,
timeout: 300,
loading: loadingComponent,
});
};
export default loadable;
\ No newline at end of file
import React, { useState } from 'react';
import classNames from 'classnames';
import { Form } from 'antd';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import AssetTree from '../AssetManage/Component/AssetTree';
import AssetDirectory from '../AssetManage/Component/AssetDirectory';
import AssetTable from '../AssetManage/Component/AssetTable';
import AssetAction from '../AssetManage/Component/AssetAction';
import Separate from '../AssetManage/Component/Separate';
import { Asset1104ManageReference, AssetManageReference } from '../../../util/constant';
import '../AssetManage/index.less';
const Asset1104Manage = (props) => {
const [ nodeId, setNodeId ] = useState('');
const [ nodeType, setNodeType ] = useState('');
const [ assetParams, setAssetParams ] = useState({ assetId: '', assetDirId: '' })
const [ expandTree, setExpandTree ] = useState(true);
const [ assetCount, setAssetCount ] = useState(0);
const [ directoryChanged, setDirectoryChanged ] = useState(false);
const [ elementsChanged, setElementsChanged ] = useState(false);
const [ assetActionChanged, setAssetActionChanged ] = useState(false);
const [ form ] = Form.useForm();
const { assetId, assetDirId } = assetParams;
const onTreeSelect = (value, type) => {
setNodeId(value||'');
setNodeType(type);
}
const onTableSelect = (id, did) => {
setAssetParams({ assetId: id, assetDirId: did });
}
const treeToggleClick = () => {
setExpandTree(!expandTree);
}
const onElementsChange = () => {
setElementsChanged(!elementsChanged);
}
const onDirectoryChange = () => {
setDirectoryChanged(!directoryChanged);
}
const onAssetActionChange = () => {
setAssetActionChanged(!assetActionChanged);
}
const onAssetCountChange = (count) => {
setAssetCount(count);
}
const classes = classNames('asset-manage', {
'asset-manage-collapse': !expandTree
});
return (
<div className={classes}>
<div className='left'>
<AssetTree reference={Asset1104ManageReference} onSelect={onTreeSelect} onDirectoryChange={onDirectoryChange} {...props} />
</div>
{
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>
<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>
)
}
export default Asset1104Manage;
\ 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 (currentNode.levelId.split('-').length > 1) {
if ((node.levelId.split('-').length===currentNode.levelId.split('-').length+1) && node.levelId.slice(0, currentLevelId.length)===currentLevelId) {
node.show = true;
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,8 +265,12 @@ const RelationContainer = (props) => {
}
const onTypeChange = (e) => {
if (e.target.value === 'map') {
window?.open('/center-home/menu/asset-map');
} else {
setType(e.target.value);
}
}
return (
<div style={{ width: '100%', height: '100%', position: 'relative' }}>
......
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,19 @@ 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 [ nodeAllowdLoadDataAsset, setNodeAllowdLoadDataAsset ] = useState(false);
const { centerId, expandId } = nodeParams;
const onTreeSelect = (value, type) => {
setNodeParams({ centerId: value||'', expandId: '' });
const onTreeSelect = (value, type, allowdLoadDataAsset) => {
setNodeParams({ centerId: value, expandId: '', nodeType: type });
setNodeAllowdLoadDataAsset(allowdLoadDataAsset);
}
const treeToggleClick = () => {
......@@ -46,11 +50,8 @@ const AssetBrowse = (props) => {
setAssetCount(count);
}
let nodeId = '';
if ((expandId||'') !== '') {
nodeId = expandId;
} else {
nodeId = centerId;
const onFullScreenChange = (value) => {
setAssetFullScreen(value);
}
const classes = classNames('asset-browse', {
......@@ -58,36 +59,46 @@ const AssetBrowse = (props) => {
'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} />
</div>
<AssetTable nodeId={centerId} nodeType={nodeParams.nodeType} nodeAllowdLoadDataAsset={nodeAllowdLoadDataAsset} reference={reference} onCountChange={onAssetCountChange} onFullScreenChange={onFullScreenChange} {...props} />
</div>
</div>
<div className='tree-toggle' onClick={treeToggleClick}>
{ expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div>
<div className='relation-toggle' onClick={relationToggleClick}>
{/* <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);
}
......
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 { dispatch } from '../../../model';
import { showMessage, formatDate } from '../../../util';
import { AppContext } from '../../../App';
import { getTemplateType } from '../../../util/axios';
const FC = (props) => {
const { onCancel, onSuccess, visible, } = props;
const [ fileList, setFileList ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ loading, setLoading ] = useState(false);
const [ logs, setLogs ] = useState([]);
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const { pageNum, pageSize } = pagination;
const [ total, setTotal ] = useState(0);
const [ users, setUsers ] = useState([]);
const app = useContext(AppContext);
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text, record, index) => {
return (index+1).toString();
},
width: 60,
},
{
title: '开始时间',
dataIndex: 'startTime',
width: 200,
ellipsis: true,
render: (_, record, __) => {
return formatDate(record.startTime);
}
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 200,
ellipsis: true,
render: (_, record, __) => {
return formatDate(record.endTime);
}
},
{
title: '耗时',
dataIndex: 'costTime',
width: 100,
ellipsis: true,
render: (_, record, __) => {
return record.costTime?`${Number(record.costTime/1000)}秒`:'';
}
},
{
title: '导入人',
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: '导入状态',
dataIndex: 'state',
ellipsis: true,
}
]
useEffect(() => {
if (visible) {
setPagination({ pageNum: 1, pageSize: 20 });
getUsers();
getLogs();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const getLogs = (p = 1, s = 20) => {
setLoading(true);
dispatch({
type: 'assetmanage.importDraftLogs',
payload: {
page: p,
pageSize: s
},
callback: data => {
setLoading(false);
setTotal(data.total);
setLogs(data.data||[]);
},
error: () => {
setLoading(false);
}
})
}
const getUsers = () => {
dispatch({
type: 'pds.getOwners',
callback: (data) => {
setUsers(data);
}
})
}
const uploadProps = {
onRemove: file => {
const index = fileList.indexOf(file);
const newFileList = fileList.slice();
newFileList.splice(index, 1);
setFileList(newFileList);
},
beforeUpload: file => {
const isLt2OM = file.size / 1024 / 1024 < 20;
if (!isLt2OM) {
showMessage('error', '上传文件必须小于20M');
setFileList([]);
return false;
}
setFileList([file]);
return false;
},
fileList: fileList || [],
accept:".xlsx",
};
const downloadTemplate = () => {
window.open(`/api/dataassetmanagertest/dataAssetApi/getImportTemplate?templateType=${getTemplateType()}`);
}
const handleOk = () => {
if ((fileList||[]).length === 0) {
showMessage('info', '请先选择文件上传');
return;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.draftAssetImport',
payload: { fileList: fileList, params: { env: `${app?.env?.domainId}`, saveAsDraft: false } },
callback: data => {
setConfirmLoading(false);
setFileList([]);
getLogs(pageNum, pageSize);
showMessage('success', '导入动作完成,详情查看日志');
onSuccess && onSuccess(data||'');
},
error: () => {
setConfirmLoading(false);
}
});
}
const reset = () => {
setConfirmLoading(false);
setFileList([]);
}
return (
<Drawer
forceRender
visible={ visible }
title='资产目录草稿批量修改'
width={900}
placement="right"
closable={ true }
onClose={() => {
reset();
onCancel && onCancel();
}}
>
<div className='mt-3'>
<Form layout='inline'>
<Form.Item label='Excel导入:'>
<Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模板下载
</Button>
<Upload style={{ display: 'inline' }} {...uploadProps }>
<Button icon={
<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
</Form.Item>
<Form.Item>
<Button type='primary' onClick={handleOk} loading={confirmLoading}>确定导入</Button>
</Form.Item>
<Button onClick={() => getLogs()} style={{ marginLeft: 'auto' }}>刷新日志</Button>
</Form>
</div>
<Divider orientation="left">导入日志</Divider>
<Table
className='mt-3'
columns={columns||[]}
rowKey={'id'}
dataSource={logs||[]}
pagination={false}
loading={loading}
expandable={{
expandedRowRender: record => <React.Fragment>
{record.message?.split('<br/>').map((info, index) => {
return <Typography.Paragraph key={index}>{info}</Typography.Paragraph>
})}
</React.Fragment>
}}
sticky
/>
<Pagination
className="text-center mt-3"
showSizeChanger
onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum||1, pageSize: _pageSize || 20 });
getLogs(_pageNum||1, _pageSize||20);
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum || 1, pageSize: _pageSize || 20 });
getLogs(_pageNum||1, _pageSize||20);
}}
current={pageNum}
pageSize={pageSize}
defaultCurrent={1}
total={total}
showTotal={total => ` ${total} `}
/>
</Drawer>
)
}
export default FC;
\ No newline at end of file
import React, { useState } from 'react';
import { Tabs } from 'antd';
import Draft from './draft'
import Recycle from './recycle'
import './index.less'
const { TabPane } = Tabs;
const FC = () => {
const [tabKey, setTabKey] = useState('1');
const onTabChange = (key) => {
setTabKey(key);
}
return (
<div className='asset-draft'>
<Tabs
tabBarExtraContent={{
left:<div style={{ marginLeft:10 }}></div>
}}
size="large"
activeKey={tabKey}
onChange={onTabChange}
>
<TabPane tab='草稿' key='1'>
<Draft />
</TabPane>
<TabPane tab='未挂载' key='2'>
<Recycle />
</TabPane>
</Tabs>
</div>
)
}
export default FC
\ No newline at end of file
.asset-draft {
height: calc(100vh - 124px);
background: #fff;
overflow: auto;
.yy-tabs-nav {
margin: 0;
}
}
\ No newline at end of file
import React, { useState } from 'react'
import { Modal, Form, Input, Spin, Button } from 'antd'
import { dispatch } from '../../../model'
import { showMessage } from '../../../util'
const FC = (props) => {
const { visible, onCancel, items } = props
const [waiting, setWaiting] = React.useState(false)
const [form] = Form.useForm()
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
const close = (refresh = false) => {
setWaiting(false)
form?.resetFields()
onCancel?.(refresh)
}
const save = async () => {
try {
const values = await form.validateFields()
setWaiting(true)
dispatch({
type: 'assetmanage.startProcess',
payload: {
params: {
...values,
},
data: (items??[]).map(item => item.id)
},
callback: () => {
showMessage('success', '申请成功!')
close(true)
},
error: () => {
setWaiting(false)
}
})
} catch (errInfo) {
}
}
const footer = React.useMemo(() => {
return [
<Button key={'cancel'}
onClick={() => close()}
>取消</Button>,
<Button key={'save'} type='primary'
disabled={waiting}
onClick={() => save()}
>保存</Button>
]
}, [close, save, waiting])
return (
<Modal
destroyOnClose
visible={visible}
title='申请'
width={520}
bodyStyle={{ overflowX: 'auto', maxHeight: '80vh' }}
footer={footer}
onCancel={() => { close() }}
>
<Spin spinning={waiting}>
<Form
labelAlign='left'
{...formItemLayout}
form={form}
>
<Form.Item
label='申请原因'
name='reason'
rules={[{ required: true, message: '请填写申请原因' }]}
>
<Input.TextArea placeholder='请在申请原因栏中描述用途说明以及必要性' rows={4} />
</Form.Item>
</Form>
</Spin>
</Modal>
);
}
export default FC
\ No newline at end of file
import React from 'react'
import { Modal, Spin, Button, Form, Input, TreeSelect, Row, Col, Checkbox } from "antd"
import produce from "immer"
import { dispatch } from '../../../model'
const FC = (props) => {
const { visible, item, action, onCancel } = props
const [waiting, setWaiting] = React.useState(false)
const [loading, setLoading] = React.useState(false)
const [elements, setElements] = React.useState()
const basicRef = React.useRef(null)
React.useEffect(() => {
getElements()
}, [])
const getElements = () => {
setLoading(true)
dispatch({
type: 'assetmanage.listComputableElements',
callback: data => {
setElements(data??[])
setLoading(false)
},
error: () => {
setLoading(false)
}
})
}
const close = (refresh = false) => {
setWaiting(false)
setLoading(false)
onCancel?.(refresh)
}
const save = async () => {
try {
const row = await basicRef.current?.validate()
setWaiting(true)
dispatch({
type: 'assetmanage.saveEvaluation',
payload: {
data: (action==='add')?row:{...item, ...row}
},
callback: data => {
close(true)
},
error: () => {
setWaiting(false)
}
})
} catch (e) {
}
}
const footer = React.useMemo(() => {
return [
<Button key={'cancel'}
onClick={() => close()}
>取消</Button>,
<Button key={'save'} type='primary'
disabled={waiting}
onClick={() => save()}
>保存</Button>
]
}, [close, save, waiting])
return (
<Modal
visible={visible}
footer={footer}
width='50%'
bodyStyle={{ padding: '15px 15px 0px 15px', overflowX: 'auto', maxHeight: '80vh' }}
title='新增评估方案'
centered destroyOnClose
onCancel={() => { close() }}
>
<Spin spinning={loading || waiting}>
<Basic ref={basicRef} item={item} elements={elements} />
</Spin>
</Modal>
)
}
export default FC
const Basic = React.forwardRef(function ({ item, elements }, ref) {
const [form] = Form.useForm()
React.useImperativeHandle(ref, () => ({
validate: async () => {
return await form?.validateFields()
},
}), [form])
React.useEffect(() => {
if (item) {
form?.setFieldsValue(item)
}
}, [item])
return (
<Form
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
autoComplete="off"
>
<Form.Item
label="方案名称"
name="name"
rules={[{ required: true, message: '请输入方案名称!' }]}
>
<Input placeholder="请输入方案名称" />
</Form.Item>
<Form.Item
name='dirs'
label='资产目录'
rules={[{ required: true, message: '请选择资产目录!' }]}
>
<AssetCatalogItem />
</Form.Item>
<Form.Item
name='elementIds'
label='评估要素'
rules={[{ required: true, message: '请选择评估要素!' }]}
>
<Checkbox.Group style={{ width: '100%' }}>
<Row>
{
(elements??[]).map((item, index) => {
return (
<Col span={6} key={index}>
<Checkbox
value={item.id}
style={{
lineHeight: '32px',
}}
>
{item.name}
</Checkbox>
</Col>
)
})
}
</Row>
</Checkbox.Group>
</Form.Item>
</Form>
)
})
const AssetCatalogItem = ({ value, onChange }) => {
const [loadingTreeData, setLoadingTreeData] = React.useState(false)
const [treeData, setTreeData] = React.useState()
React.useEffect(() => {
getTreeData()
}, [])
const treeData1 = React.useMemo(() => {
if (treeData) {
const newTreeData = produce(treeData, draft => {
const setNode = (g) => {
g.key = g.nodeId
g.title = g.text
g.value = g.nodeId
g.children?.forEach((child) => {
setNode(child)
})
}
draft.forEach((child) => {
setNode(child)
})
})
return newTreeData
}
return undefined
}, [treeData])
const getTreeData = () => {
setLoadingTreeData(true)
dispatch({
type: 'assetmanage.queryAssetDirectoryAsTree',
callback: data => {
setLoadingTreeData(false)
setTreeData(data)
},
error: () => {
setLoadingTreeData(false)
}
})
}
return (
<TreeSelect
value={(value??[]).map(item => {
return {
label: item.name,
value: item.id
}
})}
loading={loadingTreeData}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData1}
placeholder="请选择资产目录"
treeDefaultExpandAll
treeCheckable={true}
treeCheckStrictly={true}
showCheckedStrategy={TreeSelect.SHOW_ALL}
onChange={(value) => {
onChange?.((value??[]).map(item => {
return {
id: item.value,
name: item.label
}
}))
}}
/>
)
}
\ No newline at end of file
import React from 'react'
import { debounceTime, Subject } from 'rxjs'
import { Space, Input, Table, Button, Tooltip, Modal, Pagination, Typography } from 'antd'
import { ExclamationCircleOutlined } from '@ant-design/icons'
import { defaultPage, usePage } from '../../../util/page'
import { dispatch } from '../../../model'
import AddEvaluate from './add'
import './index.less'
import { showMessage } from '../../../util'
const FC = (props) => {
const [args, setArgs] = React.useState(() => ({
params: {
page: defaultPage.pageNum,
size: defaultPage.pageSize,
keyword: undefined,
startTime: undefined,
endTime: undefined,
},
}))
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [total, setTotal] = React.useState()
const [elements, setElements] = React.useState()
const $keyword = React.useMemo(() => new Subject(), [])
const [keyword, setKeyword] = React.useState()
const [addEvaluateParams, setAddEvaluateParams] = React.useState({
visible: false,
item: undefined,
action: undefined
})
const [selectedRows, setSelectedRows] = React.useState([])
const [page, setPage] = usePage()
const [modal, contextHolder] = Modal.useModal()
React.useEffect(() => {
getElements()
}, [])
const setArgsAndPage = React.useCallback((params) => {
// 设置查询参数时将分页置为1
setPage(prevpg => {
setArgs((prev) => {
const newparams = params ? { ...prev.params, ...params } : undefined
return { params: { ...newparams, page: 1, size: prevpg.pageSize } }
})
return ({ ...prevpg, pageNum: 1 })
})
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const setPageAndArgs = React.useCallback((page) => {
setPage(prev => ({ ...prev, ...page }))
setArgs((prev) => {
return { params: { ...prev.params, page: page.pageNum, size: page.pageSize } }
})
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
React.useEffect(() => {
const $$keyword = $keyword.pipe(debounceTime(1000)).subscribe((keyword) => {
setArgsAndPage({ keyword })
})
return () => {
$$keyword.unsubscribe()
}
}, [])
React.useEffect(() => {
getEvaluations()
}, [args])
const cols = [
{
title: '方案名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '评估范围',
dataIndex: 'dirs',
render: (dirs) => {
const dirStr = (dirs??[]).map(item => item.name).toString()
return (
<Tooltip title={dirStr}>
<Typography.Text ellipsis={true}>
{dirStr}
</Typography.Text>
</Tooltip>
)
}
},
{
title: '评估要素',
dataIndex: 'elementIds',
render: (elementIds) => {
const elementStr = (elements??[]).filter(item => (elementIds??[]).indexOf(item.id) !== -1).map(item => item.name).toString()
return (
<Tooltip title={elementStr}>
<Typography.Text ellipsis={true}>
{elementStr}
</Typography.Text>
</Tooltip>
)
}
},
{
title: '总得分',
dataIndex: 'score',
ellipsis: true,
},
{
title: '资产总数',
dataIndex: 'dataAssetTotalNum',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTimestamp',
ellipsis: true,
render: (_, record) => record.createTimestamp ? new Date(record.createTimestamp).toLocaleString() : ''
},
{
title: '操作',
key: 'action',
width: 120,
render: (_,record) => (
<a onClick={() => {
setAddEvaluateParams({
visible: true,
item: record,
action: 'edit',
})
}}>编辑</a>
)
}
]
const getEvaluations = () => {
setLoading(true)
dispatch({
type: 'assetmanage.getEvaluations',
payload: {
pageNum: args.params.page,
pageSize: args.params.size,
keyword: args.params.keyword,
},
callback: data => {
setLoading(false)
setData(data?.data)
setTotal(data?.total)
},
error: () => {
setLoading(false)
}
})
}
const getElements = () => {
dispatch({
type: 'assetmanage.listElements',
callback: data => {
setElements(data??[])
}
})
}
return (
<div className='asset-evalute'>
<div className='header px-3'>
<Space>
<Button onClick={() => {
setAddEvaluateParams({
visible: true,
item: undefined,
action: 'add',
})
}}>新增</Button>
<Button onClick={() => {
getEvaluations()
}}>刷新</Button>
<Tooltip title={((selectedRows??[]).length===0)?'请先选择评估方案':''}>
<Button
disabled={(selectedRows??[]).length===0}
onClick={() => {
modal.confirm({
title: '确认运行选中的评估方案吗?',
icon: <ExclamationCircleOutlined />,
okText: '确认',
cancelText: '取消',
onOk: () => {
dispatch({
type: 'assetmanage.runEvaluations',
payload: {
params: {
evaluationProjectIds: (selectedRows??[]).map(item => item.id).toString()
}
},
callback: data => {
showMessage('success', '运行成功')
},
})
}
})
}}>运行</Button>
</Tooltip>
<Tooltip title={((selectedRows??[]).length===0)?'请先选择评估方案':''}>
<Button
disabled={(selectedRows??[]).length===0}
onClick={() => {
modal.confirm({
title: '确认删除选中的评估方案吗?',
icon: <ExclamationCircleOutlined />,
okText: '确认',
cancelText: '取消',
onOk: () => {
dispatch({
type: 'assetmanage.deleteEvaluations',
payload: {
params: {
evaluationProjectIds: (selectedRows??[]).map(item => item.id).toString()
}
},
callback: data => {
showMessage('success', '删除成功')
getEvaluations()
},
})
}
})
}}>删除</Button>
</Tooltip>
</Space>
<Space>
<Input size="middle"
placeholder="方案名称"
value={keyword}
bordered={true} allowClear
onChange={(e) => {
const keyword = e.target.value
setKeyword(keyword)
$keyword.next((keyword??'').trim())
}}
style={{
width: 250
}}
/>
</Space>
</div>
<div className='px-3 pt-3'>
<Table
scroll={{y: 'calc(100vh - 285px)'}}
rowKey='id'
loading={loading}
columns={cols}
dataSource={data||[]}
pagination={false}
rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item?.id),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
/>
<Pagination
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPageAndArgs({ pageNum: _pageNum||1, pageSize: _pageSize || 20 })
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPageAndArgs({ pageNum: _pageNum||1, pageSize: _pageSize || 20 })
}}
current={page.pageNum}
pageSize={page.pageSize}
defaultCurrent={1}
total={total}
pageSizeOptions={[10,20]}
showTotal={total => `共 ${total} 条`}
/>
</div>
<AddEvaluate
{...addEvaluateParams}
onCancel={(refresh) => {
setAddEvaluateParams({
visible: false,
item: undefined,
action: undefined
})
refresh && getEvaluations()
}}
/>
{contextHolder}
</div>
)
}
export default FC
\ No newline at end of file
.asset-evalute {
height: 100%;
background-color: #fff;
.header {
display: flex;
flex: none;
height: 57px;
border-bottom: 1px solid #EFEFEF;
justify-content: space-between;
align-items: center;
}
}
\ No newline at end of file
import React, { useState } from 'react';
import { Modal, Form, Space, Button } from 'antd';
import React, { useState, useRef } from 'react';
import { Modal, Form, Space, Button, Spin } from 'antd';
import { showMessage } from '../../../../util';
import { dispatch } from '../../../../model';
import AssetAction from './AssetAction';
import './AddAssetModel.less';
import { getTemplateType } from '../../../../util/axios';
const AddAssetModel = (props) => {
const { onCancel, visible, nodeId } = props;
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ metadataId, setMetadataId ] = useState('');
const [ waiting, setWaiting ] = useState(false);
const [ elements, setElements ] = useState([]);
const [postRelations, setPostRelations] = useState();
const [ form ] = Form.useForm();
......@@ -19,14 +17,19 @@ const AddAssetModel = (props) => {
setElements(value);
}
const onMetadataChange = (value) => {
setMetadataId(value);
const onPostRelationsChange = (value) => {
setPostRelations(value);
}
const close = (refresh = false) => {
setWaiting(false)
form?.resetFields()
onCancel?.(refresh)
}
const onOk = async() => {
try {
const row = await form?.validateFields();
const newElements = [...elements];
(newElements||[]).forEach(element => {
if (row.hasOwnProperty(element.name)) {
......@@ -34,63 +37,84 @@ const AddAssetModel = (props) => {
}
});
const params = {
dirId: nodeId,
}
if ((metadataId||'')!=='') {
params.metadataId = metadataId;
}
setConfirmLoading(true);
setWaiting(true);
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
type: 'assetmanage.checkCodeIsExist',
payload: {
params,
data: { elements: newElements }
},
callback: isExist => {
if (isExist === 'true') {
setWaiting(false);
showMessage('warn', '已存在相同的资产目录编号,请重新输入');
} else {
dispatch({
type: 'assetmanage.checkIsNeedSaveAsDraft',
callback: (data) => {
const needSaveAsDraft = (data==='true')?true:false
dispatch({
type: needSaveAsDraft?'assetmanage.saveAsDraft':'assetmanage.addOrUpdateDataAsset',
payload: {
params: {
dirId: nodeId,
operation: 'release',
},
data: { elements: newElements, templateType: getTemplateType(), ...postRelations }
},
callback: () => {
setConfirmLoading(false);
showMessage("success","新增成功");
onCancel && onCancel(true);
close(true)
},
error: () => {
setWaiting(false);
}
})
},
error: () => {
setConfirmLoading(false);
setWaiting(false);
}
})
}
},
error: () => {
setWaiting(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
setConfirmLoading(false);
setWaiting(false);
}
}
return (
<Modal
forceRender
destroyOnClose
className='asset-add'
title='新增资产信息'
title='新增资产目录信息'
visible={ visible }
width={ 600 }
onCancel={() => { onCancel && onCancel(); } }
centered
width='80%'
onCancel={() => { close() } }
footer={
<Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={ onOk } loading={ confirmLoading }>确定</Button>
<Button type="primary" onClick={ onOk } disabled={ waiting }>确定</Button>
</Space>
}
bodyStyle={{ padding: '10px' }}
bodyStyle={{ overflowX: 'auto', height: '80vh' }}
>
{
visible && <AssetAction
<Spin spinning={waiting}>
<AssetAction
form={form}
dirId={nodeId}
action='add'
onMetadataChange={onMetadataChange}
onElementsChange={onElementsChange}
onPostRelationsChange={onPostRelationsChange}
/>
}
</Spin>
</Modal>
);
}
......
.asset-add {
.yy-modal-body {
max-height: 500px !important;
overflow: auto !important;
}
}
\ No newline at end of file
.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
import React, { useEffect, useState } from "react";
import { Spin, Descriptions, Divider } from "antd";
import MetadataInfo from './MetadataInfo';
import { highlightSearchContentByTerms } from '../../../../util';
import { dispatch } from '../../../../model';
import StandardName from './StandardName';
const AssetDetail = (props)=>{
const { id, dirId, terms } = props;
const [ asset, setAsset ] = useState('');
const [ types, setTypes ] = useState([]);
const [ loading, setLoading ] = useState(false);
useEffect(() => {
if ((id||'') !== '') {
getAssetThenGetAssetName();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ id ])
const getAssetThenGetAssetName = () => {
setLoading(true);
dispatch({
type: 'assetmanage.getDataAssetDetail',
payload: {
dataAssetId: id,
dirId: dirId||''
},
callback: data => {
setLoading(false);
setAsset(data);
const _types = [];
(data?.elements||[]).forEach(element => {
if (_types.indexOf(element.type) === -1) {
_types.push(element.type);
}
})
setTypes(_types);
},
error: () => {
setLoading(false);
}
})
}
return(
<Spin spinning={loading}>
{
((dirId||'')!=='') && <React.Fragment>
<Descriptions column={2}>
<Descriptions.Item label='路径'>
{asset.currentPath||''}
</Descriptions.Item>
</Descriptions>
<Divider style={{ margin: '10px 0' }} />
</React.Fragment>
}
{
(types||[]).map((type, index) => {
const _currentValues = (asset.elements||[]).filter(element => element.type===type);
return (
<div key={index}>
<div className='flex' style={{ alignItems: 'center', padding: '15px 0' }}>
<div style={{ width: 3, height: 14, backgroundColor: '#0069AC', marginRight: 5 }} />
<span style={{ fontWeight: 'bold', color: '#464646' }}>{type||''}</span>
</div>
<Descriptions column={2}>
{
(_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>
}
</Descriptions.Item>
);
})
}
</Descriptions>
<Divider style={{ margin: '10px 0' }} />
</div>
)
})
}
</Spin>
)
}
export default AssetDetail;
\ No newline at end of file
......@@ -2,26 +2,65 @@ import React from 'react';
import { Drawer, Form } from 'antd';
import AssetAction from './AssetAction';
import { AssetDraftReference } from '../../../../util/constant';
import { dispatch } from '../../../../model';
const AssetDetailDrawer = (props) => {
const { onCancel, visible, id, dirId } = props;
const { onCancel, visible, id, dirId, reference, readonly = false } = props;
const [animated, setAnimated] = React.useState(true)
const [ form ] = Form.useForm();
const [draftAsset, setDraftAsset] = React.useState()
React.useEffect(() => {
if (visible) {
setTimeout(() => {
setAnimated(false)
}, 300)
if (reference === AssetDraftReference) {
getDraft()
}
}
}, [visible])
const title = React.useMemo(() => {
if (draftAsset?.state === 'draft') return '资产目录详情(待提交)'
if (draftAsset?.state === 'auditing') return '资产目录详情(审批中)'
return '资产目录详情'
}, [draftAsset])
const getDraft = () => {
dispatch({
type: 'assetmanage.getDraftDetail',
payload: {
params: {
draftId: id,
}
},
callback: data => {
setDraftAsset(data)
}
})
}
return (
<Drawer
forceRender
visible={ visible }
title='资产详情'
width={600}
title={title}
width='80%'
placement="right"
closable={ true }
destroyOnClose
onClose={() => {
setAnimated(true);
onCancel && onCancel();
}}
>
{
visible && <AssetAction form={form} id={id} dirId={dirId} action='detail' readOnly />
visible && !animated && <AssetAction reference={reference} form={form} id={id} dirId={dirId} action='detail' readonly={readonly} 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";
import { AssetBrowseReference, AssetDraftReference } from "../../../../util/constant";
const AssetDetailPage = (props)=>{
const [ data, setData ] = useState({ id: '', dirId: '' });
const [ data, setData ] = useState({ id: '', dirId: '', isDraft: '', catalog: '' });
const [loading, setLoading] = useState(false)
const [templateType, setTemplateType] = useState()
const [draftAsset, setDraftAsset] = React.useState()
const { id, dirId } = data;
const { id, dirId, isDraft, catalog } = data;
useEffect(() => {
const _id = getQueryParam('id', props.location.search);
const _dirId = getQueryParam('dirId', props.location.search);
setData({ id: _id, dirId: _dirId });
const _id = getQueryParam('id', props.location?.search);
const _dirId = getQueryParam('dirId', props.location?.search);
const _isDraft = getQueryParam('isDraft', props.location?.search);
const _catalog = getQueryParam('catalog', props.location?.search);
setData({ id: _id, dirId: _dirId, isDraft: _isDraft, catalog: _catalog });
getDataAssetDetail()
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const title = React.useMemo(() => {
if (draftAsset?.state === 'draft') return '资产目录详情(待提交)'
if (draftAsset?.state === 'auditing') return '资产目录详情(审批中)'
return '资产目录详情'
}, [draftAsset])
const getDataAssetDetail = () => {
const _id = getQueryParam('id', props.location.search);
const _dirId = getQueryParam('dirId', props.location.search);
const _isDraft = getQueryParam('isDraft', props.location.search);
let url = 'assetmanage.getDataAssetDetail';
let payload = {
dataAssetId: _id,
dirId: _dirId||'',
checkPermission: true
};
if (_isDraft === 'true') {
url = 'assetmanage.getDraftDetail';
payload = {
params: {
draftId: _id,
}
}
}
dispatch({
type: url,
payload: payload,
callback: data => {
setLoading(false);
LocalStorage.set(`templateType-${appId}`, data?.templateType)
setTemplateType(data?.templateType)
if (_isDraft === 'true') {
setDraftAsset(data)
}
},
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' }}>{title}</span>
</div>
<Spin spinning={loading}>
<div className='detail-container'>
<div className='detail-container-card'>
<AssetDetail id={id} dirId={dirId} />
{
templateType && <AssetAction id={id} dirId={dirId} reference={(isDraft==='true')?AssetDraftReference:AssetBrowseReference} action='detail' catalog={catalog} readonly={true} />
}
</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/dataassetmanagertest/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,9 +197,7 @@ const AssetDirectory = (props) => {
</Paragraph>
</div>
{
(reference===AssetManageReference || reference===ResourceBrowseReference) && <div className='flex'>
{
(reference === AssetManageReference) && <React.Fragment>
(reference===AssetManageReference) && <div className='flex'>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={dir?.remarks||''}>
<Text className='title-color' ellipsis={true}>
......@@ -208,10 +207,8 @@ const AssetDirectory = (props) => {
</Tooltip>
</Paragraph>
<Separate width={10} background='#fff' />
</React.Fragment>
}
{
(reference===ResourceBrowseReference || resourceState) && <React.Fragment>
{/* {
resourceState && <React.Fragment>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}>
<Tooltip title={resourceState?.relatedAsset}>
<Text className='title-color' ellipsis={true}>
......@@ -239,7 +236,7 @@ const AssetDirectory = (props) => {
</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-card-body {
padding: 0 !important;
}
.yy-descriptions-item {
padding-bottom: 10px !important;
}
.yy-tree {
height: 360px !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,38 +151,38 @@ const AssetTagModal = (props) => {
setConfirmLoading(true);
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
type: 'assetmanage.checkCodeIsExist',
payload: {
params,
data: { ...asset, elements: newElements }
},
callback: () => {
const nameList = [];
(dirs||[]).forEach(dir=>{
if (row.tags.indexOf(dir.id) !== -1) {
nameList.push(dir.path);
}
})
callback: isExist => {
if (isExist === 'true') {
setConfirmLoading(false);
showMessage('warn', '已存在相同的资产目录编号,请重新输入');
} else {
dispatch({
type: 'tag.getTagIdListByNameList',
type: 'assetmanage.addOrUpdateDataAsset',
payload: {
params: {
nameList: nameList.join(',')
}
params,
data: { ...asset, elements: newElements }
},
callback: data => {
const ids = [id];
if (data && ((data?.id||'')!=='') && (data?.id!==id)) {
ids.push(data?.id);
}
dispatch({
type: 'tag.batchAddTagResourceByTagList',
payload: {
params: {
tagIds: (data||[]).join(','),
resourceIds: id,
tagIds: tag?.tagId,
resourceIds: ids.join(','),
type: 'dataAsset',
creator
}
},
callback: () => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true);
},
......@@ -194,6 +195,7 @@ const AssetTagModal = (props) => {
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,7 +7,7 @@
}
.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;
}
......@@ -15,17 +15,3 @@
color: #f50;
}
}
\ No newline at end of file
.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 = () => {
dispatchLatest({
type: 'assetmanage.previewTreeByCustomElements',
payload: {
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: 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/dataassetmanagertest/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,37 @@ const ImportAssetDrawer = (props) => {
};
const handleOk = () => {
if (!currentResourceType) {
showMessage('warn', '请先选择资产类型');
return;
}
if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模版上传');
showMessage('info', '请先选择文件上传');
return;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.checkIsNeedSaveAsDraft',
callback: (data) => {
const needSaveAsDraft = (data==='true')?true:false
dispatch({
type: 'assetmanage.assetImport',
payload: { fileList: fileList, params: { dirId: nodeId, resourceType: currentResourceType } },
payload: { fileList: fileList, params: { env: `${app?.env?.domainId}`, saveAsDraft: needSaveAsDraft } },
callback: data => {
setConfirmLoading(false);
setFileList([]);
getLogs(pageNum, pageSize);
showMessage('success', '导入动作完成,详情查看日志');
onSuccess && onSuccess(data||'');
},
error: () => {
setConfirmLoading(false);
}
});
},
error: () => {
setConfirmLoading(false);
}
})
}
const reset = () => {
......@@ -189,7 +189,7 @@ const ImportAssetDrawer = (props) => {
<Drawer
forceRender
visible={ visible }
title='资产导入'
title='资产目录导入'
width={900}
placement="right"
closable={ true }
......@@ -198,11 +198,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 +214,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,14 +226,17 @@ 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
/>
<Pagination
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum||1, pageSize: _pageSize || 20 });
getLogs(_pageNum||1, _pageSize||20);
......@@ -259,7 +249,6 @@ const ImportAssetDrawer = (props) => {
pageSize={pageSize}
defaultCurrent={1}
total={total}
pageSizeOptions={[10,20]}
showTotal={total => ` ${total} `}
/>
</Drawer>
......
......@@ -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/dataassetmanagertest/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 = {
const payload = {
params: {
ignoreRepeatPath
},
fileList: fileList,
};
} else {
payload = {
params: {
ignoreRepeatPath,
parentPath: dir.path
},
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/dataassetmanagertest/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>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模板下载
</Button>
</div>
<Space>
<div className='mt-3'>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模版下载
</Button>
</Space>
</Spin>
</div>
</Modal>
)
}
......
import React from 'react';
import { Typography } from 'antd';
import { highlightSearchContentByTerms } from '../../../../util';
import { AppContext } from '../../../../App';
const FC = ({ value = '', terms = [] }) => {
const FC = ({ value = '', terms = [], publishedValue = '' }) => {
return (
<AppContext.Consumer>
{
appValue => <a onClick={() => {
appValue?.setGlobalState && appValue?.setGlobalState({
message: 'data-govern-show-standard-detail-message',
message: 'data-govern-show-index-detail-message',
data: { name: value }
})
}}>
<span>{highlightSearchContentByTerms(value, terms)}</span>
<Typography.Text>{highlightSearchContentByTerms(value, terms)}</Typography.Text>
{
publishedValue && (publishedValue !== value) && <Typography.Text className='ml-2' type='danger'><del>{publishedValue}</del></Typography.Text>
}
</a>
}
</AppContext.Consumer>
......
......@@ -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!=='草稿' && 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, { useEffect, useState, useContext } from 'react';
import { Modal, Form, Input, Select, Space, Button } from 'antd';
import React, { useState, useContext } from 'react';
import { Modal, Form, Input } from 'antd';
import { AppContext } from '../../../App';
import { dispatch } from '../../../model';
import { AuditProgress, Basic, Process, Template } from './TaskDetail';
import { dispatchLatest } from '../../../../model';
import { showNotifaction } from '../../../../util';
import { AppContext } from '../../../../App';
const FC = (props) => {
const { visible, onCancel, task, action } = props;
const StartFlowModal = (props) => {
const { visible, onCancel, ids } = props;
const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const { user } = useContext(AppContext);
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 () => {
const handleOk = async (status = '1') => {
try {
const values = await form.validateFields();
setConfirmLoading(true);
dispatch({
type: 'assetmanage.auditTask',
dispatchLatest({
type: 'assetmanage.startFlow',
payload: {
params: {
taskId: task?.id
},
data: {
dealUser: user?.userName??'test',
processDate: new Date().toLocaleString(),
status,
title: action,
value: values.content
env: app?.env?.domainId,
dataAssetIds: ids.join(','),
applyReason: values?.desc
}
},
callback: data => {
setConfirmLoading(false);
reset();
if (data) {
showNotifaction('申请提示', (data === 'ok')?'资产目录申请成功,请在我的流程》我的申请中查看详情':data, 5);
}
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
}
......@@ -52,42 +66,31 @@ const FC = (props) => {
return (
<Modal
destroyOnClose
visible={visible}
title={`${action}${task?.title}`}
width={1000}
title='资产目录申请'
width={520}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel && onCancel();
}}
footer={
<Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={() => {handleOk()}} loading={confirmLoading}>确定</Button>
{
(action==='审核') && <Button type="danger" onClick={() => {handleOk('-1')}} loading={confirmLoading}>驳回</Button>
}
</Space>
}
onOk={handleOk}
>
<Form
{...formItemLayout}
form={form}
>
<Form.Item
label={`${action}意见`}
name="content"
rules={[{ required: true, message: `请输入${action}意见` }]}
label="申请原因"
name="desc"
rules={[{ required: true, message: '请在申请原因栏中描述用途说明以及必要性(资产目录和服务权限申请需要说明请求字段)' }]}
>
<Input.TextArea rows={4} placeholder={`请输入${action}意见`} />
<Input.TextArea rows={6} />
</Form.Item>
</Form>
<AuditProgress task={task} />
<Basic task={task} />
<Process task={task} />
<Template task={task} />
</Modal>
);
}
export default FC;
\ No newline at end of file
export default StartFlowModal;
\ No newline at end of file
import React, { useCallback, useImperativeHandle, useRef } from "react";
import { showMessage } from "../../../../util";
export interface UploadType {
chooseFile: () => void
}
export default React.forwardRef<UploadType, { accept: string, upload: (files: FileList) => void }>(function ({ accept, upload }, ref) {
const input = useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => ({
chooseFile: () => {
input.current!.click();
}
}), [])
const onChange: React.ChangeEventHandler<HTMLInputElement> = useCallback((e) => {
console.log('files', e.target.files)
if (e.target.files) { //console.debug(input.current!.files)
e.preventDefault();
let file = e.target.files[0]
if (file) {
const fileSize = file.size
const isLt5G = fileSize / 1024 / 1024 / 1024 < 5
if (!isLt5G) {
showMessage('warn', '上传文件大小不超过5G')
return
}
}
e.target.files && upload(e.target.files)
}
}, [upload])
const onInputClick: React.MouseEventHandler<HTMLInputElement> = useCallback((e) => {
input.current!.value = ''
}, [])
return <input ref={input} type="file" multiple={false} accept={accept ?? "image/png"} style={{ visibility: 'hidden' }}
onChange={onChange}
onClick={onInputClick}
/>
})
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Drawer, Pagination, List } from 'antd';
import { dispatch } from '../../../../model';
const WorkbookDrawer = (props) => {
const { onCancel, visible, report } = 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);
dispatch({
type: 'assetmanage.listWorkbooksByType' ,
payload: {
pageNum: p,
pageSize: s,
type: report?.code,
},
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) => {
dispatch({
type: 'assetmanage.getViewUrlByWorkbookId',
payload: {
workbookId: id,
},
callback: data => {
window.open(data||'');
}
})
}
return (
<Drawer
forceRender
visible={ visible }
title={`${report?.name}工作簿`}
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, report, metadata = {} } = props;
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ form ] = Form.useForm();
useEffect(() => {
if (visible) {
form?.setFieldsValue({ tableName: metadata?.tableName, columns: metadata?.columnItems?.map(item => {
return {
cnName: item.metadataColumnCnName,
metadataColumnId: item.metadataColumnId,
name: item.metadataColumnName
}
}) });
}
}, [visible, report, metadata])
const onOk = async() => {
try {
const row = await form?.validateFields();
setConfirmLoading(true);
dispatch({
type: 'assetmanage.saveWorkbookByType',
payload: {
data: {
dataAssetId:id,
metadataTableId: metadata?.metadataTableId,
tableName: metadata?.tableName,
tableCnName: metadata?.tableCnName,
type: report?.code,
...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={`${report?.name}工作簿信息`}
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="columns"
>
<ColumnsItem data={metadata?.columnItems} />
</Form.Item>
<Form.Item
label="元数据"
name="tableName"
hidden={true}
>
<Input />
</Form.Item>
</Form>
</Modal>
);
}
export default WorksheetModal;
const ColumnsItem = ({ value, onChange, data }) => {
return (
<Checkbox.Group
onChange={(checkedValue) => {
if ((checkedValue??[]).length === 0) {
onChange([])
} else {
const filterData = (data??[]).filter(item => checkedValue.includes(item.metadataColumnName))
onChange(filterData.map(item => {
return {
cnName: item.metadataColumnCnName,
metadataColumnId: item.metadataColumnId,
name: item.metadataColumnName
}
}))
}
}}
value={(value??[]).map(item => item.name)}
>
<Row>
{
data?.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>
)
}
\ No newline at end of file
import React from 'react'
import { Tabs, Space, Button, Input, Pagination, Tooltip, Modal } from 'antd'
import { paginate } from '../../../../util'
import Table from '../../ResizeableTable'
import SelectResource from './select-resource'
import { dispatch } from '../../../../model';
import { AssetDraftReference } from '../../../../util/constant'
import AssetDetailDrawer from "./AssetDetailDrawer";
import { AppContext } from '../../../../App'
const FC = React.forwardRef(function ({ item, reference, action, onChange }, ref) {
const [activeKey, setActiveKey] = React.useState('asset')
const [keyword, setKeyword] = React.useState()
const [pagination, setPagination] = React.useState({
pageNum: 1,
pageSize: 20,
})
const [loadingAssets, setLoadingAssets] = React.useState(false)
const [assets, setAssets] = React.useState()
const [loadingServices, setLoadingServices] = React.useState(false)
const [services, setServices] = React.useState()
const [selectedRows, setSelectedRows] = React.useState()
const [selectResourceParams, setSelectResourceParams] = React.useState({
visible: false,
item: undefined,
type: undefined,
})
const [detailParams, setDetailParams] = React.useState({
visible: false,
id: undefined,
dirId: undefined,
})
const app = React.useContext(AppContext)
const [modal, contextHolder] = Modal.useModal()
React.useImperativeHandle(ref, () => ({
relations: {
postAssets: assets??[],
postServices: services??[]
}
}), [assets, services])
React.useEffect(() => {
onChange?.({
postAssets: assets??[],
postServices: services??[]
})
}, [assets, services])
React.useEffect(() => {
if (item?.id) {
getRelatedAssets()
getRelatedServices()
}
}, [item])
const [tableData, total] = React.useMemo(() => {
let newTableData = (activeKey==='asset')?[...assets??[]]:[...services??[]]
if (keyword) {
newTableData = (newTableData??[]).filter(item => (item.name??'').indexOf(keyword) !== -1)
}
return [paginate(newTableData, pagination.pageNum, pagination.pageSize), (newTableData??[]).length]
}, [activeKey, assets, services, keyword, pagination])
const assetCols = [
{
title: '序号',
dataIndex: 'index',
width:60,
render:(_, __, index)=> ((pagination.pageNum-1)*pagination.pageSize+index+1)
},
{
title: '路径',
dataIndex: 'path',
},
{
title: '资产编码',
dataIndex: 'relatedAssetCode',
},
{
title: '名称',
dataIndex: 'name',
},
{
title: '操作',
dataIndex: 'action',
width: 80,
render: (_, record) => {
return (
<Space>
<a onClick={(e) => {
e.stopPropagation();
setDetailParams({
visible: true,
id: record?.relatedAssetId,
})
}}>
详情
</a>
</Space>
)
}
}
]
const seriveCols = [
{
title: '序号',
dataIndex: 'index',
width:60,
render:(_, __, index)=> ((pagination.pageNum-1)*pagination.pageSize+index+1)
},
{
title: '路径',
dataIndex: 'path',
},
{
title: '数据服务资产编号',
dataIndex: 'relatedServiceCode',
},
{
title: '数据服务名称',
dataIndex: 'name',
},
{
title: '操作',
dataIndex: 'action',
width: 80,
render: (_, record) => {
return (
<Space>
<a onClick={(e) => {
e.stopPropagation();
app?.setGlobalState && app?.setGlobalState({
message: 'data-govern-show-service-detail',
data: {
id: record.relatedServiceId,
isOnlyEnding: true
}
})
}}>
详情
</a>
</Space>
)
}
}
]
const getRelatedAssets = () => {
setLoadingAssets(true)
let url = (reference===AssetDraftReference)?'assetmanage.getDraftRelatedAssets':'assetmanage.getRelatedAssets'
let params = (reference===AssetDraftReference)?{
draftId: item?.id
}:{
dataAssetId: item?.id
}
dispatch({
type: url,
payload: {
params
},
callback: (data) => {
setLoadingAssets(false)
setAssets(data)
},
error: () => {
setLoadingAssets(false)
}
})
}
const getRelatedServices = () => {
setLoadingServices(true)
let url = (reference===AssetDraftReference)?'assetmanage.getDraftRelatedServices':'assetmanage.getRelatedServices'
let params = (reference===AssetDraftReference)?{
draftId: item?.id
}:{
dataAssetId: item?.id
}
dispatch({
type: url,
payload: {
params
},
callback: (data) => {
setLoadingServices(false)
setServices(data)
},
error: () => {
setLoadingServices(false)
}
})
}
const onEditClick = () => {
setSelectResourceParams({
visible: true,
item,
type: activeKey,
})
}
const onDeleteClick = () => {
if (activeKey === 'asset') {
setAssets(prevAssets => {
const filterData = (prevAssets??[]).filter(item => (selectedRows??[]).map(_item => _item.relatedAssetId).indexOf(item.relatedAssetId)===-1)
return filterData
})
} else if (activeKey === 'service') {
setServices(prevServices => {
const filterData = (prevServices??[]).filter(item => (selectedRows??[]).map(_item => _item.relatedServiceId).indexOf(item.relatedServiceId)===-1)
return filterData
})
}
setSelectedRows(prevSelectedRows => {
return []
})
}
const onSelectResourceCancel = (data) => {
setSelectResourceParams({
visible: false,
item: undefined,
type: undefined
})
if (data) {
if (activeKey === 'asset') {
setAssets(prevAssets => {
const filterData = (data??[]).filter(item => (prevAssets??[]).map(_item => _item.relatedAssetId).indexOf(item.relatedAssetId)===-1)
return [...prevAssets??[], ...filterData]
})
} else if (activeKey === 'service') {
setServices(prevServices => {
const filterData = (data??[]).filter(item => (prevServices??[]).map(_item => _item.relatedServiceId).indexOf(item.relatedServiceId)===-1)
return [...prevServices??[], ...filterData]
})
}
}
}
return (
<div>
<Tabs activeKey={activeKey} onChange={(val) => {
setActiveKey(val)
setPagination({ ...pagination, pageNum: 1 })
setSelectedRows()
}}>
<Tabs.TabPane tab='关联资产' key='asset' />
<Tabs.TabPane tab='关联服务' key='service' />
</Tabs>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
{
action !== 'detail' ? <Space>
<Button onClick={onEditClick}>编辑</Button>
<Tooltip title={(selectedRows??[]).length===0?(activeKey==='asset'?'请先选择资产':'请先选择服务'):''}>
<Button disabled={(selectedRows??[]).length===0} onClick={onDeleteClick}>删除</Button>
</Tooltip>
</Space> : <div />
}
<Input size="middle"
placeholder={activeKey==='asset'?'搜索资产名称':'搜索服务名称'}
value={keyword}
bordered={true} allowClear
style={{ width: 200 }}
onChange={(e) => {
setPagination({...pagination, pageNum: 1})
setKeyword(e.target.value)
}} />
</div>
<div className='mt-3'>
<Table
extraColWidth={32}
rowKey={activeKey==='asset'?'relatedAssetId':'relatedServiceId'}
loading={loadingAssets||loadingServices}
columns={activeKey==='asset'?assetCols:seriveCols}
dataSource={tableData??[]}
pagination={false}
rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => (activeKey==='asset')?item.relatedAssetId:item.relatedServiceId),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
/>
{
(total!==0) && <Pagination
className="text-center my-3"
showSizeChanger
onChange={(page,size) => {
setPagination({ pageNum: page, pageSize: size })
}}
current={pagination.pageNum}
pageSize={pagination.pageSize}
defaultCurrent={1}
total={total}
showTotal={() => `共${total??0}项`}
/>
}
</div>
<SelectResource
{...selectResourceParams}
onCancel={onSelectResourceCancel}
/>
<AssetDetailDrawer
{...detailParams}
readonly={true}
onCancel={() => {
setDetailParams({
visible: false,
id: undefined,
dirId: undefined,
})
}}
/>
{contextHolder}
</div>
)
})
export default FC
\ No newline at end of file
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