940 lines
33 KiB
JavaScript
940 lines
33 KiB
JavaScript
|
|
import { isEqual, uniqueId, cloneDeep } from 'lodash';
|
|
import * as React from 'react';
|
|
import loadjs from 'loadjs';
|
|
import classnames from 'classnames';
|
|
import { RightMenu, RightMenuStore } from './rightMenu';
|
|
import { WeaSlideModal, WeaDialog } from 'ecCom';
|
|
|
|
import './index.less';
|
|
const getLength = (str) => {
|
|
let realLength = 0, len = str.length, charCode = -1;
|
|
for (let i = 0; i < len; i++) {
|
|
charCode = str.charCodeAt(i);
|
|
if (charCode >= 0 && charCode <= 128) realLength += 1;
|
|
else realLength += 2;
|
|
}
|
|
return realLength;
|
|
};
|
|
const getSubStr = (str, start, length) => {
|
|
let rt = [], currentLength = 0, len = str.length, charCode = -1;
|
|
for (let i = 0; i < len; i++) {
|
|
charCode = str.charCodeAt(i);
|
|
if (charCode >= 0 && charCode <= 128) {
|
|
currentLength++;
|
|
} else {
|
|
currentLength += 2;
|
|
}
|
|
if (currentLength > start && currentLength <= length) {
|
|
rt.push(str.charAt(i));
|
|
}
|
|
}
|
|
return rt.join('');
|
|
}
|
|
const getShortStr = (ele, data, w = 100) => {
|
|
const length = parseInt(w / 6);
|
|
const datalength = data ? getLength(data) : 0;
|
|
if (length >= datalength) {
|
|
return data;
|
|
}
|
|
return getSubStr(data, 0, length) + '...';
|
|
}
|
|
export default class D3Tree extends React.Component {
|
|
totalNodes = 0;
|
|
maxLabelLength = 0;
|
|
i = 0;
|
|
selectedPath = [];
|
|
currentPath = [];
|
|
overedPath = [];
|
|
static defaultProps = {
|
|
widthToggle: true,
|
|
showRight: true,
|
|
layout: '0',
|
|
showMenu: false
|
|
}
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
duration: 750,
|
|
hoverNode: undefined,
|
|
id: `d3TreeContainer_${new Date().getTime()}_${uniqueId()}`,
|
|
width: 220,
|
|
height: 100
|
|
}
|
|
this.didMountTree = false;
|
|
this.rightMenuStore = new RightMenuStore();
|
|
}
|
|
render() {
|
|
const { showRight, style, showMenu } = this.props;
|
|
const menu = [
|
|
{
|
|
key: '1',
|
|
icon: <i className='icon-coms-search' />,
|
|
content: '按钮 1',
|
|
},
|
|
{
|
|
key: '2',
|
|
icon: <i className='icon-coms-search' />,
|
|
content: '按钮 2',
|
|
}
|
|
];
|
|
return (
|
|
<div className={classnames("d3-tree-wrapper", { "with-right": showRight })} onClick={() => { this.props.closeRight(false) }}>
|
|
{/* <WeaRightMenu datas={this.props.menus} > */}
|
|
<div
|
|
ref={ref => this.container = ref}
|
|
id={this.state.id}
|
|
className="d3-tree"
|
|
style={style}
|
|
onContextMenu={showMenu ? this.onContextMenu : undefined}
|
|
/>
|
|
{/* </WeaRightMenu> */}
|
|
{/* <div className="right">
|
|
{this.props.showRight && this.renderRight()}
|
|
</div> */}
|
|
<div onClick={(e) => e.stopPropagation()} className={"Prj-WeaSlideModal"}>
|
|
<WeaSlideModal ecId={`${this && this.props && this.props.ecId || ''}_WeaSlideModal@pxpvk1`} visible={this.props.showRight}
|
|
key={this.renderRight()}
|
|
top={"0px"}
|
|
width={"40%"}
|
|
height={"calc(100%)"}
|
|
direction={'right'}
|
|
measure={''}
|
|
content={(typeof(this.renderRight()) == 'object' ? this.renderRight() : <iframe style={{ height: "100%", width: "100%", border: "0" }} src={this.renderRight() }></iframe>)}
|
|
onClose={() => { this.props.closeRight(false) }}
|
|
onAnimationEnd={() => console.log('onAnimationEnd')} />
|
|
</div>
|
|
|
|
{/* <WeaDialog
|
|
onCancel={() => this.props.closeDialog(false) }
|
|
visible={this.props.showDialog}
|
|
style={{ width: 1000, height: 600 }}
|
|
title={'活动计划'}
|
|
hasScroll
|
|
url={this.renderRight() !== 'function'&& this.renderRight()}
|
|
>
|
|
</WeaDialog> */}
|
|
<div className="border-selection" ref={ref => this.selection = ref}></div>
|
|
</div>
|
|
)
|
|
}
|
|
onContextMenu = (e) => {
|
|
if (this.isClickNode && this.opreateNode) {
|
|
const left = e.clientX;
|
|
const top = e.clientY;
|
|
this.showRightMenu();
|
|
this.rightMenuStore.show(left, top,
|
|
this.opreateNode.id === this.root.id,
|
|
this.getHasChild(this.opreateNode),
|
|
this.getChildVisible(this.opreateNode));
|
|
}
|
|
this.isClickNode = false;
|
|
e.stopPropagation && e.stopPropagation();
|
|
e.preventDefault && e.preventDefault();
|
|
e.nativeEvent && e.nativeEvent.preventDefault();
|
|
}
|
|
showRightMenu = () => {
|
|
if (!this.menuWrapper) {
|
|
this.menuWrapper = document.createElement('div');
|
|
document.body.appendChild(this.menuWrapper);
|
|
}
|
|
ReactDOM.render(<RightMenu ecId={`${this && this.props && this.props.ecId || ''}_RightMenu@tvll17`}
|
|
data={this.opreateNode}
|
|
store={this.rightMenuStore}
|
|
onToggleChildren={this.toggleChildren}
|
|
menus={this.props.menus}
|
|
/>, this.menuWrapper);
|
|
}
|
|
clickAnyWhere = (e) => {
|
|
this.rightMenuStore.hide();
|
|
if (this.startMove && this.selection) {
|
|
this.startMove = false;
|
|
this.selection.style.display = 'none';
|
|
const top = parseInt(this.selection.style.top);
|
|
const left = parseInt(this.selection.style.left);
|
|
const width = parseInt(this.selection.style.width);
|
|
const height = parseInt(this.selection.style.height);
|
|
|
|
this.selection.style.height = '0px';
|
|
this.selection.style.width = '0px';
|
|
}
|
|
}
|
|
mouseup = (d) => {
|
|
const e = window.event;
|
|
if (e.button === 2) {
|
|
this.opreateNode = d;
|
|
this.isClickNode = true;
|
|
this.click(d);
|
|
}
|
|
|
|
}
|
|
renderRight = () => {
|
|
if (typeof this.props.renderRight === 'function') {
|
|
if (this.selectedNode && this.props.renderRight) {
|
|
return this.props.renderRight(this.selectedNode);
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
return this.props.renderRight;
|
|
}
|
|
}
|
|
diableMouseOut = () => {
|
|
this.canTriggerMouseOut = false;
|
|
}
|
|
enableMouseOut = () => {
|
|
this.canTriggerMouseOut = true;
|
|
this.outNode();
|
|
}
|
|
componentWillReceiveProps(nextProps) {
|
|
if (!isEqual(this.props.data, nextProps.data) ||
|
|
this.props.layout !== nextProps.layout && this.container &&
|
|
(this.didMountTree = false, this.container.innerHTML = "", true)) {
|
|
if (nextProps.data) {
|
|
clearTimeout(this.initTimer);
|
|
this.initTimer = setTimeout(() => {
|
|
this.initTree(nextProps.data);
|
|
}, 100);
|
|
}
|
|
}
|
|
if (this.props.scale !== nextProps.scale) {
|
|
if (this.svgGroup) {
|
|
const transform = this.svgGroup.attr("transform");
|
|
if (transform) {
|
|
const scale = nextProps.scale / 10;
|
|
this.svgGroup.attr("transform", `${transform.split('scale')[0]}scale(${scale})`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
componentWillUnmount() {
|
|
window.removeEventListener('resize', this.resize);
|
|
document.removeEventListener('mouseup', this.clickAnyWhere);
|
|
if (this.container) {
|
|
/* this.container.removeEventListener('mousedown', this.onMouseDown);
|
|
this.container.removeEventListener('mousemove', this.onMouseMove); */
|
|
}
|
|
if (this.menuWrapper) {
|
|
ReactDOM.unmountComponentAtNode(this.menuWrapper);
|
|
document.body.removeChild(this.menuWrapper);
|
|
delete this.menuWrapper;
|
|
}
|
|
}
|
|
componentDidMount() {
|
|
window.addEventListener('resize', this.resize);
|
|
document.addEventListener('mouseup', this.clickAnyWhere);
|
|
if (this.container) {
|
|
/* this.container.addEventListener('mousedown', this.onMouseDown);
|
|
this.container.addEventListener('mousemove', this.onMouseMove); */
|
|
}
|
|
if (!loadjs.isDefined('weaEdcD3js')) {
|
|
loadjs(['/edc/d3/d3.v3.js'], 'weaEdcD3js', {
|
|
success: () => {
|
|
if (this.container && this.props.data) {
|
|
const _this = this;
|
|
setTimeout(function(){
|
|
_this.initTree(_this.props.data);
|
|
_this.resize();
|
|
_this.click(_this.root);
|
|
},1000);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
if (this.container && this.props.data) {
|
|
const _this = this;
|
|
setTimeout(function(){
|
|
_this.initTree(_this.props.data);
|
|
_this.resize();
|
|
_this.click(_this.root);
|
|
},1000);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
onMouseDown = (e) => {
|
|
if (e.button === 0 && this.selection && e.target.tagName === 'svg') {
|
|
this.selectionLeft = e.clientX;
|
|
this.selectionTop = e.clientY;
|
|
this.selection.style.display = 'block';
|
|
this.selection.style.top = this.selectionTop + 'px';
|
|
this.selection.style.left = this.selectionLeft + 'px';
|
|
this.startMove = true;
|
|
}
|
|
}
|
|
onMouseMove = (e) => {
|
|
if (this.startMove) {
|
|
this.selection.style.height = (e.y - this.selectionTop) + 'px';
|
|
this.selection.style.width = (e.x - this.selectionLeft) + 'px';
|
|
e.stopPropagation && e.stopPropagation();
|
|
e.preventDefault && e.preventDefault();
|
|
e.nativeEvent && e.nativeEvent.preventDefault();
|
|
}
|
|
}
|
|
resize = () => {
|
|
clearTimeout(this.resizeTimer);
|
|
this.resizeTimer = setTimeout(() => {
|
|
const viewerHeight = this.container.clientHeight;
|
|
const viewerWidth = this.container.clientWidth;
|
|
d3.select(`#${this.state.id}>svg`)
|
|
.attr("width", viewerWidth)
|
|
.attr("height", viewerHeight);
|
|
}, 100);
|
|
}
|
|
getSize = () => {
|
|
const { layout } = this.props;
|
|
const { width, height } = this.state;
|
|
if (layout === '1') { // 上
|
|
return {
|
|
nodeSize: [width + 10, height],
|
|
width,
|
|
height,
|
|
deep: width
|
|
}
|
|
} else if (layout === '3') { // 下
|
|
return {
|
|
nodeSize: [width + 10, height],
|
|
width,
|
|
height,
|
|
deep: width * -1
|
|
}
|
|
} else if (layout === '0') { // 左
|
|
return {
|
|
nodeSize: [height + 10, width],
|
|
width,
|
|
height,
|
|
deep: width * 2,
|
|
}
|
|
} else if (layout === '2') { // 右
|
|
return {
|
|
nodeSize: [height + 10, width],
|
|
width,
|
|
height,
|
|
deep: width * -2,
|
|
}
|
|
}
|
|
}
|
|
searchNode = (name, nodes = [this.root]) => {
|
|
const searchedNode = [];
|
|
nodes.forEach(node => {
|
|
if (node.name.toLowerCase().indexOf(name) > -1) {
|
|
searchedNode.push(node);
|
|
}
|
|
if (node.children) {
|
|
searchedNode.push(...this.searchNode(name, node.children));
|
|
}
|
|
if (node._children) {
|
|
searchedNode.push(...this.searchNode(name, node._children));
|
|
}
|
|
});
|
|
return searchedNode;
|
|
}
|
|
initTree(treeData) {
|
|
const { layout } = this.props;
|
|
const { width, height } = this.state;
|
|
const { nodeSize } = this.getSize();
|
|
if (this.container && window.d3) {
|
|
const viewerHeight = this.container.clientHeight;
|
|
const viewerWidth = this.container.clientWidth;
|
|
this.tree = d3.layout.tree().nodeSize(nodeSize);
|
|
// define a d3 diagonal projection for use by the node paths later on.
|
|
this.diagonal = d3.svg.diagonal()
|
|
.projection((d) => {
|
|
return (layout === '1' || layout === '3') ?
|
|
[d.x + width / 2, d.y + height / 2] :
|
|
[d.y + width / 2, d.x + height / 2];
|
|
});
|
|
|
|
this.sortTree();
|
|
if (!this.didMountTree) {
|
|
const zoom = (e) => {
|
|
this.svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
|
|
if (this.props.zoom) {
|
|
this.props.zoom(d3.event.scale)
|
|
}
|
|
}
|
|
if (this.props.zoom) {
|
|
this.props.zoom(1)
|
|
}
|
|
this.zoomListener = d3.behavior.zoom().scaleExtent([0.1, 2]).on("zoom", zoom);
|
|
|
|
// define the baseSvg, attaching a class for styling and the zoomListener
|
|
this.baseSvg = d3.select("#" + this.state.id).append("svg")
|
|
.attr("width", viewerWidth)
|
|
.attr("height", viewerHeight)
|
|
.attr("class", "overlay")
|
|
.call(this.zoomListener);
|
|
this.svgGroup = this.baseSvg.append("g");
|
|
}
|
|
// Define the drag listeners for drag/drop behaviour of nodes.
|
|
// Define the root
|
|
const { x0 = viewerHeight / 2, y0 = 0 } = this.root || {};
|
|
this.root = cloneDeep(treeData)
|
|
this.root.x0 = x0
|
|
this.root.y0 = y0;
|
|
|
|
// Layout the tree initially and center on the root node.
|
|
this.update(this.root);
|
|
if (!this.didMountTree) {
|
|
this.centerNode(this.root);
|
|
}
|
|
//选中的节点在不在树中时 选中根节点
|
|
const curNodeId = this.selectedNode.id;
|
|
const treeNodeIds = [treeData.id];
|
|
treeData.children.map(c => {
|
|
treeNodeIds.push(c.id);
|
|
})
|
|
if (treeNodeIds.indexOf(curNodeId) < 0) {
|
|
this.click(this.root);
|
|
}
|
|
this.didMountTree = true;
|
|
} else {
|
|
setTimeout(() => {
|
|
this.initTree(treeData);
|
|
}, 50);
|
|
}
|
|
}
|
|
|
|
centerNode = (source) => {
|
|
const { layout } = this.props;
|
|
const { width, height } = this.state;
|
|
const viewerHeight = this.container.clientHeight;
|
|
const viewerWidth = this.container.clientWidth;
|
|
const scale = this.zoomListener.scale();
|
|
const isRoot = source === this.root;
|
|
this.selectedNode = source;
|
|
let x = 0;
|
|
let y = 0;
|
|
if (layout === '1') {
|
|
y = -source.y0;
|
|
x = -source.x0;
|
|
x = x * scale + viewerWidth / 2;
|
|
if (isRoot) {
|
|
y = y * scale + height;
|
|
} else {
|
|
y = y * scale + viewerHeight / 2;
|
|
}
|
|
} else if (layout === '3') {
|
|
y = -source.y0;
|
|
x = -source.x0;
|
|
x = x * scale + viewerWidth / 2;
|
|
if (isRoot) {
|
|
y = y * scale + viewerHeight - height * 2;
|
|
} else {
|
|
y = y * scale + viewerHeight / 2;
|
|
}
|
|
} else if (layout === '2') {
|
|
x = -source.y0;
|
|
y = -source.x0;
|
|
if (isRoot) {
|
|
x = x * scale + viewerWidth - width * 2;
|
|
} else {
|
|
x = x * scale + viewerWidth / 2;
|
|
}
|
|
y = y * scale + viewerHeight / 2;
|
|
} else {
|
|
x = -source.y0;
|
|
y = -source.x0;
|
|
if (isRoot) {
|
|
x = x * scale + width;
|
|
} else {
|
|
x = x * scale + viewerWidth / 2;
|
|
}
|
|
y = y * scale + viewerHeight / 2;
|
|
}
|
|
this.svgGroup.transition()
|
|
.duration(this.state.duration)
|
|
.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
|
|
this.zoomListener.scale(scale);
|
|
this.zoomListener.translate([x, y]);
|
|
this.click(source);
|
|
}
|
|
collapse = (d) => {
|
|
if (d.children) {
|
|
d._children = d.children;
|
|
d._children.forEach(collapse);
|
|
d.children = null;
|
|
}
|
|
}
|
|
|
|
expand = (d) => {
|
|
if (d._children) {
|
|
d.children = d._children;
|
|
d.children.forEach(expand);
|
|
d._children = null;
|
|
}
|
|
}
|
|
sortTree = () => {
|
|
this.tree.sort((a, b) => {
|
|
return parseInt(a.id) - parseInt(b.id);
|
|
});
|
|
}
|
|
getXY = (x, y) => {
|
|
const { layout } = this.props;
|
|
if (layout === '1' || layout === '3') { // 上下
|
|
return `${x},${y}`;
|
|
} else {
|
|
return `${y},${x}`;
|
|
}
|
|
}
|
|
|
|
update = (source) => {
|
|
if (!this.tree) {
|
|
setTimeout(() => {
|
|
this.update(source);
|
|
}, 50);
|
|
return;
|
|
}
|
|
const { desc, desc2, desc3, showname, showSubmitCount = true, nameTitle } = this.props;
|
|
const { duration } = this.state;
|
|
const { deep, width, height, textAnchor } = this.getSize();
|
|
// Compute the new tree layout.
|
|
window.tree = this.tree;
|
|
const nodes = this.tree.nodes(this.root).reverse();
|
|
const links = this.tree.links(nodes);
|
|
const isShownameFunc = typeof showname === 'function';
|
|
const isDescFunc = typeof desc === 'function';
|
|
const isDesc2Func = typeof desc2 === 'function';
|
|
const isDesc3Func = typeof desc3 === 'function';
|
|
// Normalize for fixed-depth.
|
|
nodes.forEach((d) => {
|
|
d.y = d.depth * deep;
|
|
if (d.current) {
|
|
this.currentPath = this.getLinksToParents(d);
|
|
}
|
|
});
|
|
|
|
// Update the nodes…
|
|
const node = this.svgGroup.selectAll("g.node")
|
|
.data(nodes, (d) => d.id);
|
|
|
|
// Enter any new nodes at the parent's previous position.
|
|
const nodeEnter = node.enter().append("g")
|
|
.attr("class", 'node')
|
|
.attr("transform", d => `translate(${this.getXY(source.x0, source.y0)})`)
|
|
.on("click", this.click)
|
|
.on('mouseup', this.mouseup);
|
|
|
|
nodeEnter.append("rect")
|
|
.attr('class', 'node-rect')
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.attr("stroke", "#999")
|
|
.attr("stroke-width", 2)
|
|
.attr("fill", "#fff")
|
|
.attr("rx", 2)
|
|
.attr("ry", 2);
|
|
nodeEnter.append("rect")
|
|
.attr("class", 'flag')
|
|
.attr("x", 1)
|
|
.attr("y", 1)
|
|
.attr("width", 6)
|
|
.attr("height", height - 2)
|
|
.attr("fill", "rgb(230, 228, 228)")
|
|
nodeEnter.append("rect")
|
|
.attr("class", 'flag2')
|
|
.attr("x", 1)
|
|
.attr("y", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return height - height*(finish/100) + 1*(finish/100)
|
|
}else{
|
|
return 1
|
|
}
|
|
})
|
|
.attr("width", 6)
|
|
.attr("height", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return (height - 2)*(finish/100)
|
|
}else{
|
|
// console.log((height - 2));
|
|
return height - 2
|
|
}
|
|
|
|
})
|
|
.attr("fill", "#fff")
|
|
.style("fill", (d) => {
|
|
if(d.type=="task"){
|
|
if(d.overstr!==""){
|
|
return "red";
|
|
}else{
|
|
return "#52c41a";
|
|
}
|
|
|
|
}else{
|
|
return d.type=="prj" ? "#f7f7f7" : (d.type=="task" && d.isfinish == 1 ? "#52c41a" : '#f7f7f7');
|
|
}
|
|
});
|
|
const textg = nodeEnter.append('g').attr('class', 'text-g');
|
|
textg.append("text")
|
|
.attr("x", 20)
|
|
.attr("y", 20)
|
|
.attr("dy", ".5em")
|
|
.attr("text-anchor", textAnchor)
|
|
.text(function (d) {
|
|
return getShortStr(this, isShownameFunc ? showname(d) : d[showname],120);
|
|
})
|
|
.on('click', this.clickTitle);
|
|
textg.append('title').text(d => nameTitle ? nameTitle : isShownameFunc ? showname(d) : d[showname]);
|
|
|
|
nodeEnter.append("text")
|
|
.attr("display", d => {
|
|
let all = 0;
|
|
if (d.children) {
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
all = d._children.length;
|
|
}
|
|
return all ? 'block' : 'none';
|
|
})
|
|
.attr("class", 'count')
|
|
.attr("x", width - 30)
|
|
.attr("y", 20)
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", textAnchor)
|
|
.text(d => {
|
|
let submitCount = 0;
|
|
let all = 0;
|
|
if (d.children) {
|
|
submitCount = d.children.filter(d => d.isfinish).length;
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
submitCount = d._children.filter(d => d.isfinish).length;
|
|
all = d._children.length;
|
|
}
|
|
if (showSubmitCount) {
|
|
return `${submitCount}/${all}`
|
|
} else {
|
|
return `${all}`
|
|
}
|
|
});
|
|
|
|
const descg = nodeEnter.append('g').attr('class', 'desc-g');
|
|
descg.append("text")
|
|
.attr("class", 'desc')
|
|
.attr("x", 20)
|
|
.attr("y", 45)
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", textAnchor).text(function (d) {
|
|
return getShortStr(this, isDescFunc ? desc(d) : d[desc], 120);
|
|
});
|
|
descg.append('title').text(d => isDescFunc ? desc(d) : d[desc]);
|
|
const descg2 = nodeEnter.append('g').attr('class', 'desc-g2');
|
|
descg2.append("text")
|
|
.attr("class", 'desc')
|
|
.attr("x", 20)
|
|
.attr("y", 65)
|
|
.attr("dy", ".35em")
|
|
.attr("text-anchor", textAnchor).text(function (d) {
|
|
return d[desc2];
|
|
});
|
|
descg2.append('title').text(d => isDesc2Func ? desc(d) : d[desc2]);
|
|
const descg3 = nodeEnter.append('g').attr('class', 'desc-g3');
|
|
descg3.append("text")
|
|
.attr("class", 'desc')
|
|
.attr("x", 20)
|
|
.attr("y", 85)
|
|
.attr("dy", ".35em")
|
|
.attr("fill", "rgb(109, 106, 236")
|
|
.attr("text-anchor", textAnchor).text(function (d) {
|
|
return getShortStr(this, isDesc3Func ? desc(d) : d[desc3], 120);
|
|
});
|
|
descg3.append('title').text(d => isDesc3Func ? desc(d) : d[desc3]);
|
|
// descText.append('svg:title').text(d=>d[desc]);
|
|
// Transition nodes to their new position.
|
|
const nodeUpdate = node.transition()
|
|
.duration(duration)
|
|
.attr("transform", d => `translate(${this.getXY(d.x, d.y)})`)
|
|
.attr("class", (d) => {
|
|
if (this.selectedNode && d.id === this.selectedNode.id) {
|
|
return 'node node-selected'
|
|
} else {
|
|
return 'node';
|
|
}
|
|
});
|
|
nodeEnter.append("rect")
|
|
.attr("class", 'flag2')
|
|
.attr("x", 1)
|
|
.attr("y", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return height - height*(finish/100) + 1*(finish/100)
|
|
}else{
|
|
return 1
|
|
}
|
|
})
|
|
.attr("width", 6)
|
|
.attr("height", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return (height - 2)*(finish/100)
|
|
}else{
|
|
// console.log((height - 2));
|
|
return height - 2
|
|
}
|
|
|
|
})
|
|
.attr("fill", "#fff")
|
|
.style("fill", (d) => {
|
|
if(d.type=="task"){
|
|
if(d.overstr!==""){
|
|
return "red";
|
|
}else{
|
|
return "#52c41a";
|
|
}
|
|
|
|
}else{
|
|
return d.type=="prj" ? "#f7f7f7" : (d.type=="task" && d.isfinish == 1 ? "#52c41a" : '#f7f7f7');
|
|
}
|
|
});
|
|
|
|
nodeUpdate.select("rect.flag2")
|
|
.attr("y", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return height - height*(finish/100) + 1*(finish/100)
|
|
}else{
|
|
return 1
|
|
}
|
|
})
|
|
.attr("height", (d)=>{
|
|
if(d.type=="task"){
|
|
// console.log((height - 2)*(d.finish/100),222,height,d.finish,d.id);
|
|
let finish = d.finish == -1 ? 0 : d.finish;
|
|
return (height - 2)*(finish/100)
|
|
}else{
|
|
// console.log((height - 2));
|
|
return height - 2
|
|
}
|
|
|
|
})
|
|
.style("fill", (d) => {
|
|
if(d.type=="task"){
|
|
if(d.overstr!==""){
|
|
return "red";
|
|
}else{
|
|
return "#52c41a";
|
|
}
|
|
|
|
}else{
|
|
return d.type=="prj" ? "#f7f7f7" : (d.type=="task" && d.isfinish == 1 ? "#52c41a" : '#f7f7f7');
|
|
}
|
|
});
|
|
|
|
|
|
|
|
nodeUpdate.select("rect.node-rect")
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.attr("stroke", "#999")
|
|
.attr("stroke-width", 2)
|
|
.attr("fill", "#fff");
|
|
|
|
nodeUpdate.select("text").style("fill-opacity", 1);
|
|
nodeUpdate.select("g.text-g>text").text(function (d) {
|
|
return getShortStr(this, isShownameFunc ? showname(d) : d[showname]);
|
|
});
|
|
nodeUpdate.select("g.text-g>title").text(function (d) {
|
|
return nameTitle ? nameTitle : isShownameFunc ? showname(d) : d[showname];
|
|
})
|
|
nodeUpdate.select("g.desc-g>text.desc").text(function (d) {
|
|
return getShortStr(this, isDescFunc ? desc(d) : d[desc], 120);
|
|
});
|
|
nodeUpdate.select("g.desc-g2>text.desc").text(function (d) {
|
|
return d[desc2];
|
|
});
|
|
nodeUpdate.select("g.desc-g3>text.desc").text(function (d) {
|
|
return getShortStr(this, isDesc3Func ? desc(d) : d[desc3], 120);
|
|
});
|
|
nodeUpdate.select("g.desc-g>title").text(function (d) {
|
|
return isDescFunc ? desc(d) : d[desc];
|
|
});
|
|
nodeUpdate.select("g.desc-g>title").text(d => isDescFunc ? desc(d) : d[desc]);
|
|
|
|
nodeUpdate.select('text.count').text(d => {
|
|
let submitCount = 0;
|
|
let all = 0;
|
|
if (d.children) {
|
|
submitCount = d.children.filter(d => d.isfinish).length;
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
submitCount = d._children.filter(d => d.isfinish).length;
|
|
all = d._children.length;
|
|
}
|
|
if (showSubmitCount) {
|
|
return `${submitCount}/${all}`
|
|
} else {
|
|
return `${all}`
|
|
}
|
|
|
|
}).attr("display", d => {
|
|
let all = 0;
|
|
if (d.children) {
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
all = d._children.length;
|
|
}
|
|
return all ? 'block' : 'none';
|
|
})
|
|
|
|
nodeUpdate.select("g.count>text").text(d => {
|
|
let submitCount = 0;
|
|
let all = 0;
|
|
if (d.children) {
|
|
submitCount = d.children.filter(d => d.isfinish).length;
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
submitCount = d._children.filter(d => d.isfinish).length;
|
|
all = d._children.length;
|
|
}
|
|
if (showSubmitCount) {
|
|
return `${submitCount}/${all}`
|
|
} else {
|
|
return `${all}`
|
|
}
|
|
|
|
}).attr("display", d => {
|
|
let all = 0;
|
|
if (d.children) {
|
|
all = d.children.length;
|
|
} else if (d._children) {
|
|
all = d._children.length;
|
|
}
|
|
return all ? 'block' : 'none';
|
|
})
|
|
|
|
|
|
|
|
// Transition exiting nodes to the parent's new position.
|
|
const nodeExit = node.exit().transition()
|
|
.duration(duration)
|
|
.attr("transform", d => `translate(${this.getXY(source.x, source.y)})`)
|
|
.remove();
|
|
|
|
nodeExit.select("rect")
|
|
.attr("width", width)
|
|
.attr("height", height)
|
|
.attr("stroke", "#ccc")
|
|
.attr("stroke-width", 2);
|
|
|
|
nodeExit.select("text");
|
|
// Update the links…
|
|
const link = this.svgGroup.selectAll("path.link")
|
|
.data(links, d => d.target.id)
|
|
.attr('class', "link");
|
|
|
|
// Enter any new links at the parent's previous position.
|
|
link.enter().insert("path", "g")
|
|
.attr('class', "link")
|
|
.attr("stroke", d => {
|
|
const { source: { id: from }, target: { id: to } } = d;
|
|
if (this.selectedPath.indexOf(`${from},${to}`) > -1) {
|
|
return "#2db7f5"
|
|
} else if (this.currentPath.indexOf(`${from},${to}`) > -1) {
|
|
return "#2db7f5";
|
|
}
|
|
return "#ccc"
|
|
})
|
|
.attr('fill', "none")
|
|
.attr("stroke-width", 1.5)
|
|
.attr("x", width / 2)
|
|
.attr("y", height / 2)
|
|
.attr("d", (d) => {
|
|
const o = {
|
|
x: source.x0,
|
|
y: source.y0
|
|
};
|
|
return this.diagonal({
|
|
source: o,
|
|
target: o
|
|
});
|
|
});
|
|
|
|
// Transition links to their new position.
|
|
link.transition()
|
|
.duration(duration)
|
|
.attr("d", this.diagonal)
|
|
.attr("stroke", d => {
|
|
const { source: { id: from }, target: { id: to } } = d;
|
|
if (this.selectedPath.indexOf(`${from},${to}`) > -1) {
|
|
return "#2db7f5"
|
|
} else if (this.currentPath.indexOf(`${from},${to}`) > -1) {
|
|
return "#2db7f5";
|
|
}
|
|
return "#ccc"
|
|
});
|
|
|
|
// Transition exiting nodes to the parent's new position.
|
|
link.exit().transition()
|
|
.duration(duration)
|
|
.attr("d", (d) => {
|
|
var o = {
|
|
x: source.x,
|
|
y: source.y
|
|
};
|
|
return this.diagonal({
|
|
source: o,
|
|
target: o
|
|
});
|
|
})
|
|
.remove();
|
|
|
|
// Stash the old positions for transition.
|
|
nodes.forEach((d) => {
|
|
d.x0 = d.x;
|
|
d.y0 = d.y;
|
|
});
|
|
}
|
|
clickTitle = (d) => {
|
|
if (this.props.onClickTitle) {
|
|
this.props.onClickTitle(d);
|
|
window.event.stopPropagation();
|
|
}
|
|
}
|
|
click = (d) => {
|
|
if (d) {
|
|
this.selectedPath = this.getLinksToParents(d);
|
|
this.selectedNode = d;
|
|
// d = this.toggleChildren(d);
|
|
this.update(d);
|
|
if (this.props.onClick) {
|
|
this.props.onClick(d, this.getHasChild(d));
|
|
}
|
|
}
|
|
}
|
|
getLinksToParents = (d) => {
|
|
const selectedPath = [];
|
|
let current = d;
|
|
while (current) {
|
|
selectedPath.push(`${current.parentid},${current.id}`);
|
|
current = current.parent;
|
|
}
|
|
return selectedPath;
|
|
}
|
|
toggleChildren = () => {
|
|
const d = this.opreateNode;
|
|
if (d) {
|
|
if (d.children) {
|
|
d._children = d.children;
|
|
d.children = null;
|
|
} else if (d._children) {
|
|
d.children = d._children;
|
|
d._children = null;
|
|
}
|
|
this.update(d);
|
|
}
|
|
}
|
|
getChildVisible = (d) => {
|
|
return !!d.children;
|
|
}
|
|
getHasChild = (d) => {
|
|
return !!d.children || !!d._children;
|
|
}
|
|
} |