Commit f583cde4 by zhaochengxiang

资产自定义

parent 5c77df1f
...@@ -59,6 +59,14 @@ export function* directoryImport(payload) { ...@@ -59,6 +59,14 @@ export function* directoryImport(payload) {
return yield call(service.directoryImport, payload); return yield call(service.directoryImport, payload);
} }
export function* previewTreeByCustomElements(payload) {
return yield call(service.previewTreeByCustomElements, payload);
}
export function* saveTreeByCustomElements(payload) {
return yield call(service.saveTreeByCustomElements, payload);
}
export function* assetImport(payload) { export function* assetImport(payload) {
return yield call(service.assetImport, payload); return yield call(service.assetImport, payload);
} }
......
...@@ -72,6 +72,14 @@ export function directoryImport(payload) { ...@@ -72,6 +72,14 @@ export function directoryImport(payload) {
return PostFile("/dataassetmanager/directoryApi/import", payload); return PostFile("/dataassetmanager/directoryApi/import", payload);
} }
export function previewTreeByCustomElements(payload) {
return PostJSON("/dataassetmanager/directoryApi/previewAsTreeByCustomElements", payload);
}
export function saveTreeByCustomElements(payload) {
return PostJSON("/dataassetmanager/directoryApi/saveCustomElementTree", payload);
}
export function assetImport(payload) { export function assetImport(payload) {
return PostFile("/dataassetmanager/dataAssetApi/import", payload); return PostFile("/dataassetmanager/dataAssetApi/import", payload);
} }
......
import React, { 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 } 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';
import DragTag from './DragTag'; import DragTag from './DragTag';
import PreviewTree from './PreviewTree';
import { dispatch, dispatchLatest } from '../../../../model';
import { showMessage } from '../../../../util';
const CustomDirectoryModal = (props) => { const CustomDirectoryModal = (props) => {
const { visible, onCancel } = props; const { visible, onCancel } = props;
const [ data, setData ] = useState([ const [ data, setData ] = useState([]);
{ const [ previewTreeData, setPreviewTreeData ] = useState({});
id: 1,
text: 'Write a cool JS library',
},
{
id: 2,
text: 'Make it generic enough',
},
{
id: 3,
text: 'Write README',
},
{
id: 4,
text: 'Create some examples',
},
{
id: 5,
text: 'Spam in Twitter and IRC to promote it (note that this element is taller than the others)',
},
{
id: 6,
text: '???',
},
{
id: 7,
text: 'PROFIT',
},
]);
const [ checkedValues, setCheckedValues ] = useState([]); const [ checkedValues, setCheckedValues ] = useState([]);
const [ checkedData, setCheckedData ] = useState([]); const [ checkedData, setCheckedData ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false); const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ form ] = Form.useForm(); const [ form ] = Form.useForm();
useEffect(() => {
if (visible) {
reset();
getAllElements();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
useEffect(() => {
if ((checkedValues||[]).length>0) {
getPreviewTreeData();
}
}, [checkedValues])
const getAllElements = () => {
dispatch({
type: 'assetmanage.listElements',
callback: data => {
setData(data||[]);
}
})
}
const getPreviewTreeData = () => {
dispatchLatest({
type: 'assetmanage.previewTreeByCustomElements',
payload: {
data: checkedValues
},
callback: data => {
setPreviewTreeData((data||[]).length>0?data[0]:{});
}
})
}
const onChange = (checkedValues) => { const onChange = (checkedValues) => {
setCheckedValues(checkedValues); setCheckedValues(checkedValues);
const _checkedData = []; const _checkedData = [];
...@@ -60,6 +73,43 @@ const CustomDirectoryModal = (props) => { ...@@ -60,6 +73,43 @@ const CustomDirectoryModal = (props) => {
setCheckedData(_checkedData); setCheckedData(_checkedData);
} }
const reset = () => {
setConfirmLoading(false);
setCheckedValues([]);
setCheckedData([]);
form.resetFields();
}
const onOk = async() => {
if ((checkedValues||[]).length === 0) {
showMessage('info', '请先选择要素');
return;
}
try {
const row = await form.validateFields();
setConfirmLoading(true);
dispatch({
type: 'assetmanage.saveTreeByCustomElements',
payload: {
data: checkedValues,
params: row
},
callback: () => {
setConfirmLoading(false);
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const moveTag = useCallback((dragIndex, hoverIndex) => { const moveTag = useCallback((dragIndex, hoverIndex) => {
const dragTag = checkedData[dragIndex]; const dragTag = checkedData[dragIndex];
...@@ -89,7 +139,7 @@ const CustomDirectoryModal = (props) => { ...@@ -89,7 +139,7 @@ const CustomDirectoryModal = (props) => {
} }
const renderItem = (item, index) => { const renderItem = (item, index) => {
return (<DragTag key={item.id} index={index} id={item.id} text={item.text} moveTag={moveTag} onClose={()=>{onTagClose(item.id)}}/>); return (<DragTag key={item.id} index={index} id={item.id} text={item.name||''} moveTag={moveTag} onClose={()=>{onTagClose(item.id)}}/>);
}; };
const formItemLayout = { const formItemLayout = {
...@@ -111,21 +161,24 @@ const CustomDirectoryModal = (props) => { ...@@ -111,21 +161,24 @@ const CustomDirectoryModal = (props) => {
width={800} width={800}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={() => { onCancel && onCancel() }} onCancel={() => { onCancel && onCancel() }}
onOk={onOk}
> >
<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>
<Checkbox.Group value={checkedValues} onChange={onChange}> <div style={{ maxHeight: 500, overflow: 'auto' }}>
{ <Checkbox.Group value={checkedValues} onChange={onChange}>
(data||[]).map((item, index) => { {
return ( (data||[]).map((item, index) => {
<Row key={index}> return (
<Checkbox value={item.id||''}>{item.text||''}</Checkbox> <Row key={index}>
</Row> <Checkbox value={item.id||''}>{item.name||''}</Checkbox>
); </Row>
}) );
} })
</Checkbox.Group> }
</Checkbox.Group>
</div>
</Col> </Col>
<Col span={16}> <Col span={16}>
{ {
...@@ -139,6 +192,9 @@ const CustomDirectoryModal = (props) => { ...@@ -139,6 +192,9 @@ const CustomDirectoryModal = (props) => {
(checkedData||[]).map((item, index) => renderItem(item, index)) (checkedData||[]).map((item, index) => renderItem(item, index))
} }
</DndProvider> </DndProvider>
<div style={{ height: 300 }}>
<PreviewTree data={previewTreeData} />
</div>
</> </>
} }
<Form className='mt-5' {...formItemLayout} form={form}> <Form className='mt-5' {...formItemLayout} form={form}>
...@@ -151,8 +207,7 @@ const CustomDirectoryModal = (props) => { ...@@ -151,8 +207,7 @@ const CustomDirectoryModal = (props) => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label='描述或原因' label='描述或原因'
name='reason' name='desc'
rules={[{ required: true, message: '请输入描述或原因'}]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
......
import React from 'react';
import G6 from '@antv/g6';
let graph = null;
const globalFontSize = 20;
const maxTextWidth = 160;
const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) {
return [
['M', x - r, y - r],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x + 2 - r, y - r],
['L', x + r - 2, y - r],
];
};
const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
return [
['M', x - r, y - r],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x + 2 - r, y - r],
['L', x + r - 2, y - r],
['M', x, y - 2 * r + 2],
['L', x, y - 2],
];
};
class PreviewTree extends React.Component {
componentDidMount() {
const { type, loadMoreData } = this.props;
const container = document.getElementById('container');
if (!container) return;
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
G6.registerNode(
'tree-node',
{
draw(cfg, group) {
const rect = group.addShape('rect', {
attrs: {
fill: '#fff',
stroke: '#666',
radius: 5,
cursor: 'pointer',
},
});
const content = (cfg.text||'').replace(/(.{19})/g, '$1\n');
const text = group.addShape('text', {
attrs: {
x: 0,
y: 0,
fill: '#000',
fontSize: globalFontSize,
textAlign: 'left',
textBaseline: 'middle',
text: content,
}
});
const bbox = text.getBBox();
rect.attr({
x: bbox.minX - 10,
y: bbox.minY - 10,
width: bbox.width + 20,
height: bbox.height + 20,
});
return group;
},
update: undefined,
},
'single-node'
);
const tooltip = new G6.Tooltip({
offsetX: 10,
offsetY: 10,
// the types of items that allow the tooltip show up
// 允许出现 tooltip 的 item 类型
itemTypes: ['node'],
// custom the tooltip's content
// 自定义 tooltip 内容
getContent: (e) => {
const outDiv = document.createElement('div');
outDiv.style.width = 'fit-content';
//outDiv.style.padding = '0px 0px 20px 0px';
outDiv.innerHTML = `
<h4>${e.item.getModel().text||''}</h4>
`;
return outDiv;
},
});
graph = new G6.TreeGraph({
container: 'container',
width,
height,
maxZoom: 1,
plugins: [tooltip],
modes: {
default: [
'drag-canvas',
'zoom-canvas',
],
},
defaultNode: {
type: 'tree-node',
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
},
defaultEdge: {
type: 'cubic-horizontal',
size: 2,
color: '#e2e2e2',
style: {
endArrow: true
}
},
layout: {
type: 'compactBox',
direction: 'LR', // H / V / LR / RL / TB / BT
getId: function getId(d) {
return d.id;
},
getVGap: function getVGap() {
return 20;
},
getHGap: function getHGap() {
return 100;
},
},
});
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', maxTextWidth, globalFontSize),
};
});
this.layoutGraph();
if (typeof window !== 'undefined') {
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.changeSize(container.scrollWidth, container.scrollHeight);
};
}
}
layoutGraph = () => {
const { data } = this.props;
if(graph && data){
console.log('data', data);
graph.data(data);
graph.render();
graph.fitView();
}
}
componentDidUpdate(prevProps, prevState){
this.layoutGraph();
}
render() {
return (
<div id='container' style={{ width: '100%', height: '100%' }} />
);
}
}
export default PreviewTree;
\ No newline at end of file
...@@ -63,7 +63,7 @@ class Relation extends React.Component { ...@@ -63,7 +63,7 @@ class Relation extends React.Component {
if (cfg.depth === 0) { if (cfg.depth === 0) {
root = cfg root = cfg
radius = cfg.children?.length > 10 radius = (cfg.children||[]).length > 10
} else { } else {
[x, y] = textXY(root, cfg) [x, y] = textXY(root, cfg)
} }
......
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