123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- import React, { useEffect, useState, useRef } from 'react';
- import { connect } from 'dva';
- import { Form, Table, DatePicker, Input, Button, Empty, Card, Affix, TreeSelect } from 'antd';
- import styles from './report.less';
- import UserRptModal from './UserRptModal';
- import DepCompareModal from './DepCompareModal';
- import moment from 'moment';
- import { downloadFile, getToken } from '@/utils/utils.js';
- import * as echarts from 'echarts';
- import { CloseOutlined } from '@ant-design/icons';
- const { RangePicker } = DatePicker;
- //按天取前月26-当月25
- var initDate;
- if (moment().date() > 25) {
- initDate = [
- moment()
- .subtract(1, 'month')
- .set('date', 26),
- moment().set('date', 25),
- ];
- } else {
- initDate = [
- moment()
- .subtract(2, 'month')
- .set('date', 26),
- moment()
- .subtract(1, 'month')
- .set('date', 25),
- ];
- }
- function Department(props) {
- const { dispatch, loading, dep } = props;
- const [form] = Form.useForm();
- const [visible, setVisible] = useState(false);
- const [modalFilter, setModalFilter] = useState({});
- const [current, setCurrent] = useState(null);
- const [currentDep, setCurrentDep] = useState(null);
- const chartRef = useRef(null);
- const filterRef = useRef({ pageSize: 99999 });
- //控制图表部门选择参数
- const [loadedDepKey, setLoadedDepKey] = useState([]);
- const [expandedDepKey, setExpandedDepKey] = useState([]);
- const columns = [
- {
- title: '部门名称',
- render: record => <a onClick={() => showUserModal(record)}>{record.dep_name}</a>,
- // render: record => <a onClick={() => setCurrent(record)}>{record.dep_name}</a>,
- width: '32%',
- },
- {
- title: '有效利用率',
- dataIndex: 'usage_percent',
- render: percent => (percent * 100).toFixed(2) + '%',
- },
- {
- title: '执行项目人日',
- dataIndex: 'type_project_cnt',
- },
- {
- title: '售前支持',
- dataIndex: 'type_sale_cnt',
- },
- {
- title: '市场品牌',
- dataIndex: 'type_market_cnt',
- },
- {
- title: '日常',
- dataIndex: 'type_normal_cnt',
- },
- {
- title: '标准化',
- dataIndex: 'type_standardize_cnt',
- },
- {
- title: '研发',
- dataIndex: 'type_rd_cnt',
- },
- // {
- // title: '漏填工时',
- // dataIndex: 'type_lost_cnt',
- // },
- {
- title: '应填报总工时',
- dataIndex: 'total_cnt',
- },
- // {
- // title: '操作',
- // width: 80,
- // render: item => <a onClick={() => showDepCompare(item)}>详情</a>,
- // },
- // {
- // title: '付费工时数',
- // dataIndex: 'pay_workload_cnt',
- // },
- // {
- // title: '付费工时率',
- // dataIndex: 'pay_workload_percent',
- // render: percent => (percent * 100).toFixed(2) + '%',
- // },
- ];
- const handleSearch = () => {
- const { time } = form.getFieldsValue();
- filterRef.current.s_time = time[0] ? moment(time[0]).format('YYYY-MM-DD') : '';
- filterRef.current.e_time = time[1] ? moment(time[1]).format('YYYY-MM-DD') : '';
- //重置图表部门选择
- setLoadedDepKey([]);
- setExpandedDepKey([]);
- dispatch({
- type: 'report/queryDepReport',
- payload: {
- filter: filterRef.current,
- },
- callback: list => handleChangeCurrent(list[0]),
- });
- };
- const handleDownload = finance => {
- const token = getToken();
- const { time } = form.getFieldsValue();
- let s_time = time[0] ? moment(time[0]).format('YYYY-MM-DD') : '';
- let e_time = time[1] ? moment(time[1]).format('YYYY-MM-DD') : '';
- if (finance)
- downloadFile(
- `/api/v2/workload/finance/people/export?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}`,
- `财务报表_部门${moment().format('YYYYMMDDHHMMSS')}.xlsx`
- );
- else
- downloadFile(
- `/api/v2/workload/rpt/dep/export2excel?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}`,
- `部门报表${moment().format('YYYYMMDDHHMMSS')}.xlsx`
- );
- };
- const renderSearch = () => {
- return (
- <Form layout="inline" form={form}>
- <Form.Item label="时间" name="time" initialValue={initDate}>
- <RangePicker placeholder="选择时间" allowClear={false} />
- </Form.Item>
- <Form.Item>
- <Button type="primary" loading={loading} onClick={handleSearch}>
- 查询
- </Button>
- </Form.Item>
- </Form>
- );
- };
- //控制图表部门选择展开/加载
- const onExpandDep = keys => {
- setExpandedDepKey(keys);
- };
- const onLoadDep = (expanded, record) => {
- return new Promise(resolve => {
- if (expanded && !record.isLoad) {
- dispatch({
- type: 'report/queryDepReport',
- payload: {
- filter: filterRef.current,
- record: record,
- },
- callback: resolve,
- });
- } else {
- resolve();
- }
- });
- };
- const showUserModal = item => {
- // const showDepCompare = item => {
- const { s_time, e_time } = filterRef.current;
- setModalFilter({
- s_time: s_time,
- e_time: e_time,
- dep_id: item.dep_id,
- });
- setVisible(true);
- };
- const renderChart = () => {
- current;
- chartRef.current.setOption({
- tooltip: {
- trigger: 'item',
- },
- graphic: {
- type: 'text',
- left: 'center',
- top: 'center',
- style: {
- text: `有效利用率\n${
- current.usage_percent ? (current.usage_percent * 100).toFixed(2) : '-'
- }%`,
- textAlign: 'center',
- },
- },
- series: [
- {
- type: 'pie',
- radius: ['40%', '70%'],
- data: current.data,
- hoverAnimation: false,
- itemStyle: { shadowColor: 'rgba(0, 0, 0, 0.5)', shadowBlur: 5 },
- },
- ],
- });
- };
- const handleChangeCurrent = item => {
- let data = [
- { value: item.type_project_cnt, name: '执行项目人日' },
- { value: item.type_sale_cnt, name: '售前支持' },
- { value: item.type_market_cnt, name: '市场品牌' },
- { value: item.type_normal_cnt, name: '日常' },
- { value: item.type_standardize_cnt, name: '标准化' },
- { value: item.type_rd_cnt, name: '研发' },
- ];
- setCurrentDep(item.dep_id);
- // 过滤为0的值
- data = data.filter(item => item.value);
- if (data.length > 0) {
- data.push({
- value: item.type_lost_cnt,
- name: '漏填工时',
- tooltip: {
- backgroundColor: 'transparent',
- formatter: () => ' ',
- },
- itemStyle: { color: '#fff' },
- emphasis: {
- label: { show: false },
- labelLine: { show: false },
- itemStyle: { color: '#fff' },
- },
- label: { show: false },
- labelLine: { show: false },
- selected: false,
- });
- setCurrent({ data, usage_percent: item.usage_percent });
- } else {
- setCurrent(null);
- }
- };
- const renderDepSelect = () => {
- return (
- <TreeSelect
- showSearch
- allowClear
- placeholder="请选择部门"
- style={{ width: '80%' }}
- multiple={false}
- treeData={dep.list}
- fieldNames={{
- label: 'dep_name',
- value: 'dep_id',
- }}
- filterTreeNode={(input, option) => {
- return option.props.dep_name.includes(input);
- }}
- onSelect={(_, node) => {
- handleChangeCurrent(node);
- }}
- treeExpandedKeys={expandedDepKey}
- onTreeExpand={keys => onExpandDep(keys)}
- treeLoadedKeys={loadedDepKey}
- loadData={node => onLoadDep(true, node)}
- value={currentDep}
- />
- );
- };
- useEffect(() => {
- // dispatch({
- // type: 'report/queryUserReport',
- // });
- handleSearch();
- chartRef.current = echarts.init(document.getElementById('chart'));
- }, []);
- useEffect(() => {
- if (current) {
- renderChart();
- }
- }, [current]);
- return (
- <div>
- <div className={styles.topPart}>
- {renderSearch()}
- <div>
- <Button type="primary" onClick={() => handleDownload(1)} style={{ marginRight: '10px' }}>
- 财务报表导出
- </Button>
- <Button type="primary" onClick={() => handleDownload(0)}>
- 导出
- </Button>
- </div>
- </div>
- <div style={{ marginTop: 20, display: 'flex' }}>
- <Table
- loading={loading}
- rowKey="dep_id"
- style={{ width: '100%' }}
- columns={columns}
- dataSource={dep.list}
- pagination={false}
- onExpand={onLoadDep}
- />
- <Affix offsetTop={20}>
- <Card
- // extra={<CloseOutlined onClick={() => setCurrent(null)} />}
- title={renderDepSelect()}
- style={{ display: 'block', marginLeft: 20 }}
- >
- {!current && <Empty style={{ width: 400 }} />}
- <div
- id="chart"
- style={{ width: 400, height: 340, display: current ? 'block' : 'none' }}
- ></div>
- </Card>
- </Affix>
- </div>
- {/* <UserRptModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} /> */}
- <DepCompareModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} />
- </div>
- );
- }
- export default connect(({ report, loading }) => ({
- dep: report.dep,
- loading: loading.models.report,
- }))(Department);
|