feature/cl #7

Merged
liang.cheng merged 8 commits from feature/cl into master 2 years ago

@ -13,14 +13,15 @@ export default defineConfig({
{ path: '/user', component: '@/pages/user' }, { path: '/user', component: '@/pages/user' },
{ path: '/company', component: '@/pages/company' }, { path: '/company', component: '@/pages/company' },
{ path: '/dragtree', component: '@/pages/dragTree' }, { path: '/dragtree', component: '@/pages/dragTree' },
{ path: '/statistics', component: '@/pages/statisticsTable' },
], ],
fastRefresh: {}, fastRefresh: {},
antd: {}, antd: {},
proxy: { proxy: {
'/api': { '/api': {
// 标识需要进行转换的请求的url // 标识需要进行转换的请求的url
//target: 'http://127.0.0.1:8686/api', // 服务端域名 / http://localhost:8686 target: 'http://127.0.0.1:8686/api', // 服务端域名
target: 'http://221.226.25.34:11080/api', //target: 'http://221.226.25.34:11080/api',
changeOrigin: true, // 允许域名进行转换 changeOrigin: true, // 允许域名进行转换
pathRewrite: { '^/api': '' }, // 将请求url里的ci去掉 pathRewrite: { '^/api': '' }, // 将请求url里的ci去掉
}, },

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@ -1,25 +1,39 @@
import { Form, Input, Modal, TreeSelect, message } from 'antd'; import { Form, Input, Modal, TreeSelect, message, Checkbox } from 'antd';
import React, { useState, useEffect } from 'react'; import React, {
useState,
useEffect,
useRef,
forwardRef,
useImperativeHandle,
} from 'react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import './index.less'; import './index.less';
import { HomeOutlined } from '@ant-design/icons';
import './index.less';
const layout = { const layout = {
labelCol: { span: 6 }, labelCol: { span: 8 },
wrapperCol: { span: 14 }, wrapperCol: { span: 14 },
}; };
const CopyDialog = ({ open, onCreate, onCancel }) => { const CopyDialog = forwardRef(({ open, onCreate, onCancel }, ref) => {
const [treeData, setData] = useState([]);
const [form] = Form.useForm(); const [form] = Form.useForm();
const formRef = useRef(null);
const [treeData, setData] = useState([]); useImperativeHandle(ref, () => ({
console.log(treeData); getTreeData() {
useEffect(() => { form.resetFields();
d3.json('/api/bs/hrmorganization/orgchart/getSubCompanyTree').then( d3.json('/api/bs/hrmorganization/orgchart/getSubCompanyTree').then(
(data) => { (data) => {
setData(data.companyTree); data.companyTree.map((item, index) => {
}, item.icon = <HomeOutlined />;
); });
}, [true]); setData(data.companyTree);
},
);
},
}));
/** /**
* 根节点树异步加载 * 根节点树异步加载
@ -33,7 +47,9 @@ const CopyDialog = ({ open, onCreate, onCancel }) => {
d3.json( d3.json(
`/api/bs/hrmorganization/orgchart/getSubCompanyTree?subcompany=${id}`, `/api/bs/hrmorganization/orgchart/getSubCompanyTree?subcompany=${id}`,
).then((data) => { ).then((data) => {
debugger; data.companyTree.map((item, index) => {
item.icon = <HomeOutlined />;
});
let arr = [...treeData, ...data.companyTree]; let arr = [...treeData, ...data.companyTree];
setData(arr); setData(arr);
}); });
@ -59,41 +75,38 @@ const CopyDialog = ({ open, onCreate, onCancel }) => {
}); });
}} }}
> >
<Form {...layout} form={form} name="form_in_modal"> <Form ref={formRef} {...layout} form={form} name="form_in_modal">
<Form.Item <Form.Item
name="department" name="company"
label="合并到部门" label="复制到分部"
rules={[ rules={[
{ {
required: true, required: true,
message: '【合并到部门】为必填项!', message: '【复制到分部】为必填项!',
}, },
]} ]}
> >
<TreeSelect <TreeSelect
className="custom-tree-select"
treeDataSimpleMode treeDataSimpleMode
allowClear allowClear
style={{ width: '100%' }} style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
loadData={onRootLoadData} loadData={onRootLoadData}
treeData={treeData} treeData={treeData}
treeIcon
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="mergeName" name="copySubDept"
label="合并后名称" valuePropName="checked"
rules={[ label="是否复制子部门信息"
{
required: true,
message: '【合并后的名称】为必填项!',
},
]}
> >
<Input /> <Checkbox />
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>
); );
}; });
export default CopyDialog; export default CopyDialog;

@ -1,3 +1,5 @@
.ant-tree .ant-tree-treenode-disabled .ant-tree-node-content-wrapper { .ant-select-tree
color: #222526; .ant-select-tree-treenode-disabled
.ant-select-tree-node-content-wrapper {
color: rgba(0, 0, 0, 0.85);
} }

@ -8,6 +8,8 @@ import React, {
} from 'react'; } from 'react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import './index.less'; import './index.less';
import { HomeOutlined, FolderOutlined } from '@ant-design/icons';
import './index.less';
const layout = { const layout = {
labelCol: { span: 6 }, labelCol: { span: 6 },
@ -19,11 +21,16 @@ const MergeDialog = forwardRef(({ open, onCreate, onCancel }, ref) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const formRef = useRef(null); const formRef = useRef(null);
const [treeLine, setTreeLine] = useState(true);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
getTreeData() { getTreeData() {
form.resetFields(); form.resetFields();
d3.json('/api/bs/hrmorganization/orgchart/getDepartmentTree').then( d3.json('/api/bs/hrmorganization/orgchart/getDepartmentTree').then(
(data) => { (data) => {
data.departmentTree.map((item, index) => {
item.icon = <HomeOutlined />;
});
setData(data.departmentTree); setData(data.departmentTree);
}, },
); );
@ -42,6 +49,13 @@ const MergeDialog = forwardRef(({ open, onCreate, onCancel }, ref) => {
d3.json( d3.json(
`/api/bs/hrmorganization/orgchart/getDepartmentTree?subcompany=${id}`, `/api/bs/hrmorganization/orgchart/getDepartmentTree?subcompany=${id}`,
).then((data) => { ).then((data) => {
data.departmentTree.map((item, index) => {
if (item.key.indexOf('d') > -1) {
item.icon = <FolderOutlined />;
} else {
item.icon = <HomeOutlined />;
}
});
let arr = [...treeData, ...data.departmentTree]; let arr = [...treeData, ...data.departmentTree];
setData(arr); setData(arr);
}); });
@ -79,12 +93,14 @@ const MergeDialog = forwardRef(({ open, onCreate, onCancel }, ref) => {
]} ]}
> >
<TreeSelect <TreeSelect
className="custom-tree-select"
treeDataSimpleMode treeDataSimpleMode
allowClear allowClear
style={{ width: '100%' }} style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
loadData={onRootLoadData} loadData={onRootLoadData}
treeData={treeData} treeData={treeData}
treeIcon
/> />
</Form.Item> </Form.Item>

@ -181,8 +181,8 @@ export default class DrawerComponents extends React.Component {
工号: dataSource[i].workCode, 工号: dataSource[i].workCode,
姓名: dataSource[i].lastName, 姓名: dataSource[i].lastName,
性别: dataSource[i].sex, 性别: dataSource[i].sex,
部门: dataSource[i].departmentId, 部门: dataSource[i].departmentName,
分部: dataSource[i].subcompanyid1, 分部: dataSource[i].subcompanyName,
岗位: dataSource[i].jobTitle, 岗位: dataSource[i].jobTitle,
手机号: dataSource[i].mobile, 手机号: dataSource[i].mobile,
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -2,19 +2,22 @@
* @Author: Chengliang 1546584672@qq.com * @Author: Chengliang 1546584672@qq.com
* @Date: 2023-06-25 16:33:21 * @Date: 2023-06-25 16:33:21
* @LastEditors: Chengliang 1546584672@qq.com * @LastEditors: Chengliang 1546584672@qq.com
* @LastEditTime: 2023-06-29 14:24:04 * @LastEditTime: 2023-09-14 17:48:39
* @FilePath: /org-chart-frant/src/components/timeline/index.jsx * @FilePath: /org-chart-frant/src/components/timeline/index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import React from 'react'; import React from 'react';
import { Timeline } from 'antd'; import { Timeline, Drawer } from 'antd';
import styles from './index.less'; import styles from './index.less';
import leftTreeShow from './img/leftTree-show.png';
import leftHide from './img/leftTree-hide.png';
export default class TimeLine extends React.Component { export default class TimeLine extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
timelineList: [], timelineList: [],
open: true,
}; };
} }
@ -46,26 +49,61 @@ export default class TimeLine extends React.Component {
}); });
} }
setOpen = () => {
this.setState({
open: !this.state.open,
});
};
render() { render() {
let showStyle = {};
let positionStyle = {};
if (this.state.open) {
showStyle = {
display: 'block',
};
positionStyle = {
left: '219px',
background: `url(${leftTreeShow}) no-repeat -2px 0`,
};
} else {
showStyle = {
display: 'none',
};
positionStyle = {
left: '0',
background: `url(${leftHide}) no-repeat -2px 0`,
};
}
return ( return (
<div className={styles.lineWrapper}> <>
<Timeline> <div
{this.state.timelineList.map((item) => { className={styles.leftRightLayoutBtn}
return ( style={positionStyle}
<Timeline.Item onClick={this.setOpen}
key={item.key} ></div>
onClick={this.handleLineClick.bind(this, item)} <div className={styles.lineWrapper} style={showStyle}>
className={styles.timeline} <Timeline>
color={item.color} {this.state.timelineList.map((item) => {
style={{ color: item.color == 'blue' ? '#1890ff' : 'dimgray' }} return (
> <Timeline.Item
<div>{item.title}</div> key={item.key}
<div className={styles.time}>{item.time}</div> onClick={this.handleLineClick.bind(this, item)}
</Timeline.Item> className={styles.timeline}
); color={item.color}
})} style={{
</Timeline> color: item.color == 'blue' ? '#1890ff' : 'dimgray',
</div> }}
>
<div>{item.title}</div>
<div className={styles.time}>{item.time}</div>
</Timeline.Item>
);
})}
</Timeline>
</div>
</>
); );
} }
} }

@ -24,6 +24,21 @@
} }
} }
.leftRightLayoutBtn {
width: 18px;
height: 60px;
position: absolute;
top: 50%;
margin-top: -30px;
z-index: 101;
cursor: pointer;
}
.leftRightLayoutBtn :hover {
background-image: url(./img/leftTree-show-hover.png) no-repeat -2px 0 !important;
background-color: #1890ff;
}
.lineWrapper::-webkit-scrollbar { .lineWrapper::-webkit-scrollbar {
/*滚动条整体样式*/ /*滚动条整体样式*/
width: 10px; width: 10px;

@ -35,6 +35,7 @@ export class TopBar extends React.Component {
root: undefined, root: undefined,
level: '2', level: '2',
fisvitual: '0', fisvitual: '0',
hidedept: '0',
}, },
open: false, open: false,
confirmLoading: false, confirmLoading: false,
@ -201,6 +202,7 @@ export class TopBar extends React.Component {
root: undefined, root: undefined,
level: '2', level: '2',
fisvitual: '0', fisvitual: '0',
hidedept: '0',
}; };
this.handleFormChange(requestData); this.handleFormChange(requestData);
this.getNodeTreeNode( this.getNodeTreeNode(
@ -234,22 +236,22 @@ export class TopBar extends React.Component {
<Col span={6}> <Col span={6}>
<Checkbox <Checkbox
style={{ marginTop: '5px', marginLeft: 100 }} style={{ marginTop: '5px', marginLeft: 100 }}
checked={this.state.requestData.fisvitual == '1'} checked={this.state.requestData.hidedept == '1'}
onChange={(e) => onChange={(e) =>
this.handleFormChange({ this.handleFormChange({
fisvitual: e.target.checked ? '1' : '0', hidedept: e.target.checked ? '1' : '0',
}) })
} }
> >
显示虚拟组织 隐藏部门
</Checkbox> </Checkbox>
<Tooltip <Tooltip
title="提示:若启用虚拟组织,需要在分部自定义表增加字段(名称 fblx) 字段类型 下拉框(0实体 1虚拟) 部门自定义表同上(字段名称 bmlx)。" title="提示:开启后将只显示分部组织架构!!!"
color="#FF7F00" color="#0082fb"
placement="rightTop" placement="rightTop"
> >
<QuestionCircleOutlined <QuestionCircleOutlined
style={{ color: '#FF7F00', cursor: 'pointer', fontSize: 16 }} style={{ color: '#0082fb', cursor: 'pointer', fontSize: 16 }}
/> />
</Tooltip> </Tooltip>
</Col> </Col>
@ -269,7 +271,29 @@ export class TopBar extends React.Component {
</Col> </Col>
</Row> </Row>
<Row style={{ marginTop: '15px' }}> <Row style={{ marginTop: '15px' }}>
<Col span={8}> <Col span={6}>
<Checkbox
style={{ marginTop: '5px' }}
checked={this.state.requestData.fisvitual == '1'}
onChange={(e) =>
this.handleFormChange({
fisvitual: e.target.checked ? '1' : '0',
})
}
>
显示虚拟组织
</Checkbox>
<Tooltip
title="提示:若启用虚拟组织,需要在分部自定义表增加字段(名称 fblx) 字段类型 下拉框(0实体 1虚拟) 部门自定义表同上(字段名称 bmlx)。"
color="#0082fb"
placement="rightTop"
>
<QuestionCircleOutlined
style={{ color: '#0082fb', cursor: 'pointer', fontSize: 16 }}
/>
</Tooltip>
</Col>
<Col span={16}>
<Button <Button
type="primary" type="primary"
style={{ marginRight: '10px' }} style={{ marginRight: '10px' }}
@ -290,6 +314,24 @@ export class TopBar extends React.Component {
> >
查询 查询
</Button> </Button>
<Button
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
window.open('#/dragtree', 'blank');
}}
>
组织调整
</Button>
<Button
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
window.open('#/statistics', 'blank');
}}
>
人数统计
</Button>
<Dropdown overlay={this.menu}> <Dropdown overlay={this.menu}>
<Button type="primary">导出</Button> <Button type="primary">导出</Button>
</Dropdown> </Dropdown>

@ -1,5 +0,0 @@
import React from 'react'
export default () => {
return <div>Hello</div>
}

@ -10,7 +10,8 @@ import OperateDialog from '../components/dialog';
import jsPDF from 'jspdf'; import jsPDF from 'jspdf';
import moment from 'moment'; import moment from 'moment';
import qs from 'qs'; import qs from 'qs';
import { message, Spin } from 'antd'; import { message, Spin, notification } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
let active = 'top'; let active = 'top';
let drawerCom = null; let drawerCom = null;
@ -31,6 +32,14 @@ export default function companyPage() {
const [timelineId, setTimelineId] = useState(0); const [timelineId, setTimelineId] = useState(0);
const infoRef = useRef(); const infoRef = useRef();
useEffect(() => {
notification.open({
message: '提示',
description:
'组织架构图中编制数和在编数显示初始化需参考文档配置定时任务并执行!!!(编制数默认取本年度最新编制信息,人数统计展示仅限于行政维度)',
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
});
}, []);
useEffect(() => { useEffect(() => {
infoRef.current = timelineId; infoRef.current = timelineId;
}, [timelineId]); }, [timelineId]);
@ -100,7 +109,7 @@ export default function companyPage() {
// //
useEffect(() => { useEffect(() => {
d3.json( d3.json(
'/api/bs/hrmorganization/orgchart/companyData?fclass=0&fisvitual=0&root=0&level=2&id=0', '/api/bs/hrmorganization/orgchart/companyData?fclass=0&fisvitual=0&hidedept=0&root=0&level=2&id=0',
).then((data) => { ).then((data) => {
setData(data.data); setData(data.data);
setHasRight(data?.hasRight); setHasRight(data?.hasRight);
@ -140,6 +149,8 @@ export default function companyPage() {
}; };
const nodeContentRender = (d, i, arr, state) => { const nodeContentRender = (d, i, arr, state) => {
let fclass = topbar.state.requestData.fclass;
let statisticsStyle = fclass == 0 ? 'block' : 'none';
if (d.data.ftype == 0) { if (d.data.ftype == 0) {
return `<div> return `<div>
<div style="display: inline-block; text-align: center; margin-left: 5px;"> <div style="display: inline-block; text-align: center; margin-left: 5px;">
@ -166,7 +177,13 @@ export default function companyPage() {
d.data.fname d.data.fname
} style="width: 110px;margin: 0 auto;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;-o-text-overflow:ellipsis; } 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> line-height: 18px;word-break: break-all;">${d.data.fname}</div>
</div>`; <div style="display: ${statisticsStyle}">
<span style="color:red">${d.data.staffNum}</span> /
<span style="color:green">${d.data.onJobNum}</span>
</div>
</div>
</div>
`;
} else if (d.data.ftype == 2) { } else if (d.data.ftype == 2) {
return ` return `
<div style="width: 100%; height: 100%; background-size: 100% 100%;"> <div style="width: 100%; height: 100%; background-size: 100% 100%;">
@ -179,6 +196,10 @@ export default function companyPage() {
font-family: Microsoft YaHei-Regular, Microsoft YaHei;color: #333333;text-align: center;"> 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; <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> line-height: 18px;word-break: break-all;">${d.data.fname}</div>
<div style="display: ${statisticsStyle}">
<span style="color:red">${d.data.staffNum}</span> /
<span style="color:green">${d.data.onJobNum}</span>
</div>
</div> </div>
</div> </div>
`; `;
@ -326,7 +347,12 @@ export default function companyPage() {
const timeLineSearch = (timeline) => { const timeLineSearch = (timeline) => {
setTimelineId(timeline.id); setTimelineId(timeline.id);
const fclass = topbar.state.requestData.fclass; const fclass = topbar.state.requestData.fclass;
const resetParams = { root: undefined, level: '2', fisvitual: '0' }; const resetParams = {
root: undefined,
level: '2',
fisvitual: '0',
hidedept: '0',
};
topbar.handleFormChange({ ...resetParams }); topbar.handleFormChange({ ...resetParams });
topbar.getNodeTreeNode( topbar.getNodeTreeNode(
`/api/bs/hrmorganization/orgchart/getSubCompanyTree?fclass=${fclass}&id=${timeline.id}`, `/api/bs/hrmorganization/orgchart/getSubCompanyTree?fclass=${fclass}&id=${timeline.id}`,

@ -1,25 +1,31 @@
import { Tree, message, Modal, Popconfirm, Spin } from 'antd'; import { Tree, message, Modal, Popconfirm, Spin, Layout, Drawer } from 'antd';
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import qs from 'qs'; import qs from 'qs';
import MergeDialog from '../components/dialog/mergeDialog'; import MergeDialog from '../components/dialog/mergeDialog';
import CopyDialog from '../components/dialog/copyDialog'; import CopyDialog from '../components/dialog/copyDialog';
import inset from '../../public/img/back/inset.png';
import { import {
HomeOutlined, HomeOutlined,
FolderOutlined, FolderOutlined,
ClusterOutlined, ClusterOutlined,
ApartmentOutlined, ApartmentOutlined,
QuestionCircleOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
const { Header, Footer, Sider, Content } = Layout;
import './index.less'; import './index.less';
const DragTree = () => { const DragTree = () => {
const [gData, setGData] = useState([]); const [gData, setGData] = useState([]);
const [expandedKeys, setExpandedKeys] = useState([undefined]); const [expandedKeys, setExpandedKeys] = useState([undefined]);
const childRef = useRef(null); const childRef = useRef(null);
const [tip, setTip] = useState('正在加载...'); const copyChildRef = useRef(null);
const [tip, setTip] = useState('正在加载,请稍候...');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [showCanceled, setShowCanceled] = useState(0); const [showCanceled, setShowCanceled] = useState(0);
const [drawerOpen, setDrawerOpen] = useState(false);
const [iframe, setIframe] = useState('');
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [mergeId, setMergeId] = useState(null); const [mergeId, setMergeId] = useState(null);
@ -30,10 +36,10 @@ const DragTree = () => {
getMoveTree(0); getMoveTree(0);
}, [true]); }, [true]);
const getMoveTree = (showCanceled) => { const getMoveTree = (showCanceled, expandedKeys = '') => {
setLoading(true); setLoading(true);
d3.json( d3.json(
`/api/bs/hrmorganization/orgchart/getMovingTree?showCanceled=${showCanceled}`, `/api/bs/hrmorganization/orgchart/getMovingTree?showCanceled=${showCanceled}&expandedKeys=${expandedKeys}`,
).then((data) => { ).then((data) => {
setGData(data.movingTree); setGData(data.movingTree);
setExpandedKeys(data.expandedKeys); setExpandedKeys(data.expandedKeys);
@ -59,79 +65,95 @@ const DragTree = () => {
}; };
const onDrop = (info) => { const onDrop = (info) => {
setLoading(true);
setTip('正在转移,请稍候...');
const dropKey = info.node.key; const dropKey = info.node.key;
const dragKey = info.dragNode.key; const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-'); const dropPos = info.node.pos.split('-');
const dropPosition = const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]); info.dropPosition - Number(dropPos[dropPos.length - 1]);
fetch('/api/bs/hrmorganization/dept/dragDepartment', {
method: 'POST', if (dropPosition == -1) {
headers: { return message.error('不支持该操作!!!', 2);
'Content-Type': 'application/json', }
}, let pos = dropPosition == 0 ? '内部' : '下方';
body: JSON.stringify({
sourcekey: dragKey, let title = `确定将【${info.dragNode.title}】移到 【${info.node.title}${pos}`;
targetkey: dropKey, Modal.confirm({
dropPosition: dropPosition, title: '转移操作',
}), content: title,
}) okText: '确认',
.then((response) => response.json()) cancelText: '取消',
.then((res) => { onOk() {
if (res.code == 200) { setLoading(true);
const data = [...gData]; setTip('正在转移,请稍候...');
// Find dragObject
let dragObj; fetch('/api/bs/hrmorganization/dept/dragDepartment', {
loop(data, dragKey, (item, index, arr) => { method: 'POST',
arr.splice(index, 1); headers: {
dragObj = item; 'Content-Type': 'application/json',
}); },
if (!info.dropToGap) { body: JSON.stringify({
// Drop on the content sourcekey: dragKey,
loop(data, dropKey, (item) => { targetkey: dropKey,
item.children = item.children || []; dropPosition: dropPosition,
// where to insert }),
item.children.unshift(dragObj); })
}); .then((response) => response.json())
} else if ( .then((res) => {
(info.node.props.children || []).length > 0 && if (res.code == 200) {
// Has children const data = [...gData];
info.node.props.expanded && // Find dragObject
// Is expanded let dragObj;
dropPosition === 1 // On the bottom gap loop(data, dragKey, (item, index, arr) => {
) { arr.splice(index, 1);
loop(data, dropKey, (item) => { dragObj = item;
item.children = item.children || []; });
// where to insert if (!info.dropToGap) {
item.children.unshift(dragObj); // Drop on the content
// in previous version, we use item.children.push(dragObj) to insert the loop(data, dropKey, (item) => {
// item to the tail of the children item.children = item.children || [];
}); // where to insert
} else { item.children.unshift(dragObj);
let ar = []; });
let i; } else if (
loop(data, dropKey, (_item, index, arr) => { (info.node.props.children || []).length > 0 &&
ar = arr; // Has children
i = index; info.node.props.expanded &&
}); // Is expanded
if (dropPosition === -1) { dropPosition === 1 // On the bottom gap
ar.splice(i, 0, dragObj); ) {
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert
item.children.unshift(dragObj);
// in previous version, we use item.children.push(dragObj) to insert the
// item to the tail of the children
});
} else {
let ar = [];
let i;
loop(data, dropKey, (_item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
setGData(data);
message.success('转移成功', 2);
} else { } else {
ar.splice(i + 1, 0, dragObj); message.warning(res.msg, 2);
} }
} setLoading(false);
setGData(data); })
} else { .catch((error) => {
message.warning(res.msg, 2); message.error('接口异常,请联系管理员');
} });
setLoading(false); },
}) onCancel() {},
.catch((error) => { });
message.error('接口异常,请联系管理员');
});
setTimeout(function () {}, 1000);
}; };
/** /**
@ -145,7 +167,7 @@ const DragTree = () => {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ ids: nodeData.id }), body: JSON.stringify({ ids: nodeData.id.substring(1) }),
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((res) => { .then((res) => {
@ -172,6 +194,7 @@ const DragTree = () => {
* @param {*} nodeData * @param {*} nodeData
*/ */
const onCancel = (nodeData) => { const onCancel = (nodeData) => {
setShowCanceled(0);
const extend = nodeData.type == '1' ? 'comp' : 'dept'; const extend = nodeData.type == '1' ? 'comp' : 'dept';
fetch(`/api/bs/hrmorganization/${extend}/updateForbiddenTagById`, { fetch(`/api/bs/hrmorganization/${extend}/updateForbiddenTagById`, {
method: 'POST', method: 'POST',
@ -179,7 +202,7 @@ const DragTree = () => {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
id: nodeData.id, id: nodeData.id.substring(1),
canceled: nodeData.canceled != '0', canceled: nodeData.canceled != '0',
}), }),
}) })
@ -187,11 +210,18 @@ const DragTree = () => {
.then((res) => { .then((res) => {
if (res.code == 200) { if (res.code == 200) {
const data = [...gData]; const data = [...gData];
loop(data, nodeData.key, (item, index, arr) => { if (nodeData.canceled != '0') {
arr.splice(index, 1); message.success('恢复成功', 2);
}); loop(data, nodeData.key, (item, index, arr) => {
arr[index].canceled = '0';
});
} else {
loop(data, nodeData.key, (item, index, arr) => {
arr.splice(index, 1);
});
message.success('封存成功', 2);
}
setGData(data); setGData(data);
message.success('封存成功', 2);
} else { } else {
message.warning(res.msg, 2); message.warning(res.msg, 2);
} }
@ -229,6 +259,7 @@ const DragTree = () => {
.then((response) => response.json()) .then((response) => response.json())
.then((res) => { .then((res) => {
if (res.code == 200) { if (res.code == 200) {
getMoveTree(0, values.department);
message.success('合并成功', 2); message.success('合并成功', 2);
} else { } else {
message.warning(res.msg, 2); message.warning(res.msg, 2);
@ -245,22 +276,31 @@ const DragTree = () => {
* @param {*} nodeData * @param {*} nodeData
*/ */
const onCopy = (nodeData) => { const onCopy = (nodeData) => {
if (copyChildRef.current) {
copyChildRef.current.getTreeData();
}
setCopyOpen(true); setCopyOpen(true);
setCopyId(nodeData.id); setCopyId(nodeData.id);
}; };
const onCopyCreate = (values) => { const onCopyCreate = (values) => {
let params = {
company: values.company,
copySubDept: values.copySubDept ? '1' : '0',
ids: copyId.substring(1),
};
fetch(`/api/bs/hrmorganization/dept/copyDepartment`, { fetch(`/api/bs/hrmorganization/dept/copyDepartment`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ ...values, ids: copyId }), body: JSON.stringify(params),
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((res) => { .then((res) => {
if (res.code == 200) { if (res.code == 200) {
setCopyOpen(false); setCopyOpen(false);
getMoveTree(0, values.company);
message.success('复制成功', 2); message.success('复制成功', 2);
} else { } else {
message.warning(res.msg, 2); message.warning(res.msg, 2);
@ -282,7 +322,6 @@ const DragTree = () => {
<> <>
{nodeData.type == 0 ? ( {nodeData.type == 0 ? (
<span> <span>
{' '}
<ClusterOutlined /> <ClusterOutlined />
<span style={{ marginLeft: '5px' }}>{nodeData.title}</span> <span style={{ marginLeft: '5px' }}>{nodeData.title}</span>
</span> </span>
@ -290,22 +329,33 @@ const DragTree = () => {
<> <>
<span> <span>
{icon} {icon}
<span style={{ marginLeft: '5px' }}>{nodeData.title}</span> <span style={{ marginLeft: '5px' }}>
{nodeData.title}
{nodeData.canceled == '1' ? (
<span style={{ color: 'red', marginLeft: '5px' }}>
(已封存)
</span>
) : (
''
)}
</span>
</span> </span>
<div id="drag-button-ops"> <div id="drag-button-ops">
<span <span
className="drag-button" className="drag-button"
onClick={() => onClick={() => {
window.open( setDrawerOpen(true);
`/spa/organization/static/index.html#/main/organization/${extend}/${nodeData.id}`, setIframe(
'_blank', `/spa/organization/static/index.html#/main/organization/${extend}/${nodeData.id.substring(
) 1,
} )}`,
);
}}
> >
查看 查看
</span> </span>
<Popconfirm <Popconfirm
title={`确认要删除 [${nodeData.title}] 吗?`} title={`确认要删除[${nodeData.title}] 吗?`}
onConfirm={() => onDelete(nodeData)} onConfirm={() => onDelete(nodeData)}
okText="确认" okText="确认"
cancelText="取消" cancelText="取消"
@ -343,43 +393,87 @@ const DragTree = () => {
); );
}; };
const onExpand = (info) => {
setExpandedKeys(info);
};
return ( return (
<> <div className="drag-wrapper">
<ApartmentOutlined <Spin tip={tip} spinning={loading}>
style={{ color: showCanceled == 0 ? '#1890ff' : '#eb2f96' }} <div className="drag-layout">
onClick={() => { <div className="drag-header">
const value = showCanceled == 0 ? 1 : 0; <img src={inset} />
setShowCanceled(value); <div>组织快速调整</div>
getMoveTree(value); </div>
<div className="drag-content">
<ApartmentOutlined
className="drag-showcanceled"
style={{ color: showCanceled == 0 ? '#000' : '#1890ff' }}
onClick={() => {
const value = showCanceled == 0 ? 1 : 0;
setTip('正在加载,请稍候...');
setShowCanceled(value);
getMoveTree(value);
}}
/>
<Tree
className="draggable-tree"
//defaultExpandedKeys={expandedKeys}
expandedKeys={expandedKeys}
onExpand={onExpand}
draggable
icon={false}
blockNode
onDragEnter={onDragEnter}
onDrop={onDrop}
treeData={gData}
titleRender={onTitleRender}
/>
</div>
<Drawer
width="60%"
placement="right"
closable={false}
onClose={() => setDrawerOpen(false)}
open={drawerOpen}
>
<iframe src={iframe} width="100%" height="100%" />
</Drawer>
<div className="drag-footer">
<p>
<QuestionCircleOutlined />
小提示
</p>
<div className="tips">
<div>1.鼠标拖拽Tree节点到任一分部部门下可快速完成组织转移;</div>
<div>2.点击查看侧滑打开组织详细信息,可快速编辑;</div>
<div>
3.鼠标悬停树节点 一键开启删除封存合并复制等功能;
</div>
<div>4.顶部小图标点击可显示已封存的组织架构</div>
</div>
</div>
</div>
</Spin>
<MergeDialog
ref={childRef}
open={open}
onCreate={onMergeCreate}
onCancel={() => {
setOpen(false);
}} }}
/> />
<Spin tip={tip} spinning={loading}> <CopyDialog
<Tree ref={copyChildRef}
className="draggable-tree" open={copyopen}
defaultExpandedKeys={expandedKeys}
draggable
icon={false}
blockNode
onDragEnter={onDragEnter}
onDrop={onDrop}
treeData={gData}
titleRender={onTitleRender}
/>
<MergeDialog
ref={childRef}
open={open}
onCreate={onMergeCreate}
onCancel={() => {
setOpen(false);
}}
/>
{/* <CopyDialog open={copyopen}
onCreate={onCopyCreate} onCreate={onCopyCreate}
onCancel={() => { onCancel={() => {
setCopyOpen(false); setCopyOpen(false);
}}/> */} }}
</Spin> />
</> </div>
); );
}; };
export default DragTree; export default DragTree;

@ -1,3 +1,7 @@
#root {
min-height: 100vh;
}
.title { .title {
background: rgb(121, 242, 157); background: rgb(121, 242, 157);
} }
@ -24,11 +28,11 @@
#drag-button-ops { #drag-button-ops {
display: none; display: none;
width: 500px;
height: 16px; height: 16px;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
margin-left: 100px; margin-left: 100px;
.drag-button { .drag-button {
display: inline-block; display: inline-block;
padding: 0 10px; padding: 0 10px;
@ -40,3 +44,76 @@
color: #22c8fb; color: #22c8fb;
} }
} }
.drag-wrapper {
min-height: inherit;
background-image: url('../../public/img/back/background.png') !important;
}
.drag-layout {
min-height: inherit;
padding: 25px;
.drag-header {
text-align: center;
div {
display: inline-block;
font-size: 25px;
font-weight: 400;
}
}
.drag-content {
width: auto;
background: #f8fafc;
border: 1px solid rgba(218, 237, 255, 1);
border-radius: 8px;
margin-top: 20px;
.drag-showcanceled {
font-size: 18px;
line-height: 30px;
margin-left: 5px;
margin-top: 5px;
}
}
.drag-footer {
margin-top: 15px;
background: #f8fafc;
border: 1px solid rgba(218, 237, 255, 1);
border-radius: 8px;
padding: 22px;
p {
span {
color: #178eff;
margin-right: 5px;
}
font-family: PingFangSC-Medium;
font-size: 14px;
color: #596378;
line-height: 24px;
font-weight: 500;
}
.tips {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #596378;
line-height: 21px;
font-weight: 400;
letter-spacing: 0.5px;
}
}
}
.ant-tree {
background: transparent;
}
.ant-drawer-body {
padding: 0px;
overflow: hidden;
}

@ -0,0 +1,89 @@
/*
* @Author: Chengliang 1546584672@qq.com
* @Date: 2023-09-11 15:33:27
* @LastEditors: Chengliang 1546584672@qq.com
* @LastEditTime: 2023-09-13 17:49:16
* @FilePath: /org-chart-frant/src/pages/statisticsTable.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { useEffect, useState, useRef } from 'react';
import * as d3 from 'd3';
import qs from 'qs';
import { Table } from 'antd';
const StatisticsTable = () => {
const [loading, setLoading] = useState(true);
const [dataSource, setDataSource] = useState([]);
const [columns, setColumns] = useState([]);
useEffect(() => {
const columns = [
{
title: '序号',
dataIndex: 'key',
key: 'key',
},
{
title: '名称',
dataIndex: 'dataIdName',
key: 'dataIdName',
},
{
title: '上级',
dataIndex: 'superIdName',
key: 'superIdName',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
render(value, row, index) {
if (value == 1) {
return '分部';
} else {
return '部门';
}
},
},
{
title: '在编数',
dataIndex: 'onJobNum',
key: 'onJobNum',
},
{
title: '编制数',
dataIndex: 'staffNum',
key: 'staffNum',
},
{
title: '创建人',
dataIndex: 'creator',
key: 'creator',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
},
{
title: '更新时间',
dataIndex: 'updateTime',
key: 'updateTime',
},
];
setColumns(columns);
d3.json(`/api/bs/hrmorganization/orgchart/selectStatistics`).then((res) => {
setDataSource(res.data.result);
setLoading(false);
});
}, [true]);
return (
<>
<div style={{ padding: '50px' }}>
<Table dataSource={dataSource} columns={columns} loading={loading} />;
</div>
</>
);
};
export default StatisticsTable;

@ -99,9 +99,8 @@ export default function userPage() {
// //
useEffect(() => { useEffect(() => {
document.cookie = document.cookie =
'ecology_JSessionid=aaaUMPsdM5DKIgXzYrwMy; JSESSIONID=aaaUMPsdM5DKIgXzYrwMy; __randcode__=f5b6cc86-28ff-416b-bc87-569246714d54; loginidweaver=1; languageidweaver=7; loginuuids=1'; 'ecology_JSessionid=aaaQRFwOgYRXPc88EZiQy; JSESSIONID=aaaQRFwOgYRXPc88EZiQy; Systemlanguid=7; languageidweaver=7; loginuuids=1; loginidweaver=sysadmin; __randcode__=0418808b-1045-4624-bea6-5b308360da7b';
d3.json( d3.json(
// "/user/data"
'/api/bs/hrmorganization/orgchart/userData?fclass=0&fisvitual=0&root=0&level=3&id=0', '/api/bs/hrmorganization/orgchart/userData?fclass=0&fisvitual=0&root=0&level=3&id=0',
).then((data) => { ).then((data) => {
setData(data.data); setData(data.data);

Loading…
Cancel
Save