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

475 lines
14 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import styles from './index.less';
import React, { useEffect, useState, useRef } from 'react';
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';
import jsPDF from 'jspdf';
import moment from 'moment';
import qs from 'qs';
import { message, Spin } from 'antd';
let active = 'top';
let drawerCom = null;
let operateCom = null;
let timeLine = null;
let orgChart = null;
let topbar = null;
export default function companyPage() {
const [data, setData] = useState(null);
let compact = 0;
let expandAll = 0;
const [sliderProgress, setSliderProgress] = useState(50);
let addNodeChildFunc = null;
let topBarSearchRequest = null;
const [hasRight, setHasRight] = useState('');
const [timelineId, setTimelineId] = useState(0);
const infoRef = useRef();
useEffect(() => {
infoRef.current = timelineId;
}, [timelineId]);
const [spinning, setSpinning] = useState(false);
// 点击节点
const onNodeClick = (node) => {
if (node.ftype == '2') {
const params = {
rootId: node.id,
fclass: topbar.state.requestData.fclass,
id: infoRef.current,
};
drawerCom.showDrawer(params);
}
};
// 扩展按钮点击
const onButtonClick = (event, d) => {
if (d.children) {
let idsList = [];
d.children.forEach((item) => {
if (item.data.hasChildren && !item._children) {
idsList.push(item.data.id);
}
});
if (idsList.length == 0) {
return;
}
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 {
api =
'/api/bs/hrmorganization/orgchart/asyncCompanyData?fclass=0&fisvitual=0&id=0&root=0&ids=' +
idsStr;
}
fetch(api)
.then((res) => res.json())
.then((data) => {
if (data.data) {
data.data.forEach((item) => {
// window.chart.addNode(item);
orgChart.addNode(item);
});
}
});
}
};
// 获取部门图片
function getDepartmentImage(fisvitual) {
return fisvitual == '0' ? `./img/back/level4.png` : `./img/back/level8.png`;
}
// 获取分部图片
function getSubcompanyImage(fisvitual) {
return fisvitual == '0' ? `./img/back/level1.png` : `./img/back/level5.png`;
}
// 获取数据
useEffect(() => {
d3.json(
'/api/bs/hrmorganization/orgchart/companyData?fclass=0&fisvitual=0&root=0&level=2&id=0',
).then((data) => {
setData(data.data);
setHasRight(data?.hasRight);
});
}, [true]);
// 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>`;
} 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>`;
}
};
// 节点宽度渲染
const nodeWidthRender = (d) => {
if (d.data.ftype == 0) {
return 1000;
} else if (d.data.ftype == 1) {
return 160;
} 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;
} else if (d.data.ftype == 2) {
return 106;
}
return 120;
};
const nodeContentRender = (d, i, arr, state) => {
if (d.data.ftype == 0) {
return `<div style="text-align:center">
<div style="display: inline-block; vertical-align: top;">
<img src="./img/company.png" />
</div>
<div style="display: inline-block; text-align: center; 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}</div>
</div>
</div>`;
} else if (d.data.ftype == 1) {
return `<div style='position:absolute;height:100%'>
<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 title=${
d.data.fname
} style="width: 110px;margin: 0 auto;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;-o-text-overflow:ellipsis;
line-height: 18px;word-break: break-all;">${d.data.fname}</div>
</div>`;
} else if (d.data.ftype == 2) {
return `
<div style="width: 100%; height: 100%; background-size: 100% 100%;">
<div style='position:absolute;height:100%'>
<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 style="width: 110px;margin: 0 auto;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;
line-height: 18px;word-break: break-all;">${d.data.fname}</div>
</div>
</div>
`;
}
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';
};
const handleLeftLayoutClick = (progressBtn) => {
progressBtn.current.style.top = 50 + 'px';
orgChart &&
orgChart
.layout('left')
.setCentered(orgChart.getChartState().root.id)
.render();
active = 'left';
};
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');
};
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();
};
const handleZoomIn = (progressBtn) => {
if (progressBtn) {
let top = parseInt(progressBtn.current.style.top) - 10;
if (top >= 0) {
progressBtn.current.style.top = top + 'px';
} else {
return;
}
}
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;
}
}
orgChart && orgChart.zoomOut();
};
const handleZoomBehavior = (value) => {
orgChart && orgChart.zoomBehavior(value - 50);
};
function downloadPdf(chart) {
chart.exportImg({
save: false,
full: true,
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
*/
const handleExport = (type) => {
if (type == 'png') {
orgChart && orgChart.exportImg({ full: true });
} else {
orgChart && downloadPdf(orgChart);
}
};
/**
* 时间轴点击
* @param {*} timeline
*/
const timeLineSearch = (timeline) => {
setTimelineId(timeline.id);
const fclass = topbar.state.requestData.fclass;
const resetParams = { root: undefined, level: '2', fisvitual: '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 };
}
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('暂无数据');
} else {
setData(data?.data);
}
}
setTimeout(function () {
setSpinning(false);
}, 200);
});
};
/**
* 切换维度
* @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]);
if (hasRight === false) {
return (
<div style={{ width: '100%', top: '40%', position: 'absolute' }}>
<img
style={{ display: 'block', margin: '0 auto' }}
src="./img/permission.png"
/>
<p style={{ textAlign: 'center' }}>对不起您暂时没有权限!</p>
</div>
);
}
return (
hasRight && (
<div className={styles.contentWrapper}>
<TopBar
ref={(r) => (topbar = r)}
onExport={(type) => {
handleExport(type);
}}
onSearch={(requestData) => {
handleSearch(requestData);
}}
changeFclass={(requestData) => {
handleChange(requestData);
}}
type="company"
url="/api/bs/hrmorganization/orgchart/getCondition?fclass=0&type=company"
/>
<ToolBar
onTopLayoutClick={(progressBtn) => handleTopLayoutClick(progressBtn)}
onLeftLayoutClick={(progressBtn) =>
handleLeftLayoutClick(progressBtn)
}
onFullscreen={(progressBtn) => handleFullscreen(progressBtn)}
onFit={(progressBtn) => handleFit(progressBtn)}
onFolderAddNode={(progressBtn) => handleFolderAddNode(progressBtn)}
onDeleteNode={(progressBtn) => handleDeleteNode(progressBtn)}
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'}
/>
<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)} />
<OperateDialog
ref={(r) => (operateCom = r)}
addFolderNode={addFolderNode}
deleteNode={deleteNode}
/>
</div>
)
);
}