You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
org-chart-frant/src/pages/company.jsx

532 lines
16 KiB
React

import styles from './index.less';
import React, { useEffect, useState, useRef } from 'react';
3 years ago
import { OrgChartComponent } from '@/components/orgChart';
import * as d3 from 'd3';
import { TopBar } from '../components/topBar';
import ToolBar from '../components/toolBar';
import TimeLine from '../components/timeline';
import DrawerComponents from '../components/drawer';
import OperateDialog from '../components/dialog';
3 years ago
import jsPDF from 'jspdf';
import moment from 'moment';
import qs from 'qs';
2 years ago
import { message, Spin, notification } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
import { getLabel } from '../util/i18n.js';
let active = 'top';
2 years ago
let drawerCom = null;
let operateCom = null;
2 years ago
let timeLine = null;
let orgChart = null;
let topbar = null;
2 years ago
export default function companyPage() {
3 years ago
const [data, setData] = useState(null);
2 years ago
let compact = 0;
let expandAll = 0;
3 years ago
const [sliderProgress, setSliderProgress] = useState(50);
let addNodeChildFunc = null;
let topBarSearchRequest = null;
const [hasRight, setHasRight] = useState('');
const [timelineId, setTimelineId] = useState(0);
const infoRef = useRef();
const [labelData, setLabelData] = useState({});
const [lebelCompleted, setLabelCompleted] = useState(false);
useEffect(() => {
infoRef.current = timelineId;
}, [timelineId]);
const [spinning, setSpinning] = useState(false);
3 years ago
// 点击节点
const onNodeClick = (node) => {
if (node.ftype == '1') {
window.open(
`/spa/organization/static/index.html#/main/organization/companyExtend/${node.fobjid}`,
'_blank',
);
} else {
window.open(
`/spa/organization/static/index.html#/main/organization/departmentExtend/${node.fobjid}`,
'_blank',
);
}
};
3 years ago
// 扩展按钮点击
2 years ago
const onButtonClick = (event, d) => {
3 years ago
if (d.children) {
let idsList = [];
d.children.forEach((item) => {
if (item.data.hasChildren && !item._children) {
idsList.push(item.data.id);
}
3 years ago
});
3 years ago
if (idsList.length == 0) {
return;
}
3 years ago
let idsStr = idsList.join(',');
let api = '';
if (topBarSearchRequest) {
let request = { ...topBarSearchRequest, ids: idsStr };
api =
'/api/bs/hrmorganization/orgchart/asyncCompanyData' +
qs.stringify(request, { addQueryPrefix: true });
} else {
let request = { ...topbar.state.requestData, ids: idsStr };
3 years ago
api =
'/api/bs/hrmorganization/orgchart/asyncCompanyData' +
qs.stringify(request, { addQueryPrefix: true });
}
3 years ago
fetch(api)
.then((res) => res.json())
.then((data) => {
if (data.data) {
data.data.forEach((item) => {
2 years ago
// window.chart.addNode(item);
orgChart.addNode(item);
3 years ago
});
}
});
}
2 years ago
};
3 years ago
// 获取部门图片
2 years ago
function getDepartmentImage(fisvitual) {
return fisvitual == '0' ? `./img/back/level4.png` : `./img/back/level8.png`;
3 years ago
}
// 获取分部图片
2 years ago
function getSubcompanyImage(fisvitual) {
return fisvitual == '0' ? `./img/back/level1.png` : `./img/back/level5.png`;
3 years ago
}
// 多语言获取
3 years ago
useEffect(() => {
d3.json('/api/bs/hrmorganization/orgchart/i18n').then((res) => {
setLabelData(res.data);
setLabelCompleted(true);
notification.open({
message: `${getLabel(547283, res.data)}`,
description: `${getLabel(547292, res.data)}`,
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
});
3 years ago
});
}, []);
// 获取数据
useEffect(() => {
if (lebelCompleted) {
d3.json(
'/api/bs/hrmorganization/orgchart/companyData?fclass=0&fisvitual=0&hidedept=0&root=0&level=2&id=0',
).then((data) => {
setData(data.data);
setHasRight(data?.hasRight);
});
}
}, [lebelCompleted]);
3 years ago
// ButtonContent渲染
const buttonContentRender = ({ node, state }) => {
if (node.children) {
return `<div style="border-radius:3px;padding:3px;font-size:10px;margin:auto auto;background-color:#66BAF5"> <div style="margin-top:0px;line-height:1.35;height:11px;font-size:25px; color: #fff;">ˆ</div> </div>`;
3 years ago
} else {
return `<div style="border-radius:3px;padding:3px;font-size:10px;margin:auto auto;background-color:#66BAF5"> <div style="margin-top:0px;line-height:1.35;height:11px;font-size:25px; color: #fff;transform:rotate(180deg)">ˆ</div> </div>`;
}
3 years ago
};
// 节点宽度渲染
const nodeWidthRender = (d) => {
if (d.data.ftype == 0) {
return 220;
3 years ago
} else if (d.data.ftype == 1) {
return 144;
3 years ago
} else if (d.data.ftype == 2) {
return 144;
}
return 200;
};
const nodeHeightRender = (d) => {
if (d.data.ftype == 0) {
return 100;
} else if (d.data.ftype == 1) {
return 106;
3 years ago
} else if (d.data.ftype == 2) {
return 106;
3 years ago
}
return 120;
};
const nodeContentRender = (d, i, arr, state) => {
2 years ago
let fclass = topbar.state.requestData.fclass;
let statisticsStyle = fclass == 0 ? 'block' : 'none';
let staffStyle = d.data.staffNum == 0 ? 'none' : 'inline-block';
3 years ago
if (d.data.ftype == 0) {
10 months ago
return `<div style="text-align: center;">
<div style="display: inline-block; margin-left: 5px;">
<div style="
font-size: 24px;
font-family: Microsoft YaHei-Bold, Microsoft YaHei;
font-weight: bold;
color: #000000;
line-height: 28px;
letter-spacing: 1px;
margin-top: 10px;
">${d.data.fname}(${d.data.staffNum}/${d.data.onJobNum})</div>
</div>
3 years ago
</div>`;
} else if (d.data.ftype == 1) {
return `<div style='position:absolute;height:100%'>
2 years ago
<img style='width:144px;height:106px' src="${getSubcompanyImage(
d.data.fisvitual,
)}"/>
</div>
<div style="width: 144px;height: 80px;top: 35px;position: relative;font-weight: 400;font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;color: #333333;text-align: center;">
<div class="tooltip">
<div class="tooltitle"> ${d.data.fname} </div>
<div class="tooltiptext">${d.data.fname}</div>
</div>
2 years ago
<div style="display: ${statisticsStyle}">
<span style="color:red;display: ${staffStyle}">${
d.data.staffNum
} /</span>
2 years ago
<span style="color:green">${d.data.onJobNum}</span>
</div>
</div>
</div>
`;
3 years ago
} else if (d.data.ftype == 2) {
return `
<div style="width: 100%; height: 100%; background-size: 100% 100%;">
<div style='position:absolute;height:100%'>
2 years ago
<img style='width:144px;height:106px' src="${getDepartmentImage(
d.data.fisvitual,
)}"/>
</div>
<div style="width: 144px;height: 80px;top: 35px;position: relative;font-weight: 400;font-size: 14px;
font-family: Microsoft YaHei-Regular, Microsoft YaHei;color: #333333;text-align: center;">
<div class="tooltip">
<div class="tooltitle"> ${d.data.fname} </div>
<div class="tooltiptext">${d.data.fname}</div>
</div>
2 years ago
<div style="display: ${statisticsStyle}">
<span style="color:red;display: ${staffStyle}">${
d.data.staffNum
} /</span>
2 years ago
<span style="color:green">${d.data.onJobNum}</span>
</div>
</div>
</div>
3 years ago
`;
}
3 years ago
return `<div>${d.data.fname}</div>`;
};
const handleTopLayoutClick = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart &&
orgChart
.layout('top')
.setCentered(orgChart.getChartState().root.id)
.render();
active = 'top';
3 years ago
};
const handleLeftLayoutClick = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart &&
orgChart
.layout('left')
.setCentered(orgChart.getChartState().root.id)
.render();
active = 'left';
3 years ago
};
2 years ago
const handleFullscreen = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart && orgChart.fullscreen('body');
};
const handleFit = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart && orgChart.fit();
};
const handleFolderAddNode = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
operateCom &&
operateCom.showOperate(topbar.state.requestData.root, '新增节点', 1);
};
const addFolderNode = (id) => {
orgChart &&
orgChart.addNode({
id: 'd_10091',
fname: '测试增加节点',
parentId: 's_10',
ftype: '2',
});
};
const handleDeleteNode = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
operateCom &&
operateCom.showOperate(topbar.state.requestData.root, '删除节点', 2);
};
const deleteNode = (id) => {
orgChart && orgChart.removeNode('d_10091');
};
2 years ago
const handleCompact = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart &&
orgChart
.compact(!!(compact++ % 2))
.render()
.fit();
};
const handleExpandAll = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart && expandAll++ % 2 ? orgChart.collapseAll() : orgChart.expandAll();
};
3 years ago
const handleZoomIn = (progressBtn) => {
if (progressBtn) {
let top = parseInt(progressBtn.current.style.top) - 10;
if (top >= 0) {
progressBtn.current.style.top = top + 'px';
} else {
return;
}
}
3 years ago
orgChart && orgChart.zoomIn();
};
const handleZoomOut = (progressBtn) => {
if (progressBtn) {
let top = parseInt(progressBtn.current.style.top) + 10;
if (top <= 100) {
progressBtn.current.style.top = top + 'px';
} else {
return;
}
}
3 years ago
orgChart && orgChart.zoomOut();
};
const handleZoomBehavior = (value) => {
orgChart && orgChart.zoomBehavior(value - 50);
};
function downloadPdf(chart) {
chart.exportImg({
save: false,
full: true,
3 years ago
onLoad: (base64) => {
var pdf = new jsPDF();
var img = new Image();
img.src = base64;
img.onload = function () {
pdf.addImage(
img,
'JPEG',
5,
5,
595 / 3,
((img.height / img.width) * 595) / 3,
);
pdf.save('chart.pdf');
};
},
});
}
/**
* 导出
* @param {*} type
*/
3 years ago
const handleExport = (type) => {
if (type == 'png') {
const hiddenElements = document.querySelectorAll('.tooltitle');
const hiddenElementsArray = Array.from(hiddenElements);
// 从 DOM 中移除隐藏内容
hiddenElementsArray.forEach((el) => (el.style.display = 'none'));
3 years ago
orgChart && orgChart.exportImg({ full: true });
} else {
orgChart && downloadPdf(orgChart);
}
3 years ago
};
/**
* 时间轴点击
* @param {*} timeline
*/
const timeLineSearch = (timeline) => {
setTimelineId(timeline.id);
const fclass = topbar.state.requestData.fclass;
const resetParams = {
root: undefined,
level: '2',
fisvitual: '0',
hidedept: '0',
};
topbar.handleFormChange({ ...resetParams });
topbar.getNodeTreeNode(
`/api/bs/hrmorganization/orgchart/getSubCompanyTree?fclass=${fclass}&id=${timeline.id}`,
false,
);
let requestData = { fclass: fclass, id: timeline.id, ...resetParams };
handleSearch(requestData, false);
};
/**
* 查询
* @param {*} requestData
*/
const handleSearch = (requestData, cache = true) => {
setSpinning(true);
if (cache) {
requestData = { ...requestData, id: infoRef.current };
}
3 years ago
topBarSearchRequest = requestData;
let api =
'/api/bs/hrmorganization/orgchart/companyData' +
qs.stringify(requestData, { addQueryPrefix: true });
fetch(api)
.then((res) => res.json())
.then((data) => {
if (data.data) {
if (!data.data.length) {
setData([{}]);
message.warning(`${getLabel(547516, labelData)}`);
} else {
setData(data?.data);
}
}
2 years ago
setTimeout(function () {
setSpinning(false);
}, 200);
3 years ago
});
};
2 years ago
/**
* 切换维度
* @param {*} requestData
*/
const handleChange = (requestData) => {
setTimelineId(0);
timeLine.searchTimeLines(
`/api/bs/hrmorganization/orgchart/timeLines?fclass=${requestData.fclass}`,
);
requestData = { ...requestData, id: 0 };
handleSearch(requestData, false);
};
useEffect(() => {
if (active == 'left') {
orgChart &&
orgChart
.setCentered(orgChart.getChartState().root?.id)
.layout('left')
.render();
} else {
orgChart &&
orgChart
.setCentered(orgChart.getChartState().root?.id)
.layout('top')
.render();
}
}, [data]);
3 years ago
if (hasRight === false) {
return (
3 years ago
<div style={{ width: '100%', top: '40%', position: 'absolute' }}>
<img
style={{ display: 'block', margin: '0 auto' }}
src="./img/permission.png"
/>
<p style={{ textAlign: 'center' }}>{getLabel(547515, labelData)}</p>
3 years ago
</div>
);
}
return (
hasRight &&
Object.keys(labelData).length != 0 && (
<div className={styles.contentWrapper}>
3 years ago
<TopBar
ref={(r) => (topbar = r)}
3 years ago
onExport={(type) => {
handleExport(type);
}}
onSearch={(requestData) => {
handleSearch(requestData);
}}
changeFclass={(requestData) => {
handleChange(requestData);
}}
type="company"
url="/api/bs/hrmorganization/orgchart/getCondition?fclass=0&type=company&id=0"
labelData={labelData}
/>
3 years ago
<ToolBar
onTopLayoutClick={(progressBtn) => handleTopLayoutClick(progressBtn)}
3 years ago
onLeftLayoutClick={(progressBtn) =>
handleLeftLayoutClick(progressBtn)
}
2 years ago
onFullscreen={(progressBtn) => handleFullscreen(progressBtn)}
onFit={(progressBtn) => handleFit(progressBtn)}
onFolderAddNode={(progressBtn) => handleFolderAddNode(progressBtn)}
onDeleteNode={(progressBtn) => handleDeleteNode(progressBtn)}
2 years ago
onCompact={(progressBtn) => handleCompact(progressBtn)}
onExpandAll={(progressBtn) => handleExpandAll(progressBtn)}
onZoomOut={(progressBtn) => handleZoomOut(progressBtn)}
onZoomIn={(progressBtn) => handleZoomIn(progressBtn)}
onZoomBehavior={(value) => handleZoomBehavior(value)}
/>
{/* <TimeLine
ref={(r) => (timeLine = r)}
onClick={(timeline) => {
timeLineSearch(timeline);
}}
url={'/api/bs/hrmorganization/orgchart/timeLines?fclass=0'}
labelData={labelData}
/> */}
<Spin size="large" spinning={spinning}>
<OrgChartComponent
setChart={(chart) => (orgChart = chart)}
setClick={(click) => (addNodeChildFunc = click)}
onNodeClick={onNodeClick}
data={data}
onButtonClick={onButtonClick}
buttonContent={buttonContentRender}
nodeWidth={nodeWidthRender}
nodeHeight={nodeHeightRender}
nodeContent={nodeContentRender}
/>
</Spin>
<DrawerComponents ref={(r) => (drawerCom = r)} labelData={labelData} />
<OperateDialog
ref={(r) => (operateCom = r)}
addFolderNode={addFolderNode}
deleteNode={deleteNode}
labelData={labelData}
/>
</div>
3 years ago
)
);
}