Department.js 8.0 KB

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