Department.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import { connect } from 'dva';
  3. import { Form, Table, DatePicker, Input, Button, Empty, Card, Affix, TreeSelect } from 'antd';
  4. import styles from './report.less';
  5. import UserRptModal from './UserRptModal';
  6. import DepCompareModal from './DepCompareModal';
  7. import moment from 'moment';
  8. import { downloadFile, getToken } from '@/utils/utils.js';
  9. import * as echarts from 'echarts';
  10. import { CloseOutlined } from '@ant-design/icons';
  11. const { RangePicker } = DatePicker;
  12. const initData = [
  13. // 上个月第一天
  14. moment()
  15. .subtract(1, 'month')
  16. .startOf('month'),
  17. // 上个月最后一天
  18. moment()
  19. .subtract(1, 'month')
  20. .endOf('month'),
  21. ];
  22. function Department(props) {
  23. const { dispatch, loading, dep, depUserTree } = props;
  24. const [form] = Form.useForm();
  25. const [visible, setVisible] = useState(false);
  26. const [modalFilter, setModalFilter] = useState({});
  27. const [current, setCurrent] = useState(null);
  28. const chartRef = useRef(null);
  29. const columns = [
  30. {
  31. title: '部门名称',
  32. render: record => <a onClick={() => showUserModal(record)}>{record.dep_name}</a>,
  33. // render: record => <a onClick={() => setCurrent(record)}>{record.dep_name}</a>,
  34. width: '32%',
  35. },
  36. {
  37. title: '有效利用率',
  38. dataIndex: 'usage_percent',
  39. render: percent => (percent * 100).toFixed(2) + '%',
  40. },
  41. {
  42. title: '执行项目人日',
  43. dataIndex: 'type_project_cnt',
  44. },
  45. {
  46. title: '售前支持',
  47. dataIndex: 'type_sale_cnt',
  48. },
  49. {
  50. title: '市场品牌',
  51. dataIndex: 'type_market_cnt',
  52. },
  53. {
  54. title: '日常',
  55. dataIndex: 'type_normal_cnt',
  56. },
  57. {
  58. title: '标准化',
  59. dataIndex: 'type_standardize_cnt',
  60. },
  61. {
  62. title: '研发',
  63. dataIndex: 'type_rd_cnt',
  64. },
  65. {
  66. title: '漏填工时',
  67. dataIndex: 'type_lost_cnt',
  68. },
  69. {
  70. title: '总计',
  71. dataIndex: 'total_cnt',
  72. },
  73. // {
  74. // title: '操作',
  75. // width: 80,
  76. // render: item => <a onClick={() => showDepCompare(item)}>详情</a>,
  77. // },
  78. // {
  79. // title: '付费工时数',
  80. // dataIndex: 'pay_workload_cnt',
  81. // },
  82. // {
  83. // title: '付费工时率',
  84. // dataIndex: 'pay_workload_percent',
  85. // render: percent => (percent * 100).toFixed(2) + '%',
  86. // },
  87. ];
  88. const filterRef = useRef({ pageSize: 99999 });
  89. const handleSearch = () => {
  90. const { time } = form.getFieldsValue();
  91. filterRef.current.s_time = time[0] ? moment(time[0]).format('YYYY-MM') : null;
  92. filterRef.current.e_time = time[1] ? moment(time[1]).format('YYYY-MM') : null;
  93. dispatch({
  94. type: 'report/queryDepReport',
  95. payload: {
  96. filter: filterRef.current,
  97. },
  98. });
  99. };
  100. const handleDownload = () => {
  101. const token = getToken();
  102. const s_time = !filterRef.current.s_time ? '' : filterRef.current.s_time;
  103. const e_time = !filterRef.current.e_time ? '' : filterRef.current.e_time;
  104. downloadFile(
  105. `/api/v2/workload/rpt/dep/export2excel?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}`,
  106. `项目报表${moment().format('YYYYMMDDHHMMSS')}.xlsx`
  107. );
  108. };
  109. const renderSearch = () => {
  110. return (
  111. <Form layout="inline" form={form}>
  112. <Form.Item label="时间" name="time" initialValue={initData}>
  113. <RangePicker placeholder="选择时间" allowClear={false} picker="month" />
  114. </Form.Item>
  115. <Form.Item>
  116. <Button type="primary" loading={loading} onClick={handleSearch}>
  117. 查询
  118. </Button>
  119. </Form.Item>
  120. </Form>
  121. );
  122. };
  123. const onExpand = (expanded, record) => {
  124. return new Promise(resolve => {
  125. if (expanded && !record.isLoad) {
  126. dispatch({
  127. type: 'report/queryDepReport',
  128. payload: {
  129. filter: filterRef.current,
  130. record: record,
  131. },
  132. callback: resolve,
  133. });
  134. } else {
  135. resolve();
  136. }
  137. });
  138. };
  139. // const showUserModal = item => {
  140. const showDepCompare = item => {
  141. const { s_time, e_time } = filterRef.current;
  142. setModalFilter({
  143. s_time: s_time,
  144. e_time: e_time,
  145. dep_id: item.dep_id,
  146. });
  147. setVisible(true);
  148. };
  149. const renderChart = () => {
  150. current;
  151. chartRef.current.setOption({
  152. tooltip: {
  153. trigger: 'item',
  154. },
  155. series: [
  156. {
  157. type: 'pie',
  158. radius: '70%',
  159. data: current,
  160. emphasis: {
  161. itemStyle: {
  162. shadowBlur: 10,
  163. shadowOffsetX: 0,
  164. shadowColor: 'rgba(0, 0, 0, 0.5)',
  165. },
  166. },
  167. },
  168. ],
  169. });
  170. };
  171. const handleChangeCurrent = item => {
  172. let data = [
  173. { value: item.type_project_cnt, name: '执行项目人日' },
  174. { value: item.type_sale_cnt, name: '售前支持' },
  175. { value: item.type_market_cnt, name: '市场品牌' },
  176. { value: item.type_normal_cnt, name: '日常' },
  177. { value: item.type_standardize_cnt, name: '标准化' },
  178. { value: item.type_rd_cnt, name: '研发' },
  179. ];
  180. // 过滤为0的值
  181. data = data.filter(item => item.value);
  182. setCurrent(data);
  183. };
  184. const renderDepSelect = () => {
  185. return (
  186. <TreeSelect
  187. showSearch
  188. allowClear
  189. placeholder="请选择部门"
  190. style={{ width: '80%' }}
  191. multiple={false}
  192. treeData={dep.list}
  193. fieldNames={{
  194. label: 'dep_name',
  195. value: 'dep_id',
  196. }}
  197. filterTreeNode={(input, option) => {
  198. return option.props.title === input;
  199. }}
  200. onSelect={(_, node) => {
  201. handleChangeCurrent(node);
  202. }}
  203. loadData={node => onExpand(true, node)}
  204. />
  205. );
  206. };
  207. useEffect(() => {
  208. // dispatch({
  209. // type: 'report/queryUserReport',
  210. // });
  211. dispatch({
  212. type: 'report/fetchDepV2',
  213. });
  214. handleSearch();
  215. chartRef.current = echarts.init(document.getElementById('chart'));
  216. }, []);
  217. useEffect(() => {
  218. if (current) {
  219. renderChart();
  220. }
  221. }, [current]);
  222. return (
  223. <div>
  224. <div className={styles.topPart}>
  225. {renderSearch()}
  226. <Button type="primary" onClick={handleDownload}>
  227. 导出
  228. </Button>
  229. </div>
  230. <div style={{ marginTop: 20, display: 'flex' }}>
  231. <Table
  232. loading={loading}
  233. rowKey="dep_id"
  234. style={{ width: '100%' }}
  235. columns={columns}
  236. dataSource={dep.list}
  237. pagination={false}
  238. onExpand={onExpand}
  239. />
  240. <Affix offsetTop={20}>
  241. <Card
  242. // extra={<CloseOutlined onClick={() => setCurrent(null)} />}
  243. title={renderDepSelect()}
  244. style={{ display: 'block', marginLeft: 20 }}
  245. >
  246. {!current?.length && <Empty style={{ width: 400 }} />}
  247. <div
  248. id="chart"
  249. style={{ width: 400, height: 340, display: current?.length > 0 ? 'block' : 'none' }}
  250. ></div>
  251. </Card>
  252. </Affix>
  253. </div>
  254. {/* <UserRptModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} /> */}
  255. <DepCompareModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} />
  256. </div>
  257. );
  258. }
  259. export default connect(({ report, loading }) => ({
  260. dep: report.dep,
  261. depUserTree: report.depUserTree,
  262. loading: loading.models.report,
  263. }))(Department);