Commit 7f4fb13a by zhaochengxiang

移植迈瑞多模版资产

parent 9a561ea1
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
"react-dnd": "^14.0.2", "react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0", "react-dnd-html5-backend": "^14.0.0",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-loadable": "^5.5.0",
"react-redux": "^7.1.0", "react-redux": "^7.1.0",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"react-router-dom": "^5.0.1", "react-router-dom": "^5.0.1",
...@@ -76,6 +77,6 @@ ...@@ -76,6 +77,6 @@
"last 1 safari version" "last 1 safari version"
] ]
}, },
"proxy": "http://192.168.0.54:8888", "proxy": "http://192.168.0.38:8089",
"homepage": "http://myhost/data-govern" "homepage": "http://myhost/data-govern"
} }
...@@ -3,35 +3,32 @@ import { ...@@ -3,35 +3,32 @@ import {
BrowserRouter as Router, BrowserRouter as Router,
Route, Switch Route, Switch
} from 'react-router-dom'; } 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 Signin from './view/Signin';
import Home from './view/Home'; import Home from './view/Home';
import Manage from './view/Manage'; import Manage from './view/Manage';
import Map from './view/Manage/Map';
import Model from './view/Manage/Model'; import Model from './view/Manage/Model';
import ModelConfig from './view/Manage/ModelConfig'; import ModelConfig from './view/Manage/ModelConfig';
import AssetManage from './view/Manage/AssetManage'; import AssetManage from './view/Manage/AssetManage';
import AssetResourceBrowse from './view/Manage/AssetResourceBrowse';
import AssetBrowse from './view/Manage/AssetBrowse'; import AssetBrowse from './view/Manage/AssetBrowse';
import AssetRecycle from './view/Manage/AssetRecycle';
import DatasourceManage from './view/Manage/DatasourceManage'; import DatasourceManage from './view/Manage/DatasourceManage';
import AssetDetailPage from './view/Manage/AssetManage/Component/AssetDetailPage'; 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 ImportAction from './view/Manage/Model/Component/ImportAction';
import EditModel from './view/Manage/Model/Component/EditModel'; import EditModel from './view/Manage/Model/Component/EditModel';
import EditTemplate from './view/Manage/ModelConfig/Component/EditTemplate'; import EditTemplate from './view/Manage/ModelConfig/Component/EditTemplate';
import AssetTree from './view/Manage/AssetManage/Component/AssetTree'; 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 DataService from './view/Manage/Pdata';
import DataServiceDetail from './view/Manage/Pdata/Component/ServiceDetail'; import DataServiceDetail from './view/Manage/Pdata/Component/ServiceDetail';
import GrantedDataServiceList from './view/Manage/Pdata/Component/GrantedList'; 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 AppContext = React.createContext();
export const appId = generateUUID();
export class App extends React.Component { export class App extends React.Component {
constructor() { constructor() {
...@@ -94,10 +91,7 @@ export class App extends React.Component { ...@@ -94,10 +91,7 @@ export class App extends React.Component {
setGlobalState, setGlobalState,
onGlobalStateChange onGlobalStateChange
}}> }}>
<AssetDetail <AssetAction reference={AssetBrowseReference} id={id} permissionId={hostParams?.permissionId} action='detail' terms={terms} readonly={true} />
id={id}
terms={terms}
/>
</AppContext.Provider> </AppContext.Provider>
); );
} }
...@@ -207,25 +201,14 @@ export class App extends React.Component { ...@@ -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/datasource-manage'} component={DatasourceManage} exact />
<Route path={'/center-home/view/data-model'} component={Model} 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/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-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-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/datasource-manage'} component={DatasourceManage} exact />
<Route path={'/center-home/menu/data-model'} component={Model} 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/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-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-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/menu/data-service'} component={DataService} exact />
<Route path={'/center-home/data-model-action'} component={EditModel} exact /> <Route path={'/center-home/data-model-action'} component={EditModel} exact />
<Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact /> <Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact />
......
...@@ -6,13 +6,12 @@ import { Connect, showErrorNotifaction } from '../util'; ...@@ -6,13 +6,12 @@ import { Connect, showErrorNotifaction } from '../util';
import { reducers } from './reducer'; import { reducers } from './reducer';
import * as user from './user'; import * as user from './user';
import * as datasource from './datasource'; import * as datasource from './datasource';
import * as map from './map';
import * as datamodel from './datamodel'; import * as datamodel from './datamodel';
import * as assetmanage from './assetmanage'; import * as assetmanage from './assetmanage';
import * as tag from './tag'; import * as tag from './tag';
import * as pds from './pds'; 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) { function* request(args) {
const { type, payload, callback, error } = args.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) { ...@@ -106,7 +106,7 @@ export function getSmartBiUrl(payload) {
} }
export function getOwners() { export function getOwners() {
return GetJSON("/informationmanagement/userData/findAll") // return GetJSON("/informationmanagement/userData/findAll")
} }
export function getDepartments() { export function getDepartments() {
......
import axios from 'axios'; import axios from 'axios';
import qs from 'qs';
import LocalStorage from 'local-storage'; 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 CancelToken = axios.CancelToken;
const baseURL = '/api/'; const baseURL = '/api/';
const debugEnv = 475629450;
const instance = axios.create({ const instance = axios.create({
baseURL, 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: { headers: {
//'X-Custom-Header': 'rest', //'X-Custom-Header': 'rest',
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private' 'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
...@@ -23,7 +37,7 @@ const instance = axios.create({ ...@@ -23,7 +37,7 @@ const instance = axios.create({
const textplain = axios.create({ const textplain = axios.create({
baseURL, baseURL,
timeout: 15000, timeout: 300000,
headers: { headers: {
//'X-Custom-Header': 'rest', //'X-Custom-Header': 'rest',
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private' 'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
...@@ -69,7 +83,7 @@ textplain.interceptors.request.use( ...@@ -69,7 +83,7 @@ textplain.interceptors.request.use(
const fileplain = axios.create({ const fileplain = axios.create({
baseURL, baseURL,
timeout: 15000, timeout: 300000,
headers:{'Content-Type':'multipart/form-data'}, headers:{'Content-Type':'multipart/form-data'},
processData:false, processData:false,
validateStatus: (status) => { validateStatus: (status) => {
...@@ -90,7 +104,7 @@ const callback = resp => { ...@@ -90,7 +104,7 @@ const callback = resp => {
if (resp.status === 401) { if (resp.status === 401) {
showMessage('warn', 'session过期,请重新登录!'); 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; return null;
} }
...@@ -101,12 +115,16 @@ const callback = resp => { ...@@ -101,12 +115,16 @@ const callback = resp => {
return resp.data || resp; return resp.data || resp;
} }
export function getTemplateType() {
return LocalStorage.get(`templateType-${appId}`)
}
export function Get(url, params) { export function Get(url, params) {
const cancelToken = __source ? __source.token : null; const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
return textplain.get(url, { return textplain.get(url, {
params: {...params, env}, cancelToken params: {templateType, ...params}, cancelToken
}).then( }).then(
callback callback
) )
...@@ -114,10 +132,22 @@ export function Get(url, params) { ...@@ -114,10 +132,22 @@ export function Get(url, params) {
export function GetJSON(url, params) { export function GetJSON(url, params) {
const cancelToken = __source ? __source.token : null; const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
return instance.get(url, { 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 validateStatus: false
}).then( }).then(
callback callback
...@@ -126,10 +156,10 @@ export function GetJSON(url, params) { ...@@ -126,10 +156,10 @@ export function GetJSON(url, params) {
export function Delete(url, params) { export function Delete(url, params) {
const cancelToken = __source ? __source.token : null; const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
return instance.delete(url, { return instance.delete(url, {
params: {...params, env}, cancelToken, params: {templateType, ...params}, cancelToken,
}).then( }).then(
callback callback
) )
...@@ -138,14 +168,14 @@ export function Delete(url, params) { ...@@ -138,14 +168,14 @@ export function Delete(url, params) {
export function PostJSON(url, payload) { export function PostJSON(url, payload) {
const { params = null, data = null } = payload||{}; const { params = null, data = null } = payload||{};
const cancelToken = __source ? __source.token : null; const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
return IsArr(data) ? instance.post(url, data, { return IsArr(data) ? instance.post(url, data, {
params: {...params, env}, cancelToken params: {templateType, ...params}, cancelToken
}).then( }).then(
callback callback
) : instance.post(url, null, { ) : instance.post(url, null, {
params: {...params, env}, data, cancelToken params: {templateType, ...params}, data, cancelToken
}).then( }).then(
callback callback
) )
...@@ -154,43 +184,68 @@ export function PostJSON(url, payload) { ...@@ -154,43 +184,68 @@ export function PostJSON(url, payload) {
export function Post(url, payload) { export function Post(url, payload) {
const { params = null, data = null } = payload||{}; const { params = null, data = null } = payload||{};
const cancelToken = __source ? __source.token : null; const cancelToken = __source ? __source.token : null;
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
return textplain.post(url, null, { return textplain.post(url, null, {
params: {...params, env}, data, cancelToken params: {templateType, ...params}, data, cancelToken
}).then( }).then(
callback callback
) )
} }
export function PostFile(url, payload, fileName='file') { export function PostFile(url, payload) {
const { fileList = null, params = null } = payload||{}; const { fileList = null, params = null } = payload||{};
const env = LocalStorage.get('assetsEnv')??debugEnv; const templateType = getTemplateType();
let formData = new FormData(); let formData = new FormData();
(fileList||[]).forEach(file=> { (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 callback
) )
} }
let config = { export const callFetchRaw = (method, url, options) => {
baseURL, const { params, ...reqConfig } = options;
headers: {
'Content-Type': 'multipart/form-data', const config = {
}, baseURL,
cache: 'no-cache', timeout: 300000,
cache: 'no-cache',
}
const templateType = getTemplateType();
return axios.request({
method,
url,
params: {templateType, ...params},
...config,
...reqConfig
})
} }
export const callFetchRaw = (method, url, options) => { export const callFetchRawByFormData = (method, url, options) => {
const { params, ...reqConfig } = options; const { params, ...reqConfig } = options;
const config = {
baseURL,
timeout: 300000,
headers: {
'Content-Type': 'multipart/form-data',
},
cache: 'no-cache',
}
const templateType = getTemplateType();
var bodyFormData = new FormData(); var bodyFormData = new FormData();
Object.keys(params||[]).forEach(key => { Object.keys(params||{}).forEach(key => {
bodyFormData.append(key, params[key]); bodyFormData.append(key, params[key]);
}); });
bodyFormData.append('templateType', templateType);
return axios.request({ return axios.request({
method, method,
...@@ -199,4 +254,4 @@ export const callFetchRaw = (method, url, options) => { ...@@ -199,4 +254,4 @@ export const callFetchRaw = (method, url, options) => {
...config, ...config,
...reqConfig ...reqConfig
}) })
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ export const AnchorId = 'id'; ...@@ -3,6 +3,7 @@ export const AnchorId = 'id';
export const AnchorTimestamp = 'timestamp'; export const AnchorTimestamp = 'timestamp';
export const AnchorDirId = 'did'; export const AnchorDirId = 'did';
export const AnchorRequireId = 'rid'; export const AnchorRequireId = 'rid';
export const AnchorTemplateType = 'templateType';
//编辑模型 //编辑模型
export const Action = 'action'; export const Action = 'action';
...@@ -26,8 +27,8 @@ export const DataModelerRoleReader = 'reader'; ...@@ -26,8 +27,8 @@ export const DataModelerRoleReader = 'reader';
//资产 //资产
export const AssetManageReference = 'asset-manage'; export const AssetManageReference = 'asset-manage';
export const Asset1104ManageReference = 'asset-1104-manage';
export const AssetBrowseReference = 'asset-browse'; export const AssetBrowseReference = 'asset-browse';
export const ResourceBrowseReference = 'resource-browse'; export const ResourceBrowseReference = 'resource-browse';
export const AssetRecycleReference = 'asset-recycle'; export const AssetRecycleReference = 'asset-recycle';
export const AssetMountReference = 'asset-mount'; export const AssetMountReference = 'asset-mount';
\ No newline at end of file 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
...@@ -396,4 +396,8 @@ export function getDataModelerRole(user) { ...@@ -396,4 +396,8 @@ export function getDataModelerRole(user) {
// } // }
return ''; 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 { Radio } from 'antd';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import Relation from './Relation'; import Relation from './Relation';
import Thermodynamic from './Thermodynamic'; import Thermodynamic from './Thermodynamic';
import { AssetBrowseReference, ResourceBrowseReference } from '../../../../util/constant'; import { AssetBrowseReference, ResourceBrowseReference } from '../../../../util/constant';
import { AppContext } from '../../../../App';
const relationTypes = [ const relationTypes = [
{ {
...@@ -15,6 +14,10 @@ const relationTypes = [ ...@@ -15,6 +14,10 @@ const relationTypes = [
{ {
title: '热力图', title: '热力图',
key: 'thermodynamic' key: 'thermodynamic'
},
{
title: '数据地图',
key: 'map',
} }
] ]
...@@ -22,8 +25,6 @@ const RelationContainer = (props) => { ...@@ -22,8 +25,6 @@ const RelationContainer = (props) => {
const { nodeParams, onChange, reference, resize } = props; const { nodeParams, onChange, reference, resize } = props;
const { env } = useContext(AppContext);
const [ type, setType ] = useState('relation'); const [ type, setType ] = useState('relation');
const [ dirs, setDirs ] = useState([]); const [ dirs, setDirs ] = useState([]);
const [ nodes, setNodes ] = useState([]); const [ nodes, setNodes ] = useState([]);
...@@ -33,7 +34,7 @@ const RelationContainer = (props) => { ...@@ -33,7 +34,7 @@ const RelationContainer = (props) => {
useEffect(() => { useEffect(() => {
getDirectoryData(); getDirectoryData();
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [ env ]) }, [])
useEffect(() => { useEffect(() => {
if (type === 'relation') { if (type === 'relation') {
...@@ -45,7 +46,7 @@ const RelationContainer = (props) => { ...@@ -45,7 +46,7 @@ const RelationContainer = (props) => {
setRelationData(null); setRelationData(null);
} }
} else { } else {
if ((nodeParams?.centerId||'')!== '' && (dirs||[]).length>0) { if ((nodeParams?.centerId||'')!=='' && (dirs||[]).length>0) {
generateThermodynamicData(); generateThermodynamicData();
} else { } else {
setThermodynamicData(null); setThermodynamicData(null);
...@@ -184,8 +185,10 @@ const RelationContainer = (props) => { ...@@ -184,8 +185,10 @@ const RelationContainer = (props) => {
let nodeLevelId = `${node.levelId}-`; let nodeLevelId = `${node.levelId}-`;
let currentLevelId = `${currentNode.levelId}-`; let currentLevelId = `${currentNode.levelId}-`;
if ((node.levelId.split('-').length===currentNode.levelId.split('-').length+1) && node.levelId.slice(0, currentLevelId.length)===currentLevelId) { if (currentNode.levelId.split('-').length > 1) {
node.show = true; if ((node.levelId.split('-').length===currentNode.levelId.split('-').length+1) && node.levelId.slice(0, currentLevelId.length)===currentLevelId) {
node.show = !node.show;
}
} }
if (node.levelId.split('-').length >2) { if (node.levelId.split('-').length >2) {
...@@ -201,6 +204,7 @@ const RelationContainer = (props) => { ...@@ -201,6 +204,7 @@ const RelationContainer = (props) => {
} }
}) })
console.log('data', data)
setNodes(data); setNodes(data);
generateCenterNodeRelationData(data); generateCenterNodeRelationData(data);
...@@ -261,7 +265,11 @@ const RelationContainer = (props) => { ...@@ -261,7 +265,11 @@ const RelationContainer = (props) => {
} }
const onTypeChange = (e) => { const onTypeChange = (e) => {
setType(e.target.value); if (e.target.value === 'map') {
window?.open('/center-home/menu/asset-map');
} else {
setType(e.target.value);
}
} }
return ( return (
......
import React from 'react'; import React from 'react';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { dispatch } from '../../../../model'
//https://echarts.apache.org/examples/zh/editor.html?c=treemap-show-parent&lang=js //https://echarts.apache.org/examples/zh/editor.html?c=treemap-show-parent&lang=js
class Thermodynamic extends React.Component { class Thermodynamic extends React.Component {
componentDidMount() { componentDidMount() {
const { data, onClick } = this.props; 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.graph = init(this)(this.elem, data, onClick);
this.getScore();
} }
} }
...@@ -17,37 +16,20 @@ class Thermodynamic extends React.Component { ...@@ -17,37 +16,20 @@ class Thermodynamic extends React.Component {
const { data, onClick, resize } = this.props; const { data, onClick, resize } = this.props;
if (data !== prevProps.data) { if (data !== prevProps.data) {
this.graph?.dispose();
this.graph = init(this)(this.elem, data, onClick); this.graph = init(this)(this.elem, data, onClick);
this.getScore();
} else if (resize !== prevProps.resize) { } else if (resize !== prevProps.resize) {
this.graph?.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; elem = undefined;
graph = undefined; graph = undefined;
render() { render() {
return ( 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) { ...@@ -61,7 +43,7 @@ const init = (ctx) => function (container, data, onClick) {
return [ return [
{ {
itemStyle: { 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, borderWidth: 0,
gapWidth: 1 gapWidth: 1
}, },
...@@ -96,9 +78,9 @@ const init = (ctx) => function (container, data, onClick) { ...@@ -96,9 +78,9 @@ const init = (ctx) => function (container, data, onClick) {
title: { title: {
text: ((data||[]).length>0) ? `${data[0]?.text||''} (${data[0]?.dataAssetAndSubDirCount})` : '', text: ((data||[]).length>0) ? `${data[0]?.text||''} (${data[0]?.dataAssetAndSubDirCount})` : '',
left: 'center', left: 'center',
bottom: 0, bottom: 10,
textStyle: { textStyle: {
fontSize: 14 fontSize: 16
} }
}, },
tooltip: { tooltip: {
...@@ -117,20 +99,22 @@ const init = (ctx) => function (container, data, onClick) { ...@@ -117,20 +99,22 @@ const init = (ctx) => function (container, data, onClick) {
}, },
series: [ series: [
{ {
name: ((data||[]).length>0) ? `${data[0]?.text||''} (${data[0]?.dataAssetAndSubDirCount})` : '',
type: 'treemap', type: 'treemap',
height: '85%', height: '85%',
visibleMin: 0, visibleMin: 1,
childrenVisibleMin: 0, childrenVisibleMin: 1,
label: { label: {
show: true, show: true,
formatter: '{b}' formatter: '{b}'
}, },
upperLabel: { upperLabel: {
show: true, show: true,
height: 30 height: 30,
color: '#fff',
}, },
itemStyle: { itemStyle: {
borderColor: '#fff' borderColor: '#555'
}, },
breadcrumb: { breadcrumb: {
show: false, show: false,
...@@ -144,7 +128,6 @@ const init = (ctx) => function (container, data, onClick) { ...@@ -144,7 +128,6 @@ const init = (ctx) => function (container, data, onClick) {
myChart.setOption(option); myChart.setOption(option);
myChart.on('click', function (params) { myChart.on('click', function (params) {
console.log(params);
const { data } = params; const { data } = params;
if (onClick && data && data.nodeId) { if (onClick && data && data.nodeId) {
......
import React, { useState } from 'react'; import React, { useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons'; import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import { ResizableBox } from 'react-resizable';
import AssetTree from '../AssetManage/Component/AssetTree'; import AssetTree from '../AssetManage/Component/AssetTree';
import AssetDirectory from '../AssetManage/Component/AssetDirectory'; import AssetDirectory from '../AssetManage/Component/AssetDirectory';
...@@ -16,16 +17,19 @@ const AssetBrowse = (props) => { ...@@ -16,16 +17,19 @@ const AssetBrowse = (props) => {
const { reference = AssetBrowseReference } = 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 [ expandTree, setExpandTree ] = useState(true);
const [ expandRelation, setExpandRelation ] = useState(true); const [ expandRelation, setExpandRelation ] = useState(true);
const [ assetCount, setAssetCount ] = useState(0); const [ assetCount, setAssetCount ] = useState(0);
const [ resizeRelation, setResizeRelation ] = useState(false); const [ resizeRelation, setResizeRelation ] = useState(false);
const [ assetFullScreen, setAssetFullScreen ] = useState(false);
const [ nodeAllowdLoadDataAsset, setNodeAllowdLoadDataAsset ] = useState(false);
const { centerId, expandId } = nodeParams; const { centerId, expandId } = nodeParams;
const onTreeSelect = (value, type) => { const onTreeSelect = (value, type, allowdLoadDataAsset) => {
setNodeParams({ centerId: value||'', expandId: '' }); setNodeParams({ centerId: value, expandId: '', nodeType: type });
setNodeAllowdLoadDataAsset(allowdLoadDataAsset);
} }
const treeToggleClick = () => { const treeToggleClick = () => {
...@@ -46,48 +50,55 @@ const AssetBrowse = (props) => { ...@@ -46,48 +50,55 @@ const AssetBrowse = (props) => {
setAssetCount(count); setAssetCount(count);
} }
let nodeId = ''; const onFullScreenChange = (value) => {
if ((expandId||'') !== '') { setAssetFullScreen(value);
nodeId = expandId;
} else {
nodeId = centerId;
} }
const classes = classNames('asset-browse', { const classes = classNames('asset-browse', {
'asset-browse-tree-collapse': !expandTree, 'asset-browse-tree-collapse': !expandTree,
'asset-browse-relation-collapse': !expandRelation, 'asset-browse-relation-collapse': !expandRelation,
}); });
const rightClasses = classNames('right', {
'right-fullscreen': assetFullScreen
});
return ( return (
<div className={classes}> <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} /> <AssetTree centerId={centerId} onSelect={onTreeSelect} reference={reference} {...props} />
</div> </ResizableBox>
{ {
expandTree && <Separate width={15} /> expandTree && <Separate width={15} />
} }
<div className='right'> <div className={rightClasses}>
<AssetDirectory id={nodeId} assetCount={assetCount} reference={reference} /> {/* <AssetDirectory id={centerId} assetCount={assetCount} reference={reference} nodeType={nodeParams.nodeType} />
<Separate height={15} /> <Separate height={15} /> */}
<div className='flex' style={{ flex: 1, height: '100%', overflow: 'hidden' }}> <div className='flex' style={{ flex: 1, height: '100%', overflow: 'hidden' }}>
{ {
expandRelation && <React.Fragment> // expandRelation && <React.Fragment>
<div style={{ flex: 1, height: '100%', overflow: 'hidden' }}> // <div style={{ flex: 1, height: '100%', overflow: 'hidden' }}>
<RelationContainer reference={reference} nodeParams={nodeParams} onChange={onRelationChange} resize={resizeRelation} /> // <RelationContainer reference={reference} nodeParams={nodeParams} onChange={onRelationChange} resize={resizeRelation} />
</div> // </div>
<Separate width={15} /> // <Separate width={15} />
</React.Fragment> // </React.Fragment>
} }
<div style={{ flex: 1, overflow: 'hidden' }}> <div style={{ flex: 1, overflow: 'hidden' }}>
<AssetTable nodeId={nodeId} reference={reference} onCountChange={onAssetCountChange} {...props} /> <AssetTable nodeId={centerId} nodeType={nodeParams.nodeType} nodeAllowdLoadDataAsset={nodeAllowdLoadDataAsset} reference={reference} onCountChange={onAssetCountChange} onFullScreenChange={onFullScreenChange} {...props} />
</div> </div>
</div> </div>
</div> <div className='tree-toggle' onClick={treeToggleClick}>
<div className='tree-toggle' onClick={treeToggleClick}>
{ expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> } { expandTree ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div> </div>
<div className='relation-toggle' onClick={relationToggleClick}> {/* <div className='relation-toggle' onClick={relationToggleClick}>
{ expandRelation ? <CaretLeftOutlined /> : <CaretRightOutlined /> } { expandRelation ? <CaretLeftOutlined /> : <CaretRightOutlined /> }
</div> */}
</div> </div>
</div> </div>
) )
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
height: 100%; height: 100%;
.left { .left {
width: 230px; flex: 0 0 auto;
border-right: 1px solid #EFEFEF; border-right: 1px solid #EFEFEF;
overflow: hidden; overflow: hidden;
} }
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
right: 0; right: 0;
background: #f2f5fc; background: #f2f5fc;
position: absolute; position: absolute;
left: 245px; left: 0px;
top: calc(50% - 40px); top: calc(50% - 40px);
width: 12px; width: 12px;
height: 80px; height: 80px;
...@@ -29,18 +29,24 @@ ...@@ -29,18 +29,24 @@
} }
.right { .right {
position: relative;
display: flex; display: flex;
width: calc(100% - 245px); flex: 1;
overflow: hidden;
flex-direction: column; flex-direction: column;
} }
.right-fullscreen {
position: static;
}
.relation-toggle { .relation-toggle {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: #f2f5fc; background: #f2f5fc;
position: absolute; position: absolute;
left: calc(50% + 130px); left: calc(50% + 7.5px);
top: calc(50% - 40px); top: calc(50% - 40px);
width: 12px; width: 12px;
height: 80px; height: 80px;
...@@ -52,24 +58,20 @@ ...@@ -52,24 +58,20 @@
&.asset-browse-relation-collapse { &.asset-browse-relation-collapse {
.relation-toggle { .relation-toggle {
left: 245px; left: 0px;
} }
} }
} }
.asset-browse-tree-collapse { .asset-browse-tree-collapse {
.left { .left {
width: 0; width: 0 !important;
} }
.tree-toggle { .tree-toggle {
left: 0; left: 0;
} }
.right {
width: 100%;
}
.relation-toggle { .relation-toggle {
left: calc(50% + 7.5px); 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 React, { useState, useRef } from 'react';
import { Modal, Form, Space, Button } from 'antd'; import { Modal, Form, Space, Button, Spin } from 'antd';
import { showMessage } from '../../../../util'; import { showMessage } from '../../../../util';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import AssetAction from './AssetAction'; import AssetAction from './AssetAction';
import { getTemplateType } from '../../../../util/axios';
import './AddAssetModel.less';
const AddAssetModel = (props) => { const AddAssetModel = (props) => {
const { onCancel, visible, nodeId } = props; const { onCancel, visible, nodeId } = props;
const [ confirmLoading, setConfirmLoading ] = useState(false); const [ waiting, setWaiting ] = useState(false);
const [ metadataId, setMetadataId ] = useState('');
const [ elements, setElements ] = useState([]); const [ elements, setElements ] = useState([]);
const [postRelations, setPostRelations] = useState();
const [ form ] = Form.useForm(); const [ form ] = Form.useForm();
...@@ -19,14 +17,19 @@ const AddAssetModel = (props) => { ...@@ -19,14 +17,19 @@ const AddAssetModel = (props) => {
setElements(value); setElements(value);
} }
const onMetadataChange = (value) => { const onPostRelationsChange = (value) => {
setMetadataId(value); setPostRelations(value);
}
const close = (refresh = false) => {
setWaiting(false)
form?.resetFields()
onCancel?.(refresh)
} }
const onOk = async() => { const onOk = async() => {
try { try {
const row = await form?.validateFields(); const row = await form?.validateFields();
const newElements = [...elements]; const newElements = [...elements];
(newElements||[]).forEach(element => { (newElements||[]).forEach(element => {
if (row.hasOwnProperty(element.name)) { if (row.hasOwnProperty(element.name)) {
...@@ -34,63 +37,84 @@ const AddAssetModel = (props) => { ...@@ -34,63 +37,84 @@ const AddAssetModel = (props) => {
} }
}); });
const params = { setWaiting(true);
dirId: nodeId,
}
if ((metadataId||'')!=='') {
params.metadataId = metadataId;
}
setConfirmLoading(true);
dispatch({ dispatch({
type: 'assetmanage.addOrUpdateDataAsset', type: 'assetmanage.checkCodeIsExist',
payload: { payload: {
params,
data: { elements: newElements } data: { elements: newElements }
}, },
callback: () => { callback: isExist => {
setConfirmLoading(false); if (isExist === 'true') {
showMessage("success","新增成功"); setWaiting(false);
onCancel && onCancel(true); 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: () => {
showMessage("success","新增成功");
close(true)
},
error: () => {
setWaiting(false);
}
})
},
error: () => {
setWaiting(false);
}
})
}
}, },
error: () => { error: () => {
setConfirmLoading(false); setWaiting(false);
} }
}) })
} catch (errInfo) { } catch (errInfo) {
console.log('Validate Failed:', errInfo); console.log('Validate Failed:', errInfo);
setConfirmLoading(false); setWaiting(false);
} }
} }
return ( return (
<Modal <Modal
forceRender destroyOnClose
className='asset-add' className='asset-add'
title='新增资产信息' title='新增资产目录信息'
visible={ visible } visible={ visible }
width={ 600 } centered
onCancel={() => { onCancel && onCancel(); } } width='80%'
onCancel={() => { close() } }
footer={ footer={
<Space> <Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button> <Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={ onOk } loading={ confirmLoading }>确定</Button> <Button type="primary" onClick={ onOk } disabled={ waiting }>确定</Button>
</Space> </Space>
} }
bodyStyle={{ padding: '10px' }} bodyStyle={{ overflowX: 'auto', height: '80vh' }}
> >
{ <Spin spinning={waiting}>
visible && <AssetAction <AssetAction
form={form} form={form}
dirId={nodeId} dirId={nodeId}
action='add' action='add'
onMetadataChange={onMetadataChange} onElementsChange={onElementsChange}
onElementsChange={onElementsChange} onPostRelationsChange={onPostRelationsChange}
/> />
} </Spin>
</Modal> </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'; ...@@ -2,26 +2,65 @@ import React from 'react';
import { Drawer, Form } from 'antd'; import { Drawer, Form } from 'antd';
import AssetAction from './AssetAction'; import AssetAction from './AssetAction';
import { AssetDraftReference } from '../../../../util/constant';
import { dispatch } from '../../../../model';
const AssetDetailDrawer = (props) => { 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 [ 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 ( return (
<Drawer <Drawer
forceRender
visible={ visible } visible={ visible }
title='资产详情' title={title}
width={600} width='80%'
placement="right" placement="right"
closable={ true } closable={ true }
destroyOnClose
onClose={() => { onClose={() => {
setAnimated(true);
onCancel && onCancel(); 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> </Drawer>
) )
......
import React, { useEffect, useState } from "react"; 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 { getQueryParam } from '../../../../util';
import { dispatch } from '../../../../model';
import './AssetDetailPage.less'; import './AssetDetailPage.less';
import { getDataAssetDetail } from "../../../../service/dataassetmanager";
import { appId } from "../../../../App";
import { AssetBrowseReference, AssetDraftReference } from "../../../../util/constant";
const AssetDetailPage = (props)=>{ 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(() => { useEffect(() => {
const _id = getQueryParam('id', props.location.search); const _id = getQueryParam('id', props.location?.search);
const _dirId = getQueryParam('dirId', props.location.search); const _dirId = getQueryParam('dirId', props.location?.search);
setData({ id: _id, dirId: _dirId }); 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 //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( return(
<div className='asset-detail position-relative'> <div className='asset-detail position-relative'>
<div className='detail-header'> <div className='detail-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>资产详情</span> <span style={{ fontSize: 16, fontWeight: 'bold', color: '#000' }}>{title}</span>
</div> </div>
<div className='detail-container'> <Spin spinning={loading}>
<div className='detail-container-card'> <div className='detail-container'>
<AssetDetail id={id} dirId={dirId} /> <div className='detail-container-card'>
{
templateType && <AssetAction id={id} dirId={dirId} reference={(isDraft==='true')?AssetDraftReference:AssetBrowseReference} action='detail' catalog={catalog} readonly={true} />
}
</div>
</div> </div>
</div> </Spin>
</div> </div>
) )
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
width: 100%; width: 100%;
height: 44px; height: 44px;
padding: 0 15px; padding: 0 15px;
background-color: #464d6e; background-color: white;
align-items: center; align-items: center;
position: fixed; position: fixed;
justify-content: space-between; justify-content: space-between;
......
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Spin, Tooltip, Typography, Dropdown, Menu } from 'antd'; import { Spin, Tooltip, Typography, Dropdown, Menu } from 'antd';
import LocalStorage from 'local-storage';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import { AssetManageReference, ResourceBrowseReference } from '../../../../util/constant'; import { AssetBrowseReference, AssetManageReference, ResourceBrowseReference } from '../../../../util/constant';
import ImportElement from './ImportElement'; import ImportElement from './ImportElement';
import ExportElement from './ExportElement';
import AttributeRelationModal from "./AttributeRelationModal"; import AttributeRelationModal from "./AttributeRelationModal";
import FilterElementModal from './FilterElementModal'; import FilterElementModal from './FilterElementModal';
import { showNotifaction } from '../../../../util'; import { showNotifaction } from '../../../../util';
...@@ -14,15 +12,15 @@ import Separate from './Separate'; ...@@ -14,15 +12,15 @@ import Separate from './Separate';
import record from '../Assets/record.png'; import record from '../Assets/record.png';
import './AssetDirectory.less'; import './AssetDirectory.less';
import { getTemplateType } from '../../../../util/axios';
const { Paragraph, Text } = Typography; const { Paragraph, Text } = Typography;
const AssetDirectory = (props) => { 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 [ dir, setDir ] = useState(null);
const [ loading, setLoading ] = useState(false); const [ loading, setLoading ] = useState(false);
const [ importElementVisible, setImportElementVisible ] = useState(false); const [ importElementVisible, setImportElementVisible ] = useState(false);
const [ exportElementVisible, setExportElementVisible ] = useState(false);
const [ attributeRelationModalVisible, setAttributeRelationModalVisible ] = useState(false); const [ attributeRelationModalVisible, setAttributeRelationModalVisible ] = useState(false);
const [ filterElementVisible, setFilterElementVisible ] = useState(false); const [ filterElementVisible, setFilterElementVisible ] = useState(false);
const [ resourceState, setResourceState ] = useState(null); const [ resourceState, setResourceState ] = useState(null);
...@@ -47,14 +45,21 @@ const AssetDirectory = (props) => { ...@@ -47,14 +45,21 @@ const AssetDirectory = (props) => {
useEffect(() => { useEffect(() => {
if ((id||'')!=='') { if ((id||'')!=='') {
getDirectory(); getDirectory();
} else {
setDir()
} }
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [ id, directoryChanged ]) }, [ id, directoryChanged ])
const getDirectory = () => { const getDirectory = () => {
let url = 'assetmanage.getDirectoryById';
if ((reference===AssetBrowseReference|| reference===ResourceBrowseReference) && nodeType==='custom') {
url = 'assetmanage.getPersonalCustomDirectoryById';
}
setLoading(true); setLoading(true);
dispatch({ dispatch({
type: 'assetmanage.getDirectoryById', type: url,
payload: { payload: {
dirId: id dirId: id
}, },
...@@ -93,7 +98,7 @@ const AssetDirectory = (props) => { ...@@ -93,7 +98,7 @@ const AssetDirectory = (props) => {
} }
const onExportElementBtnClick = () => { const onExportElementBtnClick = () => {
setExportElementVisible(true); window.open(`/api/dataassetmanagertest/elementApi/export?templateType=${getTemplateType()}`);
} }
const onFilterElementClick = () => { const onFilterElementClick = () => {
...@@ -116,10 +121,6 @@ const AssetDirectory = (props) => { ...@@ -116,10 +121,6 @@ const AssetDirectory = (props) => {
} }
} }
const onExportElementCancel = () => {
setExportElementVisible(false);
}
const onFilterElementModalCancel = () => { const onFilterElementModalCancel = () => {
setFilterElementVisible(false); setFilterElementVisible(false);
} }
...@@ -132,22 +133,22 @@ const AssetDirectory = (props) => { ...@@ -132,22 +133,22 @@ const AssetDirectory = (props) => {
<Menu> <Menu>
<Menu.Item> <Menu.Item>
<div className='text-center' onClick={onImportElementBtnClick}> <div className='text-center' onClick={onImportElementBtnClick}>
导入资产属性 导入资产目录属性
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<div className='text-center' onClick={onExportElementBtnClick}> <div className='text-center' onClick={onExportElementBtnClick}>
导出资产属性 导出资产目录属性
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<div className='text-center' onClick={onFilterElementClick}> <div className='text-center' onClick={onFilterElementClick}>
资产属性管理 资产目录浏览管理
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<div className='text-center' onClick={onAttributeRelationBtnClick}> <div className='text-center' onClick={onAttributeRelationBtnClick}>
资产属性关联 资产目录属性关联
</div> </div>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
...@@ -185,7 +186,7 @@ const AssetDirectory = (props) => { ...@@ -185,7 +186,7 @@ const AssetDirectory = (props) => {
</Tooltip> </Tooltip>
</Paragraph> </Paragraph>
</div> </div>
<div className={(reference===AssetManageReference || reference===ResourceBrowseReference)?'mb-common':''}> <div className={(reference===AssetManageReference)?'mb-common':''}>
<Paragraph> <Paragraph>
<Tooltip title={dir?.desc||''}> <Tooltip title={dir?.desc||''}>
<Text className='title-color' ellipsis={true}> <Text className='title-color' ellipsis={true}>
...@@ -196,50 +197,46 @@ const AssetDirectory = (props) => { ...@@ -196,50 +197,46 @@ const AssetDirectory = (props) => {
</Paragraph> </Paragraph>
</div> </div>
{ {
(reference===AssetManageReference || reference===ResourceBrowseReference) && <div className='flex'> (reference===AssetManageReference) && <div className='flex'>
{ <Paragraph style={{ flex: 1, overflow: 'hidden' }}>
(reference === AssetManageReference) && <React.Fragment> <Tooltip title={dir?.remarks||''}>
<Paragraph style={{ flex: 1, overflow: 'hidden' }}> <Text className='title-color' ellipsis={true}>
<Tooltip title={dir?.remarks||''}> 备注:
<Text className='title-color' ellipsis={true}> <Text className='text-color'>{dir?.remarks||''}</Text>
备注: </Text>
<Text className='text-color'>{dir?.remarks||''}</Text> </Tooltip>
</Text> </Paragraph>
</Tooltip> <Separate width={10} background='#fff' />
</Paragraph> {/* {
<Separate width={10} background='#fff' /> resourceState && <React.Fragment>
</React.Fragment> <Paragraph style={{ flex: 1, overflow: 'hidden' }}>
} <Tooltip title={resourceState?.relatedAsset}>
{ <Text className='title-color' ellipsis={true}>
(reference===ResourceBrowseReference || resourceState) && <React.Fragment> 资产化数量:
<Paragraph style={{ flex: 1, overflow: 'hidden' }}> <Text className='text-color'>{resourceState?.relatedAsset}</Text>
<Tooltip title={resourceState?.relatedAsset}> </Text>
<Text className='title-color' ellipsis={true}> </Tooltip>
资产化数量: </Paragraph>
<Text className='text-color'>{resourceState?.relatedAsset}</Text> <Separate width={10} background='#fff' />
</Text> <Paragraph style={{ flex: 1, overflow: 'hidden' }}>
</Tooltip> <Tooltip title={resourceState?.notRelatedAsset}>
</Paragraph> <Text className='title-color' ellipsis={true}>
<Separate width={10} background='#fff' /> 非资产数量:
<Paragraph style={{ flex: 1, overflow: 'hidden' }}> <Text className='text-color'>{resourceState?.notRelatedAsset}</Text>
<Tooltip title={resourceState?.notRelatedAsset}> </Text>
<Text className='title-color' ellipsis={true}> </Tooltip>
非资产数量: </Paragraph>
<Text className='text-color'>{resourceState?.notRelatedAsset}</Text> <Separate width={10} background='#fff' />
</Text> <Paragraph style={{ flex: 1, overflow: 'hidden' }}>
</Tooltip> <Tooltip title={resourceState?.uncombed}>
</Paragraph> <Text className='title-color' ellipsis={true}>
<Separate width={10} background='#fff' /> 未梳理数量:
<Paragraph style={{ flex: 1, overflow: 'hidden' }}> <Text className='text-color'>{resourceState?.uncombed}</Text>
<Tooltip title={resourceState?.uncombed}> </Text>
<Text className='title-color' ellipsis={true}> </Tooltip>
未梳理数量: </Paragraph>
<Text className='text-color'>{resourceState?.uncombed}</Text> </React.Fragment>
</Text> } */}
</Tooltip>
</Paragraph>
</React.Fragment>
}
</div> </div>
} }
</div> </div>
...@@ -266,7 +263,7 @@ const AssetDirectory = (props) => { ...@@ -266,7 +263,7 @@ const AssetDirectory = (props) => {
</div> </div>
</div> </div>
{ {/* {
(reference===AssetManageReference) && <Dropdown overlay={elementManageMenu} placement="bottomCenter"> (reference===AssetManageReference) && <Dropdown overlay={elementManageMenu} placement="bottomCenter">
<div <div
className='flex more-container' className='flex more-container'
...@@ -281,7 +278,7 @@ const AssetDirectory = (props) => { ...@@ -281,7 +278,7 @@ const AssetDirectory = (props) => {
<span style={{ fontSize: 10 }}>更多</span> <span style={{ fontSize: 10 }}>更多</span>
</div> </div>
</Dropdown> </Dropdown>
} } */}
</div> </div>
...@@ -290,11 +287,6 @@ const AssetDirectory = (props) => { ...@@ -290,11 +287,6 @@ const AssetDirectory = (props) => {
onCancel={onImportElementCancel} onCancel={onImportElementCancel}
/> />
<ExportElement
visible={exportElementVisible}
onCancel={onExportElementCancel}
/>
<FilterElementModal <FilterElementModal
visible={ filterElementVisible } visible={ filterElementVisible }
onCancel={ onFilterElementModalCancel} onCancel={ onFilterElementModalCancel}
......
...@@ -2,20 +2,20 @@ ...@@ -2,20 +2,20 @@
.asset-directory { .asset-directory {
// .more-container { .more-container {
// cursor: pointer; cursor: pointer;
// color: @icon-normal-color; color: @icon-normal-color;
// .icon { .icon {
// fill: @icon-normal-color !important; fill: @icon-normal-color !important;
// } }
// &:hover { &:hover {
// color: @icon-hover-color; color: @icon-hover-color;
// .icon { .icon {
// fill: @icon-hover-color !important; 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'; ...@@ -3,6 +3,7 @@ import { Modal, Form, Input, Space, Button, Select } from 'antd';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import { AppContext } from '../../../../App'; import { AppContext } from '../../../../App';
import MetadataInfo from './MetadataInfo'; import MetadataInfo from './MetadataInfo';
import { showMessage } from '../../../../util';
const { Option } = Select; const { Option } = Select;
...@@ -48,7 +49,7 @@ const DirsSelect = ({ value = [], data = [], onChange, ...restProps }) => { ...@@ -48,7 +49,7 @@ const DirsSelect = ({ value = [], data = [], onChange, ...restProps }) => {
const AssetTagModal = (props) => { const AssetTagModal = (props) => {
const { onCancel, visible, id, keyword, creator } = props; const { onCancel, visible, id, tag, creator } = props;
const [ form ] = Form.useForm(); const [ form ] = Form.useForm();
const [ confirmLoading, setConfirmLoading ] = useState(false); const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ elements, setElements ] = useState([]); const [ elements, setElements ] = useState([]);
...@@ -69,12 +70,12 @@ const AssetTagModal = (props) => { ...@@ -69,12 +70,12 @@ const AssetTagModal = (props) => {
useEffect(() => { useEffect(() => {
if (visible && (keyword||'')!=='') { if (visible && (tag?.name||'')!=='') {
getDirectoryByName(keyword); getDirectoryByName(tag?.name);
} }
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [ visible, keyword ]) }, [ visible ])
const getAsset = () => { const getAsset = () => {
dispatch({ dispatch({
...@@ -140,7 +141,7 @@ const AssetTagModal = (props) => { ...@@ -140,7 +141,7 @@ const AssetTagModal = (props) => {
}); });
const params = { const params = {
dirId: row.tags.join(','), dirId: (row.dirs||[]).join(','),
} }
if ((metadataId||'')!=='') { if ((metadataId||'')!=='') {
...@@ -150,50 +151,51 @@ const AssetTagModal = (props) => { ...@@ -150,50 +151,51 @@ const AssetTagModal = (props) => {
setConfirmLoading(true); setConfirmLoading(true);
dispatch({ dispatch({
type: 'assetmanage.addOrUpdateDataAsset', type: 'assetmanage.checkCodeIsExist',
payload: { payload: {
params, data: { ...asset, elements: newElements }
data: { ...asset, elements: newElements }
}, },
callback: () => { callback: isExist => {
const nameList = []; if (isExist === 'true') {
(dirs||[]).forEach(dir=>{ setConfirmLoading(false);
if (row.tags.indexOf(dir.id) !== -1) { showMessage('warn', '已存在相同的资产目录编号,请重新输入');
nameList.push(dir.path); } else {
} dispatch({
}) type: 'assetmanage.addOrUpdateDataAsset',
dispatch({ payload: {
type: 'tag.getTagIdListByNameList', params,
payload: { data: { ...asset, elements: newElements }
params: { },
nameList: nameList.join(',') callback: data => {
} const ids = [id];
}, if (data && ((data?.id||'')!=='') && (data?.id!==id)) {
callback: data => { ids.push(data?.id);
dispatch({
type: 'tag.batchAddTagResourceByTagList',
payload: {
params: {
tagIds: (data||[]).join(','),
resourceIds: id,
type: 'dataAsset',
creator
}
},
callback: () => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
} }
})
}, dispatch({
error: () => { type: 'tag.batchAddTagResourceByTagList',
setConfirmLoading(false); payload: {
} params: {
}) tagIds: tag?.tagId,
resourceIds: ids.join(','),
type: 'dataAsset',
creator
}
},
callback: () => {
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
},
error: () => {
setConfirmLoading(false);
}
})
}
}, },
error: () => { error: () => {
setConfirmLoading(false); setConfirmLoading(false);
...@@ -226,7 +228,7 @@ const AssetTagModal = (props) => { ...@@ -226,7 +228,7 @@ const AssetTagModal = (props) => {
<Modal <Modal
forceRender forceRender
className='asset-add' className='asset-add'
title='资产打标签' title='资产目录打标签'
visible={ visible } visible={ visible }
width={ 600 } width={ 600 }
onCancel={() => { onCancel={() => {
...@@ -259,8 +261,14 @@ const AssetTagModal = (props) => { ...@@ -259,8 +261,14 @@ const AssetTagModal = (props) => {
return ( return (
<> <>
<Form.Item <Form.Item
label='资产标签' label='资产目录标签'
name='tags' name='tag'
>
<span>{tag?.name||''}</span>
</Form.Item>
<Form.Item
label='挂载目录'
name='dirs'
required={true} required={true}
> >
<DirsSelect data={dirs||[]} /> <DirsSelect data={dirs||[]} />
......
...@@ -7,25 +7,11 @@ ...@@ -7,25 +7,11 @@
} }
.yy-tree{ .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; overflow: auto !important;
} }
.site-tree-search-value { .site-tree-search-value {
color: #f50; color: #f50;
} }
}
.asset-tree-read-only {
.yy-tree {
height: calc(100vh - @header-height - @breadcrumb-height - 25px - 62px) !important;;
overflow: auto !important;
}
}
.asset-tree-asset-mount-reference {
.yy-tree {
height: 400px !important;
overflow: auto !important;
}
} }
\ No newline at end of file
...@@ -389,7 +389,7 @@ const AttributeRelationModal = (props) => { ...@@ -389,7 +389,7 @@ const AttributeRelationModal = (props) => {
return ( return (
<Modal <Modal
forceRender forceRender
title={'资产属性关联'} title={'资产目录属性关联'}
visible={visible} visible={visible}
width={1000} width={1000}
onCancel={() => { 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 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 { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper'; import update from 'immutability-helper';
...@@ -7,11 +7,18 @@ import update from 'immutability-helper'; ...@@ -7,11 +7,18 @@ import update from 'immutability-helper';
import DragTag from './DragTag'; import DragTag from './DragTag';
import PreviewTree from './PreviewTree'; import PreviewTree from './PreviewTree';
import { dispatch, dispatchLatest } from '../../../../model'; import { dispatch, dispatchLatest } from '../../../../model';
import { AssetManageReference, AssetBrowseReference, ResourceBrowseReference } from '../../../../util/constant';
import { showMessage } from '../../../../util'; import { showMessage } from '../../../../util';
const resourceTypes = [
{ key: 'innerSource', name: '内部资源' },
{ key: 'outerSource', name: '外部资源' },
{ key: 'dataAsset', name: '资产' },
]
const CustomDirectoryModal = (props) => { const CustomDirectoryModal = (props) => {
const { visible, onCancel, action, dirId } = props; const { visible, onCancel, action, dirId, reference = AssetManageReference } = props;
const [ keyword, setKeyword ] = useState(''); const [ keyword, setKeyword ] = useState('');
const [ data, setData ] = useState([]); const [ data, setData ] = useState([]);
...@@ -67,11 +74,33 @@ const CustomDirectoryModal = (props) => { ...@@ -67,11 +74,33 @@ const CustomDirectoryModal = (props) => {
} }
const getPreviewTreeData = () => { const getPreviewTreeData = () => {
if ((checkedValues||[]).length === 0) {
return;
}
let url = 'assetmanage.previewTreeByCustomElements';
let payload = {
data: checkedValues
}
if (reference===AssetManageReference) {
payload.params = {
resourceTypes: (form.getFieldValue('resourceTypes')||[]).join(',')
};
}
if (reference===AssetBrowseReference || reference===ResourceBrowseReference) {
url = 'assetmanage.previewTreeByCustomElementsAndResourceType';
payload.params = {
resourceType: (reference===ResourceBrowseReference)?'resource':'dataAsset',
}
}
dispatchLatest({ dispatchLatest({
type: 'assetmanage.previewTreeByCustomElements', type: url,
payload: { payload,
data: checkedValues
},
callback: data => { callback: data => {
setPreviewTreeData((data||[]).length>0?data[0]:{}); setPreviewTreeData((data||[]).length>0?data[0]:{});
} }
...@@ -98,6 +127,12 @@ const CustomDirectoryModal = (props) => { ...@@ -98,6 +127,12 @@ const CustomDirectoryModal = (props) => {
setCheckedData(_checkedData); setCheckedData(_checkedData);
} }
const onValuesChange = (changedValues, allValues) => {
if (changedValues.hasOwnProperty('resourceTypes')) {
getPreviewTreeData();
}
}
const reset = () => { const reset = () => {
setConfirmLoading(false); setConfirmLoading(false);
setKeyword(''); setKeyword('');
...@@ -127,8 +162,23 @@ const CustomDirectoryModal = (props) => { ...@@ -127,8 +162,23 @@ const CustomDirectoryModal = (props) => {
payload.params = {...payload.params, dirId}; 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({ dispatch({
type: 'assetmanage.saveTreeByCustomElements', type: url,
payload, payload,
callback: data => { callback: data => {
setConfirmLoading(false); setConfirmLoading(false);
...@@ -194,6 +244,7 @@ const CustomDirectoryModal = (props) => { ...@@ -194,6 +244,7 @@ const CustomDirectoryModal = (props) => {
title={action==='add'?'新增自定义':'修改自定义'} title={action==='add'?'新增自定义':'修改自定义'}
visible={visible} visible={visible}
width={800} width={800}
centered
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={() => { onCancel={() => {
reset(); reset();
...@@ -203,10 +254,10 @@ const CustomDirectoryModal = (props) => { ...@@ -203,10 +254,10 @@ const CustomDirectoryModal = (props) => {
> >
<Row gutter={30}> <Row gutter={30}>
<Col span={8} style={{ borderRight: '1px solid #EFEFEF' }}> <Col span={8} style={{ borderRight: '1px solid #EFEFEF' }}>
<div className='mb-3'>资产要素</div> <div className='mb-3'>资产目录要素</div>
<Input <Input
value={keyword} value={keyword}
placeholder='请输入资产要素' placeholder='请输入资产目录要素'
style={{ marginBottom:10 }} style={{ marginBottom:10 }}
onChange={(e) => { onSearchChange(e) }} onChange={(e) => { onSearchChange(e) }}
/> />
...@@ -244,7 +295,7 @@ const CustomDirectoryModal = (props) => { ...@@ -244,7 +295,7 @@ const CustomDirectoryModal = (props) => {
</div> </div>
</> </>
} }
<Form className='mt-5' {...formItemLayout} form={form}> <Form className='mt-5' {...formItemLayout} form={form} onValuesChange={onValuesChange}>
<Form.Item <Form.Item
label='命名' label='命名'
name='name' name='name'
...@@ -252,6 +303,20 @@ const CustomDirectoryModal = (props) => { ...@@ -252,6 +303,20 @@ const CustomDirectoryModal = (props) => {
> >
<Input /> <Input />
</Form.Item> </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 <Form.Item
label='描述或原因' label='描述或原因'
name='desc' 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'; ...@@ -3,6 +3,7 @@ import { Row, Col, Checkbox, Typography, Button, Switch, Modal } from 'antd';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import './FilterElementModal.less'; import './FilterElementModal.less';
import { AssetManageReference } from '../../../../util/constant';
//type //type
//global 全局设置 //global 全局设置
...@@ -11,7 +12,7 @@ import './FilterElementModal.less'; ...@@ -11,7 +12,7 @@ import './FilterElementModal.less';
const FilterElementModal = (props) => { const FilterElementModal = (props) => {
const { visible, onCancel, type = 'global' } = props; const { visible, onCancel, type = 'global', reference = AssetManageReference } = props;
const [ elements, setElements ] = useState([]); const [ elements, setElements ] = useState([]);
const [ typesOfElements, setTypesOfElements ] = useState([]); const [ typesOfElements, setTypesOfElements ] = useState([]);
const [ selectedKeys, setSelectedKeys ] = useState([]); const [ selectedKeys, setSelectedKeys ] = useState([]);
...@@ -28,14 +29,19 @@ const FilterElementModal = (props) => { ...@@ -28,14 +29,19 @@ const FilterElementModal = (props) => {
const getAllFilterElementIdsThenGetAllElements = () => { const getAllFilterElementIdsThenGetAllElements = () => {
let url = ''; let url = '';
let payload = null;
if (type === 'global') { if (type === 'global') {
url = 'assetmanage.listFilterElementIdsConfig'; url = 'assetmanage.listFilterElementIdsConfig';
} else { } else {
url = 'assetmanage.listFilterElementIds'; url = 'assetmanage.listFilterElementIds';
payload = {
isAdmin: (reference===AssetManageReference)
}
} }
dispatch({ dispatch({
type: url, type: url,
payload,
callback: data => { callback: data => {
setSelectedKeys(data||[]); setSelectedKeys(data||[]);
getAllElements(); getAllElements();
...@@ -123,7 +129,10 @@ const FilterElementModal = (props) => { ...@@ -123,7 +129,10 @@ const FilterElementModal = (props) => {
dispatch({ dispatch({
type: url, type: url,
payload: { payload: {
data: selectedKeys data: selectedKeys,
params: {
isAdmin: (reference===AssetManageReference)
}
}, },
callback: () => { callback: () => {
reset(); reset();
...@@ -148,7 +157,7 @@ const FilterElementModal = (props) => { ...@@ -148,7 +157,7 @@ const FilterElementModal = (props) => {
<Modal <Modal
forceRender forceRender
visible={visible} visible={visible}
title={type==='global'?'资产属性管理':'可见列设置'} title={type==='global'?'资产目录浏览管理':'可见列设置'}
width={520} width={520}
onCancel={cancel} onCancel={cancel}
footer={[ footer={[
......
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useContext } from 'react';
import { Button, Upload, Drawer, Table, Pagination, Divider, Form, Select } from 'antd'; import { Button, Upload, Drawer, Table, Pagination, Divider, Form, Typography } from 'antd';
import { UploadOutlined, DownloadOutlined } from '@ant-design/icons'; import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
import LocalStorage from 'local-storage';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import { showMessage, formatDate } from '../../../../util'; import { showMessage, formatDate } from '../../../../util';
import { AppContext } from '../../../../App';
import { getTemplateType } from '../../../../util/axios';
const ImportAssetDrawer = (props) => { const ImportAssetDrawer = (props) => {
const { onCancel, onSuccess, visible, nodeId } = props; const { onCancel, onSuccess, visible, nodeId } = props;
...@@ -16,8 +17,9 @@ const ImportAssetDrawer = (props) => { ...@@ -16,8 +17,9 @@ const ImportAssetDrawer = (props) => {
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } ); const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const { pageNum, pageSize } = pagination; const { pageNum, pageSize } = pagination;
const [ total, setTotal ] = useState(0); const [ total, setTotal ] = useState(0);
const [ resourceTypes, setResourceTypes ] = useState(undefined); const [ users, setUsers ] = useState([]);
const [ currentResourceType, setCurrentResourceType ] = useState(undefined);
const app = useContext(AppContext);
const columns = [ const columns = [
{ {
...@@ -60,6 +62,14 @@ const ImportAssetDrawer = (props) => { ...@@ -60,6 +62,14 @@ const ImportAssetDrawer = (props) => {
dataIndex: 'operator', dataIndex: 'operator',
width: 100, width: 100,
ellipsis: true, 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: '导入状态', title: '导入状态',
...@@ -72,41 +82,15 @@ const ImportAssetDrawer = (props) => { ...@@ -72,41 +82,15 @@ const ImportAssetDrawer = (props) => {
if (visible) { if (visible) {
setPagination({ pageNum: 1, pageSize: 20 }); setPagination({ pageNum: 1, pageSize: 20 });
getUsers();
getLogs(); getLogs();
getResourceTypes();
} }
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible]) }, [visible])
const getResourceTypes = () => {
dispatch({
type: 'assetmanage.getResourceTypes',
callback: data => {
setResourceTypes(data);
if ((data||[]).length > 0) {
setCurrentResourceType(data[0].code);
}
}
})
}
const downloadTemplate = () => { const downloadTemplate = () => {
if (!currentResourceType) { window.open(`/api/dataassetmanagertest/dataAssetApi/getImportTemplate?templateType=${getTemplateType()}`);
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}`);
}
})
} }
const getLogs = (p = 1, s = 20) => { const getLogs = (p = 1, s = 20) => {
...@@ -128,6 +112,15 @@ const ImportAssetDrawer = (props) => { ...@@ -128,6 +112,15 @@ const ImportAssetDrawer = (props) => {
}) })
} }
const getUsers = () => {
dispatch({
type: 'pds.getOwners',
callback: (data) => {
setUsers(data);
}
})
}
const uploadProps = { const uploadProps = {
onRemove: file => { onRemove: file => {
...@@ -154,30 +147,37 @@ const ImportAssetDrawer = (props) => { ...@@ -154,30 +147,37 @@ const ImportAssetDrawer = (props) => {
}; };
const handleOk = () => { const handleOk = () => {
if (!currentResourceType) {
showMessage('warn', '请先选择资产类型');
return;
}
if ((fileList||[]).length === 0) { if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模版上传'); showMessage('info', '请先选择文件上传');
return; return;
} }
setConfirmLoading(true); setConfirmLoading(true);
dispatch({ dispatch({
type: 'assetmanage.assetImport', type: 'assetmanage.checkIsNeedSaveAsDraft',
payload: { fileList: fileList, params: { dirId: nodeId, resourceType: currentResourceType } }, callback: (data) => {
callback: data => { const needSaveAsDraft = (data==='true')?true:false
setConfirmLoading(false);
setFileList([]); dispatch({
getLogs(pageNum, pageSize); type: 'assetmanage.assetImport',
onSuccess && onSuccess(data||''); 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: () => { error: () => {
setConfirmLoading(false); setConfirmLoading(false);
} }
}); })
} }
const reset = () => { const reset = () => {
...@@ -189,7 +189,7 @@ const ImportAssetDrawer = (props) => { ...@@ -189,7 +189,7 @@ const ImportAssetDrawer = (props) => {
<Drawer <Drawer
forceRender forceRender
visible={ visible } visible={ visible }
title='资产导入' title='资产目录导入'
width={900} width={900}
placement="right" placement="right"
closable={ true } closable={ true }
...@@ -198,11 +198,11 @@ const ImportAssetDrawer = (props) => { ...@@ -198,11 +198,11 @@ const ImportAssetDrawer = (props) => {
onCancel && onCancel(); onCancel && onCancel();
}} }}
> >
<div className='mt-3 flex' style={{ justifyContent: 'space-between' }}> <div className='mt-3'>
<Form layout='inline'> <Form layout='inline'>
<Form.Item label='Word上传:'> <Form.Item label='Excel导入:'>
<Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }> <Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }>
下载 下载
</Button> </Button>
<Upload style={{ display: 'inline' }} {...uploadProps }> <Upload style={{ display: 'inline' }} {...uploadProps }>
<Button icon={ <Button icon={
...@@ -214,21 +214,8 @@ const ImportAssetDrawer = (props) => { ...@@ -214,21 +214,8 @@ const ImportAssetDrawer = (props) => {
<Form.Item> <Form.Item>
<Button type='primary' onClick={handleOk} loading={confirmLoading}>确定导入</Button> <Button type='primary' onClick={handleOk} loading={confirmLoading}>确定导入</Button>
</Form.Item> </Form.Item>
<Button onClick={() => getLogs()} style={{ marginLeft: 'auto' }}>刷新日志</Button>
</Form> </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> </div>
<Divider orientation="left">导入日志</Divider> <Divider orientation="left">导入日志</Divider>
<Table <Table
...@@ -239,14 +226,17 @@ const ImportAssetDrawer = (props) => { ...@@ -239,14 +226,17 @@ const ImportAssetDrawer = (props) => {
pagination={false} pagination={false}
loading={loading} loading={loading}
expandable={{ 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 sticky
/> />
<Pagination <Pagination
className="text-center mt-3" className="text-center mt-3"
showSizeChanger showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => { onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum||1, pageSize: _pageSize || 20 }); setPagination({ pageNum: _pageNum||1, pageSize: _pageSize || 20 });
getLogs(_pageNum||1, _pageSize||20); getLogs(_pageNum||1, _pageSize||20);
...@@ -259,7 +249,6 @@ const ImportAssetDrawer = (props) => { ...@@ -259,7 +249,6 @@ const ImportAssetDrawer = (props) => {
pageSize={pageSize} pageSize={pageSize}
defaultCurrent={1} defaultCurrent={1}
total={total} total={total}
pageSizeOptions={[10,20]}
showTotal={total => ` ${total} `} showTotal={total => ` ${total} `}
/> />
</Drawer> </Drawer>
......
...@@ -4,6 +4,7 @@ import { UploadOutlined } from '@ant-design/icons'; ...@@ -4,6 +4,7 @@ import { UploadOutlined } from '@ant-design/icons';
import { dispatch } from '../../../../model'; import { dispatch } from '../../../../model';
import { showNotifaction } from '../../../../util'; import { showNotifaction } from '../../../../util';
import { getTemplateType } from '../../../../util/axios';
const ImportDirectory = (props) => { const ImportDirectory = (props) => {
const { visible, onCancel, dirId } = props; const { visible, onCancel, dirId } = props;
...@@ -41,7 +42,7 @@ const ImportDirectory = (props) => { ...@@ -41,7 +42,7 @@ const ImportDirectory = (props) => {
} }
const download = () => { const download = () => {
window.open("/data-govern/docs/AssetThemeModel.xlsx"); window.open(`/api/dataassetmanagertest/directoryApi/getImportTemplate?templateType=${getTemplateType()}`);
} }
const upload = async (ignoreRepeatPath = false) => { const upload = async (ignoreRepeatPath = false) => {
...@@ -51,23 +52,12 @@ const ImportDirectory = (props) => { ...@@ -51,23 +52,12 @@ const ImportDirectory = (props) => {
setUploading(true); setUploading(true);
let payload; const payload = {
if (row.type === 'root') { params: {
payload = { ignoreRepeatPath
params: { },
ignoreRepeatPath fileList: fileList,
}, };
fileList: fileList,
};
} else {
payload = {
params: {
ignoreRepeatPath,
parentPath: dir.path
},
fileList: fileList
};
}
dispatch({ dispatch({
type: 'assetmanage.directoryImport', type: 'assetmanage.directoryImport',
...@@ -143,7 +133,7 @@ const ImportDirectory = (props) => { ...@@ -143,7 +133,7 @@ const ImportDirectory = (props) => {
onCancel={() => { onCancel && onCancel() }} onCancel={() => { onCancel && onCancel() }}
footer={ footer={
<Space> <Space>
<Button type="primary" onClick={ download } >下载</Button> <Button type="primary" onClick={ download } >下载</Button>
<Button type="primary" onClick={() => { upload(false); } } loading={uploading}>上传</Button> <Button type="primary" onClick={() => { upload(false); } } loading={uploading}>上传</Button>
<Button onClick={() => { onCancel && onCancel() }}>返回</Button> <Button onClick={() => { onCancel && onCancel() }}>返回</Button>
</Space> </Space>
...@@ -152,13 +142,10 @@ const ImportDirectory = (props) => { ...@@ -152,13 +142,10 @@ const ImportDirectory = (props) => {
<Form {...formItemLayout} form={form}> <Form {...formItemLayout} form={form}>
<Form.Item <Form.Item
label="挂载位置" label="挂载位置"
name="type" // name="type"
rules={[{ required: true, message: '必填项'}]} rules={[{ required: true, message: '必填项'}]}
> >
<Radio.Group > <span>根节点导入</span>
<Radio value='root' >根节点导入</Radio>
<Radio value='self' disabled={ dirId===null }>选中节点导入</Radio>
</Radio.Group>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="文件" label="文件"
......
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { Button, Upload, Modal, Select, Space, Spin } from 'antd'; import { Button, Upload, Modal } from 'antd';
import { DownloadOutlined, UploadOutlined } from '@ant-design/icons'; import { DownloadOutlined, UploadOutlined } from '@ant-design/icons';
import { dispatchLatest, dispatch } from '../../../../model'; import { dispatchLatest } from '../../../../model';
import { showMessage } from '../../../../util'; import { showMessage } from '../../../../util';
import { getTemplateType } from '../../../../util/axios';
const ImportElement = (props) => { const ImportElement = (props) => {
const { onCancel, visible } = props; const { onCancel, visible } = props;
const [ fileList, setFileList ] = useState([]); const [ fileList, setFileList ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false); 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 = () => { const downloadTemplate = () => {
window.open("/data-govern/docs/ElementModel.xlsx"); window.open(`/api/dataassetmanagertest/elementApi/getImportTemplate?templateType=${getTemplateType()}`);
} }
const uploadProps = { const uploadProps = {
...@@ -61,19 +36,14 @@ const ImportElement = (props) => { ...@@ -61,19 +36,14 @@ const ImportElement = (props) => {
const handleOk = () => { const handleOk = () => {
if ((fileList||[]).length === 0) { if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模版上传'); showMessage('info', '请先选择文件上传');
return; return;
} }
setConfirmLoading(true); setConfirmLoading(true);
dispatchLatest({ dispatchLatest({
type: 'assetmanage.importElement', type: 'assetmanage.importElement',
payload: { payload: { fileList },
params: {
resourceType: currentResourceType
},
fileList
},
callback: data => { callback: data => {
setConfirmLoading(false); setConfirmLoading(false);
reset(); reset();
...@@ -87,15 +57,13 @@ const ImportElement = (props) => { ...@@ -87,15 +57,13 @@ const ImportElement = (props) => {
const reset = () => { const reset = () => {
setFileList([]); setFileList([]);
setLoading(false);
setConfirmLoading(false);
} }
return ( return (
<Modal <Modal
forceRender forceRender
visible={visible} visible={visible}
title='资产属性导入' title='资产目录属性导入'
width={520} width={520}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={() => { onCancel={() => {
...@@ -104,34 +72,18 @@ const ImportElement = (props) => { ...@@ -104,34 +72,18 @@ const ImportElement = (props) => {
}} }}
onOk={handleOk} onOk={handleOk}
> >
<Spin spinning={loading} > <div>
<div className='mb-3 flex'> <Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
<Select 模板下载
value={currentResourceType} </Button>
placeholder='请选择资产类型' </div>
onChange={(val) => { <div className='mt-3'>
setCurrentResourceType(val); <Upload {...uploadProps}>
}} <Button icon={<UploadOutlined />}>
style={{ width: 200 }} 选择文件上传
>
{
resourceTypes?.map((item,index) => {
return <Select.Option key={item.code}>{item.name}</Select.Option>
})
}
</Select>
</div>
<Space>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模版下载
</Button> </Button>
</Space> </Upload>
</Spin> </div>
</Modal> </Modal>
) )
} }
......
import React from 'react'; import React from 'react';
import { Typography } from 'antd';
import { highlightSearchContentByTerms } from '../../../../util'; import { highlightSearchContentByTerms } from '../../../../util';
import { AppContext } from '../../../../App'; import { AppContext } from '../../../../App';
const FC = ({ value = '', terms = [] }) => { const FC = ({ value = '', terms = [], publishedValue = '' }) => {
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{ {
appValue => <a onClick={() => { appValue => <a onClick={() => {
appValue?.setGlobalState && appValue?.setGlobalState({ appValue?.setGlobalState && appValue?.setGlobalState({
message: 'data-govern-show-standard-detail-message', message: 'data-govern-show-index-detail-message',
data: { name: value } 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> </a>
} }
</AppContext.Consumer> </AppContext.Consumer>
); );
} }
export default FC; export default FC;
\ No newline at end of file
...@@ -2,9 +2,10 @@ import React from 'react'; ...@@ -2,9 +2,10 @@ import React from 'react';
import { Button } from 'antd'; import { Button } from 'antd';
import { SettingFilled } from '@ant-design/icons'; import { SettingFilled } from '@ant-design/icons';
import { highlightSearchContentByTerms } from '../../../../util';
import { AppContext } from '../../../../App'; import { AppContext } from '../../../../App';
const MetadataInfo = ({ value = '', config = true }) => { const MetadataInfo = ({ value = '', config = true, terms = [] }) => {
let metadata = {}; let metadata = {};
try { try {
...@@ -18,13 +19,13 @@ const MetadataInfo = ({ value = '', config = true }) => { ...@@ -18,13 +19,13 @@ const MetadataInfo = ({ value = '', config = true }) => {
{ {
value => <div className='flex'> 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={() => { <a onClick={() => {
value?.setGlobalState && value?.setGlobalState({ value?.setGlobalState && value?.setGlobalState({
message: 'data-govern-show-metadata-message', message: 'data-govern-show-metadata-message',
data: metadata data: metadata
}) })
}} style={{ marginRight: 5, marginTop: config?5:0 }}>{metadata?.tableName||''} }} style={{ marginRight: 5, marginTop: config?5:0 }}>{highlightSearchContentByTerms(metadata?.tableName||'',terms)}
</a> </a>
{ {
(metadata?.columnItems||[]).map((item, index) => { (metadata?.columnItems||[]).map((item, index) => {
...@@ -36,7 +37,7 @@ const MetadataInfo = ({ value = '', config = true }) => { ...@@ -36,7 +37,7 @@ const MetadataInfo = ({ value = '', config = true }) => {
_content += item.metadataColumnName||''; _content += item.metadataColumnName||'';
return <span key={index}>{_content}</span> return <span key={index}>{highlightSearchContentByTerms(_content,terms)}</span>
}) })
} }
</div> </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 React, { useState, useContext } from 'react';
import { Modal, Form, Input, Select, Space, Button } from 'antd'; import { Modal, Form, Input } from 'antd';
import { AppContext } from '../../../App'; import { dispatchLatest } from '../../../../model';
import { dispatch } from '../../../model'; import { showNotifaction } from '../../../../util';
import { AuditProgress, Basic, Process, Template } from './TaskDetail'; import { AppContext } from '../../../../App';
const FC = (props) => { const StartFlowModal = (props) => {
const { visible, onCancel, task, action } = props; const { visible, onCancel, ids } = props;
const [form] = Form.useForm(); const [ form ] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false); const [ confirmLoading, setConfirmLoading ] = useState(false);
const { user } = useContext(AppContext);
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 { try {
const values = await form.validateFields(); const values = await form.validateFields();
setConfirmLoading(true); setConfirmLoading(true);
dispatch({
type: 'assetmanage.auditTask', dispatchLatest({
type: 'assetmanage.startFlow',
payload: { payload: {
params: { params: {
taskId: task?.id env: app?.env?.domainId,
}, dataAssetIds: ids.join(','),
data: { applyReason: values?.desc
dealUser: user?.userName??'test',
processDate: new Date().toLocaleString(),
status,
title: action,
value: values.content
} }
}, },
callback: data => { callback: data => {
setConfirmLoading(false);
reset(); reset();
if (data) {
showNotifaction('申请提示', (data === 'ok')?'资产目录申请成功,请在我的流程》我的申请中查看详情':data, 5);
}
onCancel && onCancel(true); onCancel && onCancel(true);
}, },
error: () => { error: () => {
setConfirmLoading(false); setConfirmLoading(false);
} }
}) })
} catch (errInfo) { } catch (errInfo) {
} }
...@@ -52,42 +66,31 @@ const FC = (props) => { ...@@ -52,42 +66,31 @@ const FC = (props) => {
return ( return (
<Modal <Modal
destroyOnClose
visible={visible} visible={visible}
title={`${action}${task?.title}`} title='资产目录申请'
width={1000} width={520}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={() => { onCancel={() => {
reset(); reset();
onCancel && onCancel(); onCancel && onCancel();
}} }}
footer={ onOk={handleOk}
<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>
}
> >
<Form <Form
form={form} {...formItemLayout}
form={form}
>
<Form.Item
label="申请原因"
name="desc"
rules={[{ required: true, message: '请在申请原因栏中描述用途说明以及必要性(资产目录和服务权限申请需要说明请求字段)' }]}
> >
<Form.Item <Input.TextArea rows={6} />
label={`${action}意见`} </Form.Item>
name="content" </Form>
rules={[{ required: true, message: `请输入${action}意见` }]}
>
<Input.TextArea rows={4} placeholder={`请输入${action}意见`} />
</Form.Item>
</Form>
<AuditProgress task={task} />
<Basic task={task} />
<Process task={task} />
<Template task={task} />
</Modal> </Modal>
); );
} }
export default FC; export default StartFlowModal;
\ No newline at end of file \ 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