XuZinan 2 rokov pred
rodič
commit
a195e82f44
30 zmenil súbory, kde vykonal 1296 pridanie a 528 odobranie
  1. 134 108
      src/pages/PurchaseAdmin/PurchaseList/Approval/ApprovalModal.js
  2. 47 28
      src/pages/PurchaseAdmin/PurchaseList/Approval/Auth.js
  3. 18 9
      src/pages/PurchaseAdmin/PurchaseList/Approval/AuthModal.js
  4. 104 22
      src/pages/PurchaseAdmin/PurchaseList/Approval/DetailModal.js
  5. 16 0
      src/pages/PurchaseAdmin/PurchaseList/Approval/DetailModal.less
  6. 2 4
      src/pages/PurchaseAdmin/PurchaseList/Approval/ExecutionModal.js
  7. 86 31
      src/pages/PurchaseAdmin/PurchaseList/Approval/List.js
  8. 7 14
      src/pages/PurchaseAdmin/PurchaseList/Approval/MemberModal.js
  9. 21 21
      src/pages/PurchaseAdmin/PurchaseList/Approval/QualityOperateModal.js
  10. 21 3
      src/pages/PurchaseAdmin/PurchaseList/Approval/models/approval.js
  11. 2 2
      src/pages/PurchaseAdmin/PurchaseList/Index.js
  12. 176 0
      src/pages/PurchaseAdmin/PurchaseList/Report/DepCompareModal.js
  13. 20 28
      src/pages/PurchaseAdmin/PurchaseList/Report/Department.js
  14. 126 0
      src/pages/PurchaseAdmin/PurchaseList/Report/Finance.js
  15. 111 78
      src/pages/PurchaseAdmin/PurchaseList/Report/Project.js
  16. 1 3
      src/pages/PurchaseAdmin/PurchaseList/Report/Resource.js
  17. 50 17
      src/pages/PurchaseAdmin/PurchaseList/Report/UserProjectRptModal.js
  18. 2 2
      src/pages/PurchaseAdmin/PurchaseList/Report/UserRptModal.js
  19. 148 53
      src/pages/PurchaseAdmin/PurchaseList/Report/models/report.js
  20. 71 36
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/AddModal.js
  21. 46 11
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/Auth.js
  22. 3 4
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/AuthWorkList.js
  23. 1 2
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/CalendarModal.js
  24. 1 3
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/RejectModal.js
  25. 1 3
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/SearchForm.js
  26. 11 13
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/WorkList.js
  27. 12 12
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/index.js
  28. 25 20
      src/pages/PurchaseAdmin/PurchaseList/WorkingHours/models/workingHours.js
  29. 16 0
      src/services/approval.js
  30. 17 1
      src/services/workHours.js

+ 134 - 108
src/pages/PurchaseAdmin/PurchaseList/Approval/ApprovalModal.js

@@ -1,11 +1,8 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Select, Modal, Input, TreeSelect } from 'antd';
+import { Form, Select, Modal, Input, TreeSelect } from 'antd';
 import moment from 'moment';
 import provinces from './provinces';
 import { queryApproval } from '@/services/approval';
-// import project from '@/pages/ProjectAdmin/ProjectAdmin/models/project';
 const { Option } = Select;
 const { TreeNode } = TreeSelect;
 // 新建
@@ -18,6 +15,8 @@ function AddModal(props) {
     onOk,
     form,
     data,
+    currentUser,
+    depUserTree,
     flowList = [],
     industryList = [],
     typeList = [],
@@ -37,48 +36,26 @@ function AddModal(props) {
     form.validateFields((err, fieldsValue) => {
       if (err) return;
       let values = { ...fieldsValue, id: data.id };
-      //项目分类为研发时
-      if (fieldsValue.type_id == 7) {
-        values.project_name = fieldsValue.project_name;
-        values.flow_id = Number(fieldsValue.flow_id);
-        values.type_id = Number(fieldsValue.type_id);
-        // 获得flow下第一个node的id
-        values.node_id = flowList.find(item => item.id == values.flow_id).Nodes[0].id;
-        //以下为测试用默认值
-        // values.industry_id = 0;
-        // values.location = '';
-        // values.location_code = '';
-        // values.project_full_code = '';
-        // values.name = '';
-        // values.version = '';
-
-        //通过当前项目数生成项目编号
-        // dispatch({
-        //   type: 'approval/queryApproval',
-        //   callback: data => {
-        //     let project_full_code = '';
-        //     if (total < 10) {
-        //       project_full_code = '00' + data.pagination.total;
-        //     } else if (total < 100) project_full_code = '0' + data.pagination.total;
-        //     else project_full_code = data.pagination.total;
-        //     values.project_full_code = `RD${moment().format('YYYYMM')}${project_full_code}`;
-        //     onOk(values);
-        //   },
-        // });
-        onOk(values);
-      } else {
-        values.project_name = fieldsValue.project_name;
-        values.type_id = Number(fieldsValue.type_id);
+      values.project_name = fieldsValue.project_name;
+      values.flow_id = Number(fieldsValue.flow_id);
+      values.type_id = Number(fieldsValue.type_id);
+      // 获得flow下第一个node的id
+      values.node_id = flowList.find(item => item.id == values.flow_id).Nodes[0].id;
+      //项目分类为不为研发时
+      if (fieldsValue.type_id != 7) {
         values.industry_id = Number(fieldsValue.industry_id);
-        values.flow_id = Number(fieldsValue.flow_id);
-        // 获得flow下第一个node的id
-        values.node_id = flowList.find(item => item.id == values.flow_id).Nodes[0].id;
         let [location, location_code] = fieldsValue.location.split('##');
         values.location = location;
         values.location_code = location_code;
         values.project_full_code = `${codes.type}${codes.industry}${codes.location}${codes.name}${codes.version}`;
-        onOk(values);
       }
+      if (fieldsValue.author) {
+        values.author = Number(fieldsValue.author.split("||")[0]);
+        values.author_dep_id = Number(fieldsValue.author.split("||")[1]);
+      } else {
+        values.author = null;
+      }
+      onOk(values);
     });
   };
 
@@ -88,7 +65,6 @@ function AddModal(props) {
       let code = item.code || '';
       if (code.length == 4) {
         code = code.substr(1);
-        console.log(code);
       }
       if (code) {
         title += `(${code})`;
@@ -108,7 +84,7 @@ function AddModal(props) {
       type: item.code,
     });
     setType(item);
-    form.setFieldsValue({ flow_id: id == 7 ? '4' : '1'});
+    form.setFieldsValue({ flow_id: id == 7 ? '4' : '1' });
   };
   const changeIndustry = id => {
     const item = industryList.find(item => item.id == id);
@@ -133,7 +109,7 @@ function AddModal(props) {
   const onBlurName = e => {
     let value = e.target.value.toUpperCase();
     while (value.length < 3) {
-      value = value + "V";
+      value = value + 'V';
     }
     form.setFieldsValue({
       name: value,
@@ -144,69 +120,101 @@ function AddModal(props) {
     });
   };
 
-  const renderDetail = () => {
-    return <>
-      <Form.Item label="行业名称">
-        {form.getFieldDecorator('industry_id', {
-          initialValue: String(data.industry_id || ''),
-          rules: [{ required: true, message: '请选择行业名称' }],
-        })(
-          <Select style={{ width: '100%' }} onChange={changeIndustry}>
-            {industryList.map(item => (
-              <Option key={item.id}>
-                {item.name}({item.code})
-              </Option>
-            ))}
-          </Select>
-        )}
-      </Form.Item>
-      <Form.Item label="项目地区">
-        {form.getFieldDecorator('location', {
-          initialValue: data.location,
-          rules: [{ required: true, message: '请选择项目地区' }],
-        })(
-          <TreeSelect
-            dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
-            onChange={changeLocation}
+  const renderUserSelectTreeNodes = data => {
+    return data.map(item => {
+      if (item.children) {
+        return (
+          <TreeNode
+            title={item.title}
+            key={item.key}
+            value={item.manage || item.value}
+            dataRef={item}
+            selectable={item.selectable}
           >
-            {renderTreeNodes(provinces)}
-          </TreeSelect>
-        )}
-      </Form.Item>
-      <Form.Item label="项目简称">
-        {form.getFieldDecorator('name', {
-          initialValue: data.name,
-          rules: [
-            { required: true, message: '请输入项目简称' },
-            {
-              validator: (rule, value, callback) => {
-                if (value.match(/[^A-Za-z]/g)) {
-                  callback('项目简称只能是英文字符');
-                } else {
-                  callback();
-                }
+            {renderUserSelectTreeNodes(item.children)}
+          </TreeNode>
+        );
+      }
+      return (
+        <TreeNode
+          title={item.title}
+          key={item.ID}
+          value={item.manage || item.value}
+          selectable={item.selectable}
+        />
+      );
+    });
+  };
+
+  const onChangeManager = (e) => {
+    console.log(e);
+  };
+
+  const renderDetail = () => {
+    return (
+      <>
+        <Form.Item label="行业名称">
+          {form.getFieldDecorator('industry_id', {
+            initialValue: String(data.industry_id || ''),
+            rules: [{ required: true, message: '请选择行业名称' }],
+          })(
+            <Select style={{ width: '100%' }} onChange={changeIndustry}>
+              {industryList.map(item => (
+                <Option key={item.id}>
+                  {item.name}({item.code})
+                </Option>
+              ))}
+            </Select>
+          )}
+        </Form.Item>
+        <Form.Item label="项目地区">
+          {form.getFieldDecorator('location', {
+            initialValue: data.location,
+            rules: [{ required: true, message: '请选择项目地区' }],
+          })(
+            <TreeSelect
+              dropdownStyle={{ maxHeight: 300, overflow: 'auto' }}
+              onChange={changeLocation}
+            >
+              {renderTreeNodes(provinces)}
+            </TreeSelect>
+          )}
+        </Form.Item>
+        <Form.Item label="项目简称">
+          {form.getFieldDecorator('name', {
+            initialValue: data.name,
+            rules: [
+              { required: true, message: '请输入项目简称' },
+              {
+                validator: (rule, value, callback) => {
+                  if (value.match(/[^A-Za-z]/g)) {
+                    callback('项目简称只能是英文字符');
+                  } else {
+                    callback();
+                  }
+                },
               },
-            },
-          ],
-        })(<Input maxLength={3} onBlur={onBlurName} />)}
-      </Form.Item>
-      <Form.Item label="项目期数">
-        {form.getFieldDecorator('version', {
-          initialValue: data.version,
-          rules: [{ required: true, message: '请选择项目期数' }],
-        })(
-          <Select style={{ width: '100%' }} onChange={changeVersion}>
-            {['一期', '二期', '三期', '四期', '五期'].map((item, index) => (
-              <Option key={index + 1}>{item}</Option>
-            ))}
-          </Select>
-        )}
-      </Form.Item>
-      <Form.Item label="项目编号">
-        {codes.type || '***'}-{codes.industry || '***'}-{codes.location || '***'}-
-        {codes.name || '***'}-{codes.version || '*'}
-      </Form.Item>
-    </>;
+            ],
+          })(<Input maxLength={3} onBlur={onBlurName} />)}
+        </Form.Item>
+        <Form.Item label="项目期数">
+          {form.getFieldDecorator('version', {
+            initialValue: data.version,
+            rules: [{ required: true, message: '请选择项目期数' }],
+          })(
+            <Select style={{ width: '100%' }} onChange={changeVersion}>
+              {['一期', '二期', '三期', '四期', '五期'].map((item, index) => (
+                <Option key={index + 1}>{item}</Option>
+              ))}
+            </Select>
+          )}
+        </Form.Item>
+        <Form.Item label="项目编号">
+          {codes.type || '***'}-{codes.industry || '***'}-{codes.location || '***'}-
+          {codes.name || '***'}-{codes.version || '*'}
+        </Form.Item>
+      </>
+    );
   };
 
   // const renderCode = () => {
@@ -258,9 +266,7 @@ function AddModal(props) {
           {form.getFieldDecorator('project_name', {
             initialValue: String(data.project_name || ''),
             rules: [{ required: true, message: '请输入项目名称' }],
-          })(
-            <Input style={{ width: '100%' }}/>
-          )}
+          })(<Input style={{ width: '100%' }} />)}
         </Form.Item>
         <Form.Item label="项目类别">
           {form.getFieldDecorator('type_id', {
@@ -283,13 +289,33 @@ function AddModal(props) {
           })(
             <Select style={{ width: '100%' }} disabled>
               {flowList
-                .filter(item => item && (item.id != 2 && item.id != 3))
+                .filter(item => item && item.id != 2 && item.id != 3)
                 .map(item => (
                   <Option key={item.id}>{item.name}</Option>
                 ))}
             </Select>
           )}
         </Form.Item>
+        {currentUser.IsSuper && (
+          <Form.Item label="售前经理">
+            {form.getFieldDecorator('author', {
+              initialValue: String((data.author && data.author_dep_id) ? `${data.author}||${data.author_dep_id}` : '')
+            })(
+              <TreeSelect
+                showSearch
+                allowClear
+                style={{ width: '100%' }}
+                multiple={false}
+                filterTreeNode={(input, option) => {
+                  return option.props.title === input;
+                }}
+                onChange={onChangeManager}
+              >
+                {renderUserSelectTreeNodes(depUserTree)}
+              </TreeSelect>
+            )}
+          </Form.Item>
+        )}
         {type?.id != 7 && renderDetail()}
       </Form>
     </Modal>

+ 47 - 28
src/pages/PurchaseAdmin/PurchaseList/Approval/Auth.js

@@ -1,7 +1,5 @@
 import React, { useState, useEffect, useMemo } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Table, Button, Divider, Modal, Popover, Input, Select } from 'antd';
+import { Table, Button, Form, Divider, Modal, Popover, Input, Select } from 'antd';
 import moment from 'moment';
 import router from 'umi/router';
 import styles from './List.less';
@@ -21,6 +19,14 @@ const STATUS = [
     value: 1,
     label: '转执行',
   },
+  {
+    value: 2,
+    label: '转运营',
+  },
+  {
+    value: 3,
+    label: '转质保',
+  },
 ];
 
 function Auth(props) {
@@ -51,7 +57,7 @@ function Auth(props) {
     {
       title: '分类',
       dataIndex: 'TypeInfo',
-      render: TypeInfo => TypeInfo ?`${TypeInfo.name}(${TypeInfo.code})`: "-",
+      render: TypeInfo => (TypeInfo ? `${TypeInfo.name}(${TypeInfo.code})` : '-'),
     },
     /*
     {
@@ -82,16 +88,18 @@ function Auth(props) {
       title: '状态',
       dataIndex: 'project_status',
       render: project_status => {
-        return project_status === 0 ? <>售前</> : <>转执行</>;
+        // return project_status === 0 ? <>售前</> : <>转执行</>;
         //若添加其他状态则启用以下switch case:
-        /*
         switch (project_status){
           case 0:
-            return <>{'售前'}</>;
+            return <>售前</>;
           case 1:
-            return <>{'转执行'}</>;
+            return <>转执行</>;
+          case 2:
+            return <>转运营</>;
+          case 3:
+            return <>转质保</>
         }
-        */
       },
     },
     {
@@ -125,31 +133,33 @@ function Auth(props) {
       },
     },
     {
-      title: '创建人',
-      dataIndex: 'AuthorUser.CName',
+      title: '售前项目经理',
+      dataIndex: 'AuthorUser',
+      render: AuthorUser => (AuthorUser ? AuthorUser.CName : '-'),
     },
     {
       title: '创建时间',
       dataIndex: 'c_time',
       render: c_time => moment(c_time).format('YYYY.MM.DD'),
     },
+    {
+      title: '执行经理',
+      dataIndex: 'Leader',
+      render: Leader => (Leader ? Leader.CName : '-'),
+    },
     {
       title: '操作',
       render: record => (
         <>
-          {record.type_id != 7 && (
-            <>
-              <a
-                onClick={() => {
-                  setCurrentItem(record);
-                  setDetailVisible(true);
-                }}
-              >
-                项目详情
-              </a>
-              <Divider type="vertical" />
-            </>
-          )}
+          <a
+            onClick={() => {
+              setCurrentItem(record);
+              setDetailVisible(true);
+            }}
+          >
+            项目详情
+          </a>
+          <Divider type="vertical" />
           <a
             onClick={() => {
               setCurrentItem(record);
@@ -164,11 +174,15 @@ function Auth(props) {
   ];
 
   const canAuth = useMemo(() => {
-    let { NodeInfo, audit_status } = currentItem;
+    let { NodeInfo, audit_status, project_status } = currentItem;
     if (!NodeInfo || flowList.length == 0 || depRole.length == 0) return;
     if (audit_status != 1) return;
     let flow = flowList.find(item => item.id == NodeInfo.flow_id);
     if (!flow) return false;
+
+    if (project_status == 2) return currentItem.opt_manager_id == currentUser.ID;
+    if (project_status == 3) return currentItem.wty_manager_id == currentUser.ID;
+
     let { NodeAudits } = flow.Nodes.find(item => item.id == NodeInfo.id);
 
     const role = depRole.find(item => {
@@ -229,10 +243,11 @@ function Auth(props) {
   };
 
   const handleSearch = () => {
-    form.validateFields((error, { projectCode, projectStatus }) => {
+    form.validateFields((error, { projectName, projectCode, projectStatus }) => {
       // console.log(error,values);
       let params = {};
-      params.project_code = projectCode;
+      params.project_name = projectName;
+      params.project_code = projectCode?.toUpperCase();
       params.project_status = projectStatus;
 
       dispatch({
@@ -245,6 +260,11 @@ function Auth(props) {
   const renderSearch = () => {
     return (
       <Form style={{ marginBottom: 20 }} layout="inline">
+        <Form.Item label="项目名称">
+          {form.getFieldDecorator('projectName', {
+            initialValue: null,
+          })(<Input style={{ width: 200 }}></Input>)}
+        </Form.Item>
         <Form.Item label="项目编号">
           {form.getFieldDecorator('projectCode', {
             initialValue: null,
@@ -255,7 +275,6 @@ function Auth(props) {
             initialValue: null,
           })(
             <Select
-              allowClear
               showSearch
               style={{ width: 120 }}
               filterOption={(input, option) =>

+ 18 - 9
src/pages/PurchaseAdmin/PurchaseList/Approval/AuthModal.js

@@ -1,25 +1,34 @@
 import React, { useState, useEffect, useMemo } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Button, Modal, Steps } from 'antd';
+import { Button, Form, Modal, Steps } from 'antd';
 
 const { Step } = Steps;
 // 新建
 function AuthModal(props) {
   const { visible, onClose, onAuth, form, data, flowList = [], canAuth, loading } = props;
 
-  const folw = useMemo(() => {
+  const flow = useMemo(() => {
     if (!data.flow_id) return {};
     return flowList.find(item => item.id == data.flow_id) || {};
   }, [flowList, data]);
 
   const current = useMemo(() => {
     if (!data.node_id) return 0;
-    return folw.Nodes?.findIndex(item => item.id == data.node_id);
+    return flow.Nodes?.findIndex(item => item.id == data.node_id);
   }, [flowList, data]);
 
   const getAudits = nodeInfo => {
-    return (nodeInfo || []).map(item => item.AuthorRoleInfo.Name).join(',');
+    switch (nodeInfo.id) {
+      case 11:
+        return '执行项目经理';
+      case 12:
+        return '运营经理';
+      case 13:
+        return '执行项目经理';
+      case 14:
+        return '质保经理';
+      default:
+        return (nodeInfo.NodeAudits || []).map(item => item.AuthorRoleInfo.Name).join(',');
+    }
   };
 
   const renderFooter = () => {
@@ -42,7 +51,7 @@ function AuthModal(props) {
 
   return (
     <Modal
-      title="项目立项"
+      title="审核详情"
       width={800}
       visible={visible}
       onCancel={onClose}
@@ -50,11 +59,11 @@ function AuthModal(props) {
     >
       <Steps current={current}>
         {/* <Steps current={data?.node_id}> */}
-        {(folw.Nodes || []).map(item => (
+        {(flow.Nodes || []).map(item => (
           <Step
             key={item.id}
             title={item.node}
-            description={`审批人:${getAudits(item.NodeAudits)}`}
+            description={`审批人:${getAudits(item)}`}
           />
         ))}
       </Steps>

+ 104 - 22
src/pages/PurchaseAdmin/PurchaseList/Approval/DetailModal.js

@@ -1,7 +1,8 @@
-import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Modal } from 'antd';
+import React, { useState, useEffect, useMemo } from 'react';
+import { Form, Modal, Steps } from 'antd';
+import styles from './DetailModal.less';
+
+const { Step } = Steps;
 // 新建
 function DetailModal(props) {
   const { visible, onClose, onOk, form, data, flowList = [], disabled, loading } = props;
@@ -24,31 +25,112 @@ function DetailModal(props) {
     });
   }, [data, visible]);
 
-  return (
-    <Modal title="项目立项" visible={visible} onCancel={onClose} footer={null}>
-      <Form labelCol={{ span: 5 }} wrapperCol={{ span: 15 }}>
-        <Form.Item label="项目名称">{data.project_name}</Form.Item>
-        {data.TypeInfo && <Form.Item label="项目类别">{data.TypeInfo?.name}</Form.Item>}
-        {data.IndustryInfo && <Form.Item label="行业名称">{data.IndustryInfo?.name}</Form.Item>}
-        <Form.Item label="流程">{flowList.find(item => item.id == data.flow_id)?.name}</Form.Item>
+  const renderDetail = () => (
+    <>
+      <div className={styles.subTitle}>项目详情</div>
+      <Form labelCol={{ span: 4 }} wrapperCol={{ span: 18 }}>
+        <Form.Item className={styles.formItem} label="项目名称">
+          {data.project_name}
+        </Form.Item>
+        {data.TypeInfo && (
+          <Form.Item className={styles.formItem} label="项目类别">
+            {data.TypeInfo?.name}
+          </Form.Item>
+        )}
+        {data.IndustryInfo && (
+          <Form.Item className={styles.formItem} label="行业名称">
+            {data.IndustryInfo?.name}
+          </Form.Item>
+        )}
+        <Form.Item className={styles.formItem} label="流程">
+          {flowList.find(item => item.id == data.flow_id)?.name}
+        </Form.Item>
         {data.location && (
-          <Form.Item label="项目地区">
+          <Form.Item className={styles.formItem} label="项目地区">
             {data.location}({data.location_code})
           </Form.Item>
         )}
-        {data.name && <Form.Item label="项目简称">{data.name}</Form.Item>}
-
-        {data.version && <Form.Item label="项目批次">{data.version}期</Form.Item>}
-
-        <Form.Item label="审核人">{data.AuthorUser?.CName}</Form.Item>
-        <Form.Item label="所属部门">{data.AuthorDepInfo?.Name}</Form.Item>
-        {codes.type && (
-          <Form.Item label="项目编号">
-            {codes.type || '***'}-{codes.industry || '***'}-{codes.location || '***'}-
-            {codes.name || '***'}-{codes.version || '*'}
+        {data.name && (
+          <Form.Item className={styles.formItem} label="项目简称">
+            {data.name}
+          </Form.Item>
+        )}
+        {data.version && (
+          <Form.Item className={styles.formItem} label="项目批次">
+            {data.version}期
+          </Form.Item>
+        )}
+        {data.AuthorUser && (
+          <Form.Item className={styles.formItem} label="售前项目经理">
+            {data.AuthorUser.CName}
+          </Form.Item>
+        )}
+        {data.AuthorDepInfo && (
+          <Form.Item className={styles.formItem} label="所属部门">
+            {data.AuthorDepInfo.Name}
+          </Form.Item>
+        )}
+        {data.project_full_code && (
+          <Form.Item className={styles.formItem} label="项目编号">
+            {data.project_full_code}
+          </Form.Item>
+        )}
+        {data.WtyManager && (
+          <Form.Item className={styles.formItem} label="质保经理">
+            {data.WtyManager.CName}
+          </Form.Item>
+        )}
+        {data.OptManager && (
+          <Form.Item className={styles.formItem} label="运营经理">
+            {data.OptManager.CName}
           </Form.Item>
         )}
       </Form>
+    </>
+  );
+
+  const flow = useMemo(() => {
+    if (!data.flow_id) return {};
+    return flowList.find(item => item.id == data.flow_id) || {};
+  }, [flowList, data]);
+
+  const current = useMemo(() => {
+    if (!data.node_id) return 0;
+    return flow.Nodes?.findIndex(item => item.id == data.node_id);
+  }, [flowList, data]);
+
+  const getAudits = nodeInfo => {
+    switch (nodeInfo.id) {
+      case 11:
+        return '执行项目经理';
+      case 12:
+        return '运营经理';
+      case 13:
+        return '执行项目经理';
+      case 14:
+        return '质保经理';
+      default:
+        return (nodeInfo.NodeAudits || []).map(item => item.AuthorRoleInfo.Name).join(',');
+    }
+  };
+
+  const renderAuth = () => (
+    <div className={styles.authDetail}>
+      <div className={styles.subTitle}>审核详情</div>
+      <Steps className={styles.auth} current={current}>
+        {/* <Steps current={data?.node_id}> */}
+        {(flow.Nodes || []).map(item => (
+          <Step key={item.id} title={item.node} description={`审批人:${getAudits(item)}`} />
+        ))}
+      </Steps>
+    </div>
+  );
+
+  return (
+    <Modal title="项目详情" width={800} visible={visible} onCancel={onClose} footer={null}>
+      {/* {data.type_id != 7 && renderDetail()} */}
+      {renderDetail()}
+      {data.audit_status != 0 && renderAuth()}
     </Modal>
   );
 }

+ 16 - 0
src/pages/PurchaseAdmin/PurchaseList/Approval/DetailModal.less

@@ -0,0 +1,16 @@
+.authDetail {
+  margin-top: 20px;
+  .auth {
+    padding-top: 10px;
+  }
+}
+
+.subTitle {
+  font-size: 20px;
+  font-weight: bold;
+  margin-bottom: 10px;
+}
+
+.formItem {
+  margin-bottom: 0;
+}

+ 2 - 4
src/pages/PurchaseAdmin/PurchaseList/Approval/ExecutionModal.js

@@ -1,13 +1,11 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Select, Modal, Input, TreeSelect } from 'antd';
+import { Form, Select, Modal, Input, TreeSelect } from 'antd';
 import { connect } from 'dva'
 const { Option } = Select;
 const { TreeNode } = TreeSelect;
 
 function ExecutionModal(props) {
-  const { visible, onOk, onClose, form, currentItem, flowList = [], loading, depUserTree, dispatch } = props;
+  const { visible, onOk, onClose, form, currentItem, loading, depUserTree, dispatch } = props;
 
   const handleOk = () => {
     form.validateFields((err, {managerID, contractStatus}) => {

+ 86 - 31
src/pages/PurchaseAdmin/PurchaseList/Approval/List.js

@@ -1,7 +1,5 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Table, Button, Select, Divider, Modal, Popover, Input, Checkbox } from 'antd';
+import { Table, Button, Form, Select, Divider, Modal, Popover, Input, Checkbox } from 'antd';
 import moment from 'moment';
 import router from 'umi/router';
 import styles from './List.less';
@@ -23,6 +21,14 @@ const STATUS = [
     value: 1,
     label: '转执行',
   },
+  {
+    value: 2,
+    label: '转运营',
+  },
+  {
+    value: 3,
+    label: '转质保',
+  },
 ];
 
 function List(props) {
@@ -89,16 +95,18 @@ function List(props) {
       title: '状态',
       dataIndex: 'project_status',
       render: project_status => {
-        return project_status === 0 ? <>售前</> : <>转执行</>;
+        // return project_status === 0 ? <>售前</> : <>转执行</>;
         //若添加其他状态则启用以下switch case:
-        /*
-        switch (project_status){
+        switch (project_status) {
           case 0:
             return <>售前</>;
           case 1:
             return <>转执行</>;
+          case 2:
+            return <>转运营</>;
+          case 3:
+            return <>转质保</>;
         }
-        */
       },
     },
     {
@@ -132,14 +140,20 @@ function List(props) {
       },
     },
     {
-      title: '创建人',
-      dataIndex: 'AuthorUser.CName',
+      title: '售前项目经理',
+      dataIndex: 'AuthorUser',
+      render: AuthorUser => (AuthorUser ? AuthorUser.CName : '-'),
     },
     {
       title: '创建时间',
       dataIndex: 'c_time',
       render: c_time => moment(c_time).format('YYYY.MM.DD'),
     },
+    {
+      title: '执行经理',
+      dataIndex: 'Leader',
+      render: Leader => (Leader ? Leader.CName : '-'),
+    },
     {
       title: '操作',
       render: record => renderEditBtns(record),
@@ -147,10 +161,11 @@ function List(props) {
   ];
 
   const handleSearch = () => {
-    form.validateFields((error, { projectCode, projectStatus }) => {
+    form.validateFields((error, { projectName, projectCode, projectStatus }) => {
       // console.log(error,values);
       let params = {};
-      params.project_code = projectCode;
+      params.project_name = projectName;
+      params.project_code = projectCode?.toUpperCase();
       params.project_status = projectStatus;
       params.currentPage = 1;
 
@@ -177,6 +192,11 @@ function List(props) {
   const renderSearch = () => {
     return (
       <Form layout="inline">
+        <Form.Item label="项目名称">
+          {form.getFieldDecorator('projectName', {
+            initialValue: null,
+          })(<Input style={{ width: 200 }}></Input>)}
+        </Form.Item>
         <Form.Item label="项目编号">
           {form.getFieldDecorator('projectCode', {
             initialValue: null,
@@ -187,7 +207,6 @@ function List(props) {
             initialValue: null,
           })(
             <Select
-              allowClear
               showSearch
               style={{ width: 120 }}
               filterOption={(input, option) =>
@@ -361,39 +380,75 @@ function List(props) {
       </>
     );
     let { audit_status, project_status, type_id } = record;
+    //权限审核
+    let canEdit = () => {
+      if (currentUser.IsSuper) return true;
+      switch (audit_status) {
+        case 0:
+          return currentUser.ID == record.author;
+        case 1:
+          return false;
+        case 2:
+          if (project_status == 0) return currentUser.ID == record.author;
+          if (project_status == 1) return currentUser.ID == record.LeaderId;
+          return false;
+        case 3:
+          switch (project_status) {
+            case 0:
+              return currentUser.ID == record.author;
+            case 1:
+              return currentUser.ID == record.LeaderId;
+            case 2:
+              return currentUser.ID == record.LeaderId || currentUser.ID == record.opt_manager_id;
+            case 3:
+              return currentUser.ID == record.LeaderId || currentUser.ID == record.wty_manager_id;
+          }
+          return false;
+      }
+    };
     let toReturn = [];
-    if (type_id != 7) dividerPush(detailBtn, toReturn);
+    dividerPush(detailBtn, toReturn);
     switch (audit_status) {
       //未提交
       case 0:
-        if (currentUser.ID == record.author || currentUser.IsSuper) dividerPush(editBtn, toReturn);
+        canEdit() && dividerPush(editBtn, toReturn);
         break;
       //审核中
       case 1:
         break;
       //审核拒绝
       case 2:
-        if (project_status == 0 && (currentUser.ID == record.author || currentUser.IsSuper))
-          dividerPush(editBtn, toReturn);
-        else if (
-          project_status == 1 &&
-          (currentUser.ID == record.LeaderId || currentUser.IsSuper)
-        ) {
+        if (project_status == 0 && canEdit()) dividerPush(editBtn, toReturn);
+        else if (project_status == 1 && canEdit()) {
           dividerPush(memberBtn, toReturn);
           dividerPush(statusBtn, toReturn);
         }
         break;
       //审核通过
       case 3:
-        if (project_status == 0 && (currentUser.ID == record.author || currentUser.IsSuper)) {
-          dividerPush(memberBtn, toReturn);
-          dividerPush(executionBtn, toReturn);
-        } else if (
-          project_status == 1 &&
-          (currentUser.ID == record.LeaderId || currentUser.IsSuper)
-        ) {
-          dividerPush(memberBtn, toReturn);
-          dividerPush(statusBtn, toReturn);
+        switch (project_status) {
+          //售前
+          case 0:
+            if (canEdit()) {
+              dividerPush(memberBtn, toReturn);
+              dividerPush(executionBtn, toReturn);
+            }
+            break;
+          //转执行
+          case 1:
+            if (canEdit()) {
+              dividerPush(memberBtn, toReturn);
+              dividerPush(statusBtn, toReturn);
+            }
+            break;
+          //转运营
+          case 2:
+            canEdit() && dividerPush(memberBtn, toReturn);
+            break;
+          //转质保
+          case 3:
+            canEdit() && dividerPush(memberBtn, toReturn);
+            break;
         }
         break;
     }
@@ -443,6 +498,8 @@ function List(props) {
         onChange={queryList}
       />
       <ApprovalModal
+        currentUser={currentUser}
+        depUserTree={depUserTree}
         loading={loading}
         industryList={industryList}
         flowList={flowList}
@@ -464,7 +521,6 @@ function List(props) {
       <ExecutionModal
         depUserTree={depUserTree}
         loading={loading}
-        flowList={flowList}
         visible={executionVisible}
         currentItem={currentItem}
         onOk={() => setExecutionVisible(false)}
@@ -481,7 +537,6 @@ function List(props) {
       <QualityOperateModal
         depUserTree={depUserTree}
         loading={loading}
-        flowList={flowList}
         visible={qualityOperateVisible}
         currentItem={currentItem}
         onOk={() => setQualityOperateVisible(false)}

+ 7 - 14
src/pages/PurchaseAdmin/PurchaseList/Approval/MemberModal.js

@@ -1,7 +1,5 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Modal, TreeSelect, Table, Button } from 'antd';
+import { Form, Modal, TreeSelect, Table, Button } from 'antd';
 import { connect } from 'dva';
 const { TreeNode } = TreeSelect;
 
@@ -25,16 +23,7 @@ const tempData = [
 ];
 
 function MemberModal(props) {
-  const {
-    visible,
-    onClose,
-    form,
-    currentItem,
-    loading,
-    depUserTree,
-    dataSource,
-    dispatch,
-  } = props;
+  const { visible, onClose, form, currentItem, loading, depUserTree, dataSource, dispatch } = props;
   const [currentMember, setCurrentMember] = useState({});
 
   const columns = [
@@ -53,7 +42,11 @@ function MemberModal(props) {
     {
       title: '操作',
       //移除按钮,此处应传入当前成员的数据
-      render: member => <a onClick={() => onDelete(member)}>移除</a>,
+      render: member =>
+        member.ID != currentItem.author &&
+        member.ID != currentItem.LeaderId &&
+        member.ID != currentItem.wty_manager_id &&
+        member.ID != currentItem.opt_manager_id && <a onClick={() => onDelete(member)}>移除</a>,
     },
   ];
 

+ 21 - 21
src/pages/PurchaseAdmin/PurchaseList/Approval/QualityOperateModal.js

@@ -1,7 +1,5 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Select, Modal, Input, TreeSelect } from 'antd';
+import { Form, Select, Modal, Input, TreeSelect } from 'antd';
 import { connect } from 'dva';
 const { Option } = Select;
 const { TreeNode } = TreeSelect;
@@ -13,29 +11,31 @@ function QualityOperateModal(props) {
     onClose,
     form,
     currentItem,
-    flowList = [],
     loading,
     depUserTree,
     dispatch,
     qualityOperate,
   } = props;
 
-  //   const handleOk = () => {
-  //     form.validateFields((err, {managerID, contractStatus}) => {
-  //       if (err) return;
-  //       const [exe_manager_id,dep_id] = managerID.split("||")
-  //       dispatch({
-  //         type: 'approval/startExecution',
-  //         payload: {
-  //           project_code_id: currentItem.id,
-  //           with_contract: Number(contractStatus),
-  //           dep_id: Number(dep_id),
-  //           exe_manager_id: Number(exe_manager_id),
-  //         },
-  //         callback: () => onOk(),
-  //       })
-  //     });
-  //   };
+  const handleOk = () => {
+    form.validateFields((err, { managerID }) => {
+      if (err) return;
+      const [manager_id, dep_id] = managerID.split('||');
+      let params = {};
+      params.type = qualityOperate ? 'approval/startOperate' : 'approval/startQuality';
+      params.payload = {
+        project_code_id: currentItem.id,
+        dep_id: Number(dep_id),
+      };
+      qualityOperate
+        ? (params.payload.opt_manager_id= Number(manager_id))
+        : (params.payload.wty_manager_id = Number(manager_id));
+      dispatch({
+        ...params,
+        callback: () => onOk(),
+      });
+    });
+  };
 
   const onChangeManager = (id, e) => {
     console.log(id);
@@ -75,7 +75,7 @@ function QualityOperateModal(props) {
       destroyOnClose
       visible={visible}
       onCancel={onClose}
-      onOk={onOk}
+      onOk={handleOk}
     >
       <Form labelCol={{ span: 5 }} wrapperCol={{ span: 15 }}>
         <Form.Item label={qualityOperate ? '运营经理' : '质保经理'}>

+ 21 - 3
src/pages/PurchaseAdmin/PurchaseList/Approval/models/approval.js

@@ -13,6 +13,8 @@ import {
   addMember,
   queryMember,
   startExecution,
+  startOperate,
+  startQuality,
   deleteMember,
 } from '@/services/approval';
 import { message } from 'antd';
@@ -38,6 +40,7 @@ function getDepUserTree(data) {
       item.value = item.ID;
       item.manage =  item.ID + "||" + data.ID
       item.selectable = true;
+      item.DepId = data.ID;
       data.children.push(item);
     });
   }
@@ -130,7 +133,6 @@ export default {
       });
     },
     *queryMember({ payload }, { call, put }) {
-      console.log(111);
       const { data } = yield call(queryMember, payload);
       if (!data) return;
       yield put({
@@ -156,10 +158,8 @@ export default {
     },
     *createApproval({ payload, callback }, { call, put }) {
       const res = yield call(createApproval, payload);
-      console.log(res);
       if (res) {
         const { data } = res;
-        console.log(data);
         callback && callback();
         message.success('创建成功');
         yield put({
@@ -223,6 +223,24 @@ export default {
         type: 'queryApproval',
       });
     },
+    *startOperate({ payload, callback }, { call, put }) {
+      const res = yield call(startOperate, payload);
+      if (!res) return;
+      callback && callback();
+      message.success('转运营送审成功');
+      yield put({
+        type: 'queryApproval',
+      });
+    },
+    *startQuality({ payload, callback }, { call, put }) {
+      const res = yield call(startQuality, payload);
+      if (!res) return;
+      callback && callback();
+      message.success('转质保送审成功');
+      yield put({
+        type: 'queryApproval',
+      });
+    },
   },
 
   reducers: {

+ 2 - 2
src/pages/PurchaseAdmin/PurchaseList/Index.js

@@ -37,7 +37,7 @@ function LayoutDetail(props) {
               defaultSelectedKeys={[props.location.pathname]}
               style={{ lineHeight: '64px', width: '100%' }}
             >
-              {/* <SubMenu key="/home/work-hours" title="工时管理">
+              <SubMenu key="/home/work-hours" title="工时管理">
                 <Menu.Item key="/home/work-hours">
                   <Link to="/home/work-hours">上报工时</Link>
                 </Menu.Item>
@@ -75,7 +75,7 @@ function LayoutDetail(props) {
                     </Menu.Item>
                   )}
                 </SubMenu>
-              )} */}
+              )}
               {/* {isAdmin && ( */}
                 <Menu.Item key="/home">
                   <Link to="/home">采购清单</Link>

+ 176 - 0
src/pages/PurchaseAdmin/PurchaseList/Report/DepCompareModal.js

@@ -0,0 +1,176 @@
+import React, { useState, useEffect } from 'react';
+import { Modal, Table } from 'antd';
+import { connect } from 'dva';
+import moment from 'moment';
+import { set } from 'lodash';
+
+function DepCompareModal(props) {
+  const {
+    dispatch,
+    visible,
+    onOk,
+    onCancel,
+    depCompare,
+    depUserProject,
+    depUser,
+    filter,
+    loading,
+  } = props;
+  const [userVisible, setUserVisible] = useState(false);
+  //本部门
+  const columns = [
+    { title: '用户名称', dataIndex: 'c_name' },
+    { title: '员工号', dataIndex: 'user_name' },
+    { 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: '有效利用率',
+      dataIndex: 'usage_percent',
+      render: (percent = 0) => (percent * 100).toFixed(2) + '%',
+    },
+  ];
+  // 其他部门
+  const columnsDep = [
+    {
+      width: 350,
+      render: record =>
+        // record.dep_name || <a onClick={() => onClickUser(record)}>{record.c_name}</a>,
+        record.dep_name || record.c_name,
+    },
+    { title: '执行项目', dataIndex: 'type_project_cnt' },
+    { title: '售前项目', dataIndex: 'type_sale_cnt' },
+    { title: '研发项目', dataIndex: 'type_rd_cnt' },
+    { title: '运营项目', dataIndex: 'type_opt_cnt' },
+    { title: '质保项目', dataIndex: 'type_wty_cnt' },
+    { title: '总计', dataIndex: 'total_cnt' },
+  ];
+  const columnsUser = [
+    { title: '项目名称', dataIndex: 'project_name' },
+    { title: '总工时', dataIndex: 'total_cnt' },
+  ];
+
+  const onChangePage = pagination => {
+    dispatch({
+      type: 'report/queryUserReport',
+      payload: {
+        ...filter,
+        currentPage: pagination.current,
+      },
+    });
+  };
+
+  const onExpand = (expanded, record) => {
+    if (expanded && !record.isLoad) {
+      dispatch({
+        type: 'report/queryDepCompareUser',
+        payload: {
+          s_time: filter.s_time,
+          e_time: filter.e_time,
+          project_dep_id: filter.dep_id,
+          record: record,
+        },
+      });
+    }
+  };
+
+  const onClickUser = user => {
+    dispatch({
+      type: 'report/queryDepUserProject',
+      payload: {
+        s_time: filter.s_time,
+        e_time: filter.e_time,
+        project_dep_id: filter.dep_id,
+        dep_id: user.dep_id,
+        user_id: user.user_id,
+      },
+    });
+    setUserVisible(true);
+  };
+
+  useEffect(() => {
+    if (!filter?.dep_id) return;
+    dispatch({
+      type: 'report/queryUserReport',
+      payload: filter,
+    });
+    dispatch({
+      type: 'report/queryDepCompare',
+      payload: filter,
+    });
+  }, [filter]);
+
+  return (
+    <>
+      <Modal
+        title="部门对账单"
+        width="80%"
+        visible={visible}
+        onCancel={onCancel}
+        footer={false}
+        destroyOnClose
+      >
+        <Table
+          title={() => '本部门工时详情'}
+          columns={columns}
+          loading={loading}
+          dataSource={depUser.list}
+          pagination={depUser.pagination}
+          onChange={onChangePage}
+        ></Table>
+        {/* <Table
+          title={() => '本部门在所属项目下产生的工时'}
+          columns={columns}
+          loading={loading}
+          dataSource={depCompare.length == 0 ? [] : [depCompare[0]]}
+          rowKey="key"
+          childrenColumnName="child"
+          pagination={false}
+          onExpand={onExpand}
+        /> */}
+        {console.log(depCompare)}
+        {depCompare.length > 0 && (
+          <Table
+            title={() => '其他部门在所属项目下产生的工时'}
+            columns={columnsDep}
+            loading={loading}
+            dataSource={depCompare}
+            rowKey="key"
+            childrenColumnName="child"
+            pagination={false}
+            onExpand={onExpand}
+          />
+        )}
+      </Modal>
+
+      {/* <Modal
+        title="用户详情"
+        width="80%"
+        visible={userVisible}
+        onCancel={() => setUserVisible(false)}
+        footer={false}
+        destroyOnClose
+      >
+        <Table
+          columns={columnsUser}
+          loading={loading}
+          dataSource={depUserProject}
+          pagination={false}
+          rowKey="project_name"
+        ></Table>
+      </Modal> */}
+    </>
+  );
+}
+
+export default connect(({ report, loading }) => ({
+  depCompare: report.depCompare,
+  depUserProject: report.depUserProject,
+  depUser: report.depUser,
+  loading: loading.models.report,
+}))(DepCompareModal);

+ 20 - 28
src/pages/PurchaseAdmin/PurchaseList/Report/Department.js

@@ -1,12 +1,11 @@
 import React, { useEffect, useState, useRef } from 'react';
 import { connect } from 'dva';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Table, DatePicker, Input, Button } from 'antd';
+import { Form, Table, DatePicker, Input, Button } 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 { downloadFile, getToken } from '@/utils/utils.js';
 
 const { RangePicker } = DatePicker;
 
@@ -17,7 +16,8 @@ function Department(props) {
   const columns = [
     {
       title: '部门名称',
-      render: record => <a onClick={() => showUserModal(record)}>{record.dep_name}</a>,
+      // render: record => <a onClick={() => showUserModal(record)}>{record.dep_name}</a>,
+      render: record => <a onClick={() => showDepCompare(record)}>{record.dep_name}</a>,
     },
     {
       title: '执行项目人日',
@@ -67,15 +67,6 @@ function Department(props) {
     // },
   ];
   const filterRef = useRef({ pageSize: 99999 });
-  const onChangePage = pagination => {
-    dispatch({
-      type: 'report/queryUserReport',
-      payload: {
-        ...filterRef.current,
-        currentPage: pagination.current,
-      },
-    });
-  };
   const handleSearch = () => {
     form.validateFields((error, { time }) => {
       filterRef.current.s_time = time[0] ? moment(time[0]).format('YYYY-MM-DD') : null;
@@ -91,21 +82,19 @@ function Department(props) {
   };
   const handleDownload = () => {
     const token = getToken();
-    const s_time = !(filterRef.current.s_time) ? '' : filterRef.current.s_time;
-    const e_time = !(filterRef.current.e_time) ? '' : filterRef.current.e_time;
-    downloadFile(`/api/v2/workload/rpt/dep/export2excel?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}`, `项目报表${moment().format('YYYYMMDDHHMMSS')}.xlsx`)
+    const s_time = !filterRef.current.s_time ? '' : filterRef.current.s_time;
+    const e_time = !filterRef.current.e_time ? '' : filterRef.current.e_time;
+    downloadFile(
+      `/api/v2/workload/rpt/dep/export2excel?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}`,
+      `项目报表${moment().format('YYYYMMDDHHMMSS')}.xlsx`
+    );
   };
   const renderSearch = () => {
-    const formItemLayout = {
-      labelCol: { span: 5 },
-      wrapperCol: { span: 18 },
-    };
-
     return (
-      <Form layout="inline" {...formItemLayout}>
+      <Form layout="inline">
         <Form.Item label="时间">
           {form.getFieldDecorator('time', {
-            initialValue: [moment().startOf('years'), moment().endOf('years')],
+            initialValue: [moment().startOf('years'), moment()],
           })(<RangePicker placeholder="选择时间" />)}
         </Form.Item>
         <Form.Item>
@@ -127,7 +116,8 @@ function Department(props) {
       });
     }
   };
-  const showUserModal = item => {
+  // const showUserModal = item => {
+  const showDepCompare = item => {
     const { s_time, e_time } = filterRef.current;
     setModalFilter({
       s_time: s_time,
@@ -147,7 +137,9 @@ function Department(props) {
     <div>
       <div className={styles.topPart}>
         {renderSearch()}
-        <Button type="primary" onClick={handleDownload}>导出</Button>
+        <Button type="primary" onClick={handleDownload}>
+          导出
+        </Button>
       </div>
       <Table
         loading={loading}
@@ -156,10 +148,10 @@ function Department(props) {
         columns={columns}
         dataSource={dep.list}
         pagination={false}
-        onChange={onChangePage}
         onExpand={onExpand}
       />
-      <UserRptModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} />
+      {/* <UserRptModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} /> */}
+      <DepCompareModal filter={modalFilter} visible={visible} onCancel={() => setVisible(false)} />
     </div>
   );
 }

+ 126 - 0
src/pages/PurchaseAdmin/PurchaseList/Report/Finance.js

@@ -0,0 +1,126 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { connect } from 'dva';
+import { Form, Table, DatePicker, Input, Button, Select } from 'antd';
+import styles from './report.less';
+import moment from 'moment';
+import { downloadFile, getToken } from '@/utils/utils.js';
+
+const { RangePicker } = DatePicker;
+
+function Finance(props) {
+  const { dispatch, form, loading, finance } = props;
+  const [modalFilter, setModalFilter] = useState({});
+  const columns = [
+    { title: '资源名称', dataIndex: 'c_name' },
+    { title: '资源编号', dataIndex: 'user_name' },
+    { title: '资源归属部门', dataIndex: 'dep_name' },
+    { title: '项目名称', dataIndex: 'project_name' },
+    { title: '项目编号', dataIndex: 'project_code' },
+    { title: '项目状态', dataIndex: 'project_status' },
+    { title: '项目归属部门', dataIndex: 'project_dep_name' },
+    { title: '工时类型', dataIndex: 'type_name' },
+    { title: '保存未提交', dataIndex: 'un_audit_cnt' },
+    { title: '提交未审批', dataIndex: 'pending_audit_cnt' },
+    { title: '已拒绝', dataIndex: 'refuse_audit_cnt' },
+    { title: '审批通过', dataIndex: 'pass_audit_cnt' },
+  ];
+  const STATUS = [
+    { value: 0, label: '售前' },
+    { value: 1, label: '转执行' },
+    { value: 2, label: '转运营' },
+    { value: 3, label: '转质保' },
+  ];
+  const filterRef = useRef({ pageSize: 20 });
+  const onChangePage = pagination => {
+    dispatch({
+      type: 'report/queryFinanceReport',
+      payload: {
+        ...filterRef.current,
+        currentPage: pagination.current,
+      },
+    });
+  };
+  const handleSearch = () => {
+    form.validateFields((error, { time, project_name, project_status }) => {
+      filterRef.current.s_time = time[0] ? moment(time[0]).format('YYYY-MM-DD') : null;
+      filterRef.current.e_time = time[1] ? moment(time[1]).format('YYYY-MM-DD') : null;
+      filterRef.current.project_name = project_name;
+      filterRef.current.project_status = project_status;
+
+      dispatch({
+        type: 'report/queryFinanceReport',
+        payload: {
+          ...filterRef.current,
+        },
+      });
+    });
+  };
+  const handleDownload = () => {
+    const token = getToken();
+    const s_time = filterRef.current?.s_time || '';
+    const e_time = filterRef.current?.e_time || '';
+    const project_name = filterRef.current?.project_name || '';
+    const project_status = filterRef.current?.project_status || '';
+    downloadFile(
+      `/api/v2/workload/finance/export2excel?JWT-TOKEN=${token}&s_time=${s_time}&e_time=${e_time}&project_name=${project_name}&project_status${project_status}`,
+      `财务报表${moment().format('YYYYMMDDHHMMSS')}.xlsx`
+    );
+  };
+  const renderSearch = () => {
+    return (
+      <Form layout="inline">
+        <Form.Item label="时间">
+          {form.getFieldDecorator('time', {
+            initialValue: [moment().startOf('years'), moment()],
+          })(<RangePicker placeholder="选择时间" />)}
+        </Form.Item>
+        <Form.Item label="项目名称">{form.getFieldDecorator('project_name')(<Input />)}</Form.Item>
+        <Form.Item label="项目状态">
+          {form.getFieldDecorator('project_status', {
+            initialValue: null,
+          })(
+            <Select style={{ width: 120 }}>
+              <Select.Option value={null}>全部</Select.Option>
+              {STATUS.map(item => (
+                <Select.Option key={item.value}>{item.label}</Select.Option>
+              ))}
+            </Select>
+          )}
+        </Form.Item>
+        <Form.Item>
+          <Button type="primary" loading={loading} onClick={handleSearch}>
+            查询
+          </Button>
+        </Form.Item>
+      </Form>
+    );
+  };
+  useEffect(() => {
+    handleSearch();
+  }, []);
+
+  return (
+    <div>
+      <div className={styles.topPart}>
+        {renderSearch()}
+        <Button type="primary" onClick={handleDownload}>
+          导出
+        </Button>
+      </div>
+      <Table
+        loading={loading}
+        style={{ marginTop: 20 }}
+        rowKey={record => `${record.user_name}-${record.project_code}-${record.type_name}`}
+        columns={columns}
+        dataSource={finance.list}
+        pagination={finance.pagination}
+        onChange={onChangePage}
+      />
+    </div>
+  );
+}
+
+export default connect(({ report, loading }) => ({
+  finance: report.finance,
+  loading: loading.models.report,
+}))(Form.create()(Finance));

+ 111 - 78
src/pages/PurchaseAdmin/PurchaseList/Report/Project.js

@@ -1,8 +1,6 @@
 import React, { useEffect, useState, useRef } from 'react';
 import { connect } from 'dva';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Table, DatePicker, Input, Button, Select, message } from 'antd';
+import { Form, Table, DatePicker, Input, Button, Select, message, Popover } from 'antd';
 import report from './models/report';
 import styles from './report.less';
 import moment from 'moment';
@@ -11,7 +9,7 @@ import { downloadFile, getToken } from '@/utils/utils.js';
 
 const { Option } = Select;
 const { RangePicker } = DatePicker;
-const initDate = [moment().startOf('years'), moment().endOf('years')];
+const initDate = [moment().startOf('years'), moment()];
 
 // var currentYear = new Date().getFullYear();
 // var yearList = [];
@@ -26,15 +24,15 @@ function Resource(props) {
   const [visible, setVisible] = useState(false);
   const [modalFilter, setModalFilter] = useState({});
   const filterRef = useRef({});
-  const onChangePage = pagination => {
-    dispatch({
-      type: 'report/queryProjectReport',
-      payload: {
-        ...filterRef.current,
-        currentPage: pagination.current,
-      },
-    });
-  };
+  // const onChangePage = pagination => {
+  //   dispatch({
+  //     type: 'report/queryProjectReport',
+  //     payload: {
+  //       ...filterRef.current,
+  //       currentPage: pagination.current,
+  //     },
+  //   });
+  // };
   const getMonthColumns = () => {
     let arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];
     var time;
@@ -63,45 +61,53 @@ function Resource(props) {
       date[cYear].push(arr[cMonth]);
       current.add('month', 1);
     }
-    console.log(date);
     current.subtract('month', 12);
     let monthColumns = Object.keys(date).map(year => ({
       title: year + '年',
-      children: date[year].map((item) => {
+      children: date[year].map(item => {
         let key = current.format('YYYY-MM');
         current.add('month', 1);
         return {
           title: `${item}`,
           render: record => {
             const { month } = record;
-            return month[key]?.total_audit_cnt || 0;
+            return month.find(item => item.st == key)?.pass_audit_cnt || 0;
+            // return month[key]?.total_audit_cnt || 0;
+            // return (
+            //   <Popover
+            //     content={
+            //       <>
+            //         {`审核通过: ${month[key]?.pass_audit_cnt || 0}`}
+            //         <br />
+            //         {`审核中: ${month[key]?.pending_audit_cnt || 0}`}
+            //         <br />
+            //         {`审核拒绝: ${month[key]?.refuse_audit_cnt || 0}`}
+            //         <br />
+            //         {`未提审: ${month[key]?.refuse_audit_cnt || 0}`}
+            //       </>
+            //     }
+            //   >
+            //     {month[key]?.total_audit_cnt || 0}
+            //   </Popover>
+            // );
             // return `${JSON.stringify(month)}`;
           },
         };
       }),
     }));
     return monthColumns;
-
-    // return {
-    //   title: '月份',
-    //   children: arr.map((item, index) => ({
-    //     title: `${item}`,
-    //     dataIndex: `month[${String(index)}].total_audit_cnt`,
-    //     render: cnt => cnt || 0,
-    //   })),
-    // };
-  };
-  const onExpand = (expanded, record) => {
-    if (expanded && !record.isLoad) {
-      dispatch({
-        type: 'report/queryProjectReportDetail',
-        payload: {
-          ...filterRef.current,
-          record: record,
-        },
-      });
-    }
   };
+  // const onExpand = (expanded, record) => {
+  //   if (expanded && !record.isLoad) {
+  //     dispatch({
+  //       type: 'report/queryProjectReportDetail',
+  //       payload: {
+  //         ...filterRef.current,
+  //         record: record,
+  //       },
+  //     });
+  //   }
+  // };
 
   const handleSearch = () => {
     form.validateFields((error, { time, project_name }) => {
@@ -166,61 +172,89 @@ function Resource(props) {
     {
       title: '项目名称',
       render: record =>
-        !record.isParent ? (
-          <a onClick={() => showUserModal(record)}>{record.type_name}</a>
+        !record.child && record.total_audit_cnt ? (
+          <a onClick={() => showUserModal(record)}>{record.name}</a>
         ) : (
-          record.type_name
+          record.name
         ),
     },
-    {
-      title: '分项工作代码',
-      align: 'center',
-      dataIndex: 'type_code',
-    },
-    // {
-    //   title: '项目名称',
-    //   render: record => (record.project_id == 0 ? '' : record.project_name),
-    // },
-    // {
-    //   title: '项目编号',
-    //   dataIndex: 'project_code',
-    // },
-    // {
-    //   title: '工作内容',
-    //   dataIndex: 'type_name',
-    // },
     ...getMonthColumns(),
-    {
-      title: '总计',
-      dataIndex: 'month_rpt',
-      render: arr => (arr ? arr.reduce((total, item) => total + item.total_audit_cnt, 0) : ''),
-    },
+    { title: '总计', dataIndex: 'total_audit_cnt' },
   ];
+  // const columns = [
+  //   {
+  //     title: '项目名称',
+  //     render: record =>
+  //       !record.isParent ? (
+  //         <a onClick={() => showUserModal(record)}>{record.type_name}</a>
+  //       ) : (
+  //         record.type_name
+  //       ),
+  //   },
+  //   {
+  //     title: '分项工作代码',
+  //     align: 'center',
+  //     dataIndex: 'type_code',
+  //   },
+  //   // {
+  //   //   title: '项目名称',
+  //   //   render: record => (record.project_id == 0 ? '' : record.project_name),
+  //   // },
+  //   // {
+  //   //   title: '项目编号',
+  //   //   dataIndex: 'project_code',
+  //   // },
+  //   // {
+  //   //   title: '工作内容',
+  //   //   dataIndex: 'type_name',
+  //   // },
+  //   ...getMonthColumns(),
+  //   {
+  //     title: '总计',
+  //     dataIndex: 'month_rpt',
+  //     render: arr => (
+  //       <Popover
+  //         content={
+  //           <>
+  //             {`审核通过: ${
+  //               arr ? arr.reduce((total, item) => total + item.pass_audit_cnt, 0) : ''
+  //             }`}
+  //             <br />
+  //             {`审核中: ${
+  //               arr ? arr.reduce((total, item) => total + item.pending_audit_cnt, 0) : ''
+  //             }`}
+  //             <br />
+  //             {`审核拒绝: ${
+  //               arr ? arr.reduce((total, item) => total + item.refuse_audit_cnt, 0) : ''
+  //             }`}
+  //             <br />
+  //             {`未提审: ${arr ? arr.reduce((total, item) => total + item.un_audit_cnt, 0) : ''}`}
+  //           </>
+  //         }
+  //       >
+  //         {arr ? arr.reduce((total, item) => total + item.total_audit_cnt, 0) : ''}
+  //       </Popover>
+  //     ),
+  //   },
+  // ];
   const showUserModal = item => {
     const { s_time, e_time } = filterRef.current;
     setModalFilter({
       s_time: s_time,
       e_time: e_time,
+      type_id: item.id,
       project_id: item.project_id,
-      p_type_id: item.project_id == 0 ? item.p_type_id : 0,
-      type_id: item.type_id,
+      flag: item.flag,
     });
     setVisible(true);
   };
   useEffect(() => {
-    // dispatch({
-    //   type: 'report/queryProjectReport',
-    // });
-    // dispatch({
-    //   type: 'report/queryProject',
-    // });
     handleSearch();
   }, []);
 
-  useEffect(() => {
-    setExpandedRowKeys(project.list.map(item => item.id));
-  }, [project.list]);
-  console.log(project.list)
+  // useEffect(() => {
+  //   setExpandedRowKeys(project.list.map(item => item.id));
+  // }, [project.list]);
 
   return (
     <div className={styles.page}>
@@ -233,11 +267,10 @@ function Resource(props) {
       <Table
         style={{ marginTop: 20 }}
         columns={columns}
-        rowKey="id"
-        onExpand={onExpand}
-        dataSource={project.list}
-        pagination={project.pagination}
-        onChange={onChangePage}
+        rowKey="key"
+        childrenColumnName="child"
+        // onExpand={onExpand}
+        dataSource={project}
       />
       <UserProjectRptModal
         filter={modalFilter}

+ 1 - 3
src/pages/PurchaseAdmin/PurchaseList/Report/Resource.js

@@ -1,8 +1,6 @@
 import React, { useEffect, useState, useRef } from 'react';
 import { connect } from 'dva';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Table, DatePicker, Input, Button } from 'antd';
+import { Form, Table, DatePicker, Input, Button } from 'antd';
 import report from './models/report';
 import moment from 'moment';
 

+ 50 - 17
src/pages/PurchaseAdmin/PurchaseList/Report/UserProjectRptModal.js

@@ -1,23 +1,54 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Modal, Table, Input, Button } from 'antd';
+import { Modal, Table, Form, Input, Button } from 'antd';
 import { connect } from 'dva';
 import moment from 'moment';
 
-function AddModal(props) {
+function UserProjectRptModal(props) {
   const { dispatch, visible, onOk, onCancel, data, filter, loading, form } = props;
+  // const getMonthColumns = () => {
+  //   let arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];
+
+  //   return {
+  //     title: '月份',
+  //     children: arr.map((item, index) => ({
+  //       title: `${item}`,
+  //       dataIndex: `month[${String(index)}].total_audit_cnt`,
+  //       render: cnt => cnt || 0,
+  //     })),
+  //   };
+  // };
   const getMonthColumns = () => {
     let arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];
+    var time = [moment(filter.s_time), moment(filter.e_time)];
+    var date = {};
+    let eYear = time[1].year();
+    let eMonth = time[1].month();
+    let current = moment(time[0]);
+    let cYear, cMonth;
 
-    return {
-      title: '月份',
-      children: arr.map((item, index) => ({
-        title: `${item}`,
-        dataIndex: `month[${String(index)}].total_audit_cnt`,
-        render: cnt => cnt || 0,
-      })),
-    };
+    for (let i = 0; i < 12; i++) {
+      cYear = current.year();
+      cMonth = current.month();
+      if (!date[cYear]) date[cYear] = [];
+      date[cYear].push(arr[cMonth]);
+      current.add('month', 1);
+    }
+    current.subtract('month', 12);
+    let monthColumns = Object.keys(date).map(year => ({
+      title: year + '年',
+      children: date[year].map(item => {
+        let key = current.format('YYYY-MM');
+        current.add('month', 1);
+        return {
+          title: `${item}`,
+          render: record => {
+            const { month_rpt } = record;
+            return month_rpt.find(item => item.ts == key)?.pass_audit_cnt || 0;
+          },
+        };
+      }),
+    }));
+    return monthColumns;
   };
   const columns = [
     {
@@ -28,11 +59,12 @@ function AddModal(props) {
       title: '部门',
       dataIndex: 'dep_name',
     },
-    getMonthColumns(),
+    // getMonthColumns(),
+    ...getMonthColumns(),
     {
       title: '总计',
       dataIndex: 'month_rpt',
-      render: arr => (arr ? arr.reduce((total, item) => total + item.total_audit_cnt, 0) : ''),
+      render: arr => (arr ? arr.reduce((total, item) => total + item.pass_audit_cnt, 0) : ''),
     },
   ];
   const onChangePage = pagination => {
@@ -45,7 +77,8 @@ function AddModal(props) {
     });
   };
   const handleSearch = () => {
-    if (!filter?.project_id && !filter?.p_type_id) return;
+    // if (!filter?.project_id && !filter?.p_type_id) return;
+    if (!filter?.classification) return;
     const dep_name = form.getFieldValue('dep_name');
     dispatch({
       type: 'report/queryUserProjectReport',
@@ -56,7 +89,7 @@ function AddModal(props) {
     });
   };
   useEffect(() => {
-    if (!filter?.project_id && !filter?.p_type_id) return;
+    if (!filter?.project_id) return;
     dispatch({
       type: 'report/queryUserProjectReport',
       payload: filter,
@@ -89,4 +122,4 @@ function AddModal(props) {
 export default connect(({ report, loading }) => ({
   data: report.projectUser,
   loading: loading.models.report,
-}))(Form.create()(AddModal));
+}))(Form.create()(UserProjectRptModal));

+ 2 - 2
src/pages/PurchaseAdmin/PurchaseList/Report/UserRptModal.js

@@ -3,7 +3,7 @@ import { Modal, Table } from 'antd';
 import { connect } from 'dva';
 import moment from 'moment';
 
-function AddModal(props) {
+function UserRptModal(props) {
   const { dispatch, visible, onOk, onCancel, data, filter, loading } = props;
   const columns = [
     {
@@ -94,4 +94,4 @@ function AddModal(props) {
 export default connect(({ report, loading }) => ({
   data: report.depUser,
   loading: loading.models.report,
-}))(AddModal);
+}))(UserRptModal);

+ 148 - 53
src/pages/PurchaseAdmin/PurchaseList/Report/models/report.js

@@ -6,11 +6,15 @@ import {
   queryProjectReportDetail,
   queryUserProjectReport,
   queryProject,
+  queryDepCompare,
+  queryDepCompareUser,
+  queryFinanceReport,
+  queryUserProject,
 } from '@/services/workHours';
 import { queryRole } from '@/services/SysAdmin';
 import { message } from 'antd';
 import moment from 'moment';
-import { filter } from 'lodash';
+import { filter, times } from 'lodash';
 import list from '../../List/models/list';
 
 export default {
@@ -32,10 +36,17 @@ export default {
       list: [],
       pagination: {},
     },
-    project: {
+    // project: {
+    //   list: [],
+    //   pagination: {},
+    // },
+    project: [],
+    finance: {
       list: [],
       pagination: {},
     },
+    depCompare: [],
+    depUserProject: [],
     projectList: [],
   },
 
@@ -63,18 +74,18 @@ export default {
     },
     *queryUserProjectReport({ payload }, { call, put }) {
       const { data } = yield call(queryUserProjectReport, payload);
-      try {
-        data.list.forEach(item => {
-          let month = {};
-          item.month_rpt.forEach(mItem => {
-            let m = moment(mItem.ts).month();
-            month[m] = mItem;
-          });
-          item.month = month;
-        });
-      } catch (error) {
-        console.error(error);
-      }
+      // try {
+      //   data.list.forEach(item => {
+      //     let month = {};
+      //     item.month_rpt.forEach(mItem => {
+      //       let m = moment(mItem.ts).month();
+      //       month[m] = mItem;
+      //     });
+      //     item.month = month;
+      //   });
+      // } catch (error) {
+      //   console.error(error);
+      // }
       yield put({
         type: 'save',
         payload: { projectUser: data },
@@ -84,7 +95,7 @@ export default {
       const { filter = {}, record = {} } = payload;
       const { data } = yield call(queryDepReport, { ...filter, dep_id: record.dep_id });
       data.list.forEach(item => {
-        if(item.sub_dep_num) item.children = [];
+        if (item.sub_dep_num) item.children = [];
         item.isLoad = false;
       });
       // 判断是否为根节点
@@ -106,55 +117,88 @@ export default {
     *queryProjectReport({ payload }, { call, put }) {
       const { data } = yield call(queryProjectReport, {
         ...payload,
-        // pageSize: 20,
       });
-      let treeData = {};
-      try {
-        data.list.forEach(item => {
-          let p_type_id = item.project_id == 0 ? 't' + item.p_type_id : 'p' + item.project_id;
-          treeData[p_type_id] = {
-            id: p_type_id,
-            type_name:
-              item.project_id == 0
-                ? item.project_name
-                : `${item.project_name}(${item.project_code})`,
-            project_name: item.project_name,
-            project_id: item.project_id,
-            project_code: item.project_code,
-            p_type_id: item.p_type_id,
-            month_rpt: item.month_rpt,
-            type_code: '',
-            children: [],
-            month: {},
-            isLoad: false,
-            isParent: true,
-          };
-          item.month_rpt.forEach(mItem => {
-            // let m = moment(mItem.ts).month();
-            treeData[p_type_id].month[mItem.ts] = mItem;
-          });
-        });
-      } catch (error) {
-        console.log(error);
-      }
-      console.log(treeData);
+      const idHelper = (item, params) => {
+        if (params.flag === undefined) {
+          item.child.forEach(childItem => idHelper(childItem, { flag: item.id }));
+          item.key = 'f' + item.id;
+        } else {
+          item.flag = params.flag;
+          if (params.project_id === undefined) {
+            item.child?.forEach(childItem =>
+              idHelper(childItem, { flag: item.flag, project_id: item.id })
+            );
+            item.key = 'f' + item.flag + 'p' + item.id;
+          } else {
+            item.project_id = params.project_id;
+            item.key = 'f' + item.flag + 'p' + item.project_id + 't' + item.id;
+          }
+        }
+      };
+      data.forEach(item => idHelper(item, {}));
       yield put({
         type: 'save',
         payload: {
-          project: {
-            list: Object.values(treeData),
-            pagination: data.pagination,
-          },
+          project: data,
         },
       });
     },
+    // *queryProjectReport({ payload }, { call, put }) {
+    //   const { data } = yield call(queryProjectReport, {
+    //     ...payload,
+    //     // pageSize: 20,
+    //   });
+    //   let treeData = {};
+    //   try {
+    //     data.list.forEach(item => {
+    //       let p_type_id =
+    //         item.rpt.project_id == 0 ? 't' + item.rpt.p_type_id : 'p' + item.rpt.project_id;
+    //       treeData[p_type_id] = {
+    //         id: p_type_id,
+    //         type_name:
+    //           item.rpt.project_id == 0
+    //             ? item.rpt.project_name
+    //             : `${item.rpt.project_name}(${item.rpt.project_code})`,
+    //         project_name: item.rpt.project_name,
+    //         project_id: item.rpt.project_id,
+    //         project_code: item.rpt.project_code,
+    //         p_type_id: item.rpt.p_type_id,
+    //         month_rpt: item.rpt.month_rpt,
+    //         classification: item.classification,
+    //         project_ids: item.project_ids,
+    //         type_code: '',
+    //         children: [],
+    //         month: {},
+    //         isLoad: false,
+    //         isParent: true,
+    //       };
+    //       item.rpt.month_rpt.forEach(mItem => {
+    //         // let m = moment(mItem.ts).month();
+    //         treeData[p_type_id].month[mItem.ts] = mItem;
+    //       });
+    //     });
+    //   } catch (error) {
+    //     console.log(error);
+    //   }
+    //   console.log(treeData);
+    //   yield put({
+    //     type: 'save',
+    //     payload: {
+    //       project: {
+    //         list: Object.values(treeData),
+    //         pagination: data.pagination,
+    //       },
+    //     },
+    //   });
+    // },
     *queryProjectReportDetail({ payload = {} }, { call, put, select }) {
       const { record = {} } = payload;
       const { data } = yield call(queryProjectReportDetail, {
         s_time: payload.s_time,
         e_time: payload.e_time,
-        project_id: record.project_id,
+        classification: record.classification,
         p_type_id: record.p_type_id,
+        project_ids: record.project_ids.join(','),
       });
       data.forEach((item, index) => {
         let month = {};
@@ -162,18 +206,69 @@ export default {
           // let m = moment(mItem.ts).month();
           month[mItem.ts] = mItem;
         });
-      
+
         item.month = month;
         item.id = record.id + '_' + index;
       });
       const project = yield select(s => s.report.project);
-      record.children = data.length == 0 ? null : data;
+      record.children =
+        data.length == 0 ? null : data.map(item => ({ ...item, project_ids: record.project_ids }));
       record.isLoad = true;
       yield put({
         type: 'save',
         payload: { project: { ...project } },
       });
     },
+    *queryFinanceReport({ payload = {} }, { call, put }) {
+      const { data } = yield call(queryFinanceReport, payload);
+      yield put({
+        type: 'save',
+        payload: { finance: data },
+      });
+    },
+    *queryDepCompare({ payload }, { call, put }) {
+      let { data } = yield call(queryDepCompare, payload);
+      if (data) {
+        const treeHelper = item => {
+          item.key = 'd' + item.dep_id;
+          if (!item.child && item.total_cnt) item.child = [];
+          item.child && item.child.forEach(childItem => treeHelper(childItem));
+        };
+        data.forEach(item => treeHelper(item));
+      } else data = [];
+      yield put({
+        type: 'save',
+        payload: { depCompare: data },
+      });
+    },
+    *queryDepCompareUser({ payload }, { call, put, select }) {
+      try {
+        let record = payload.record;
+        payload.dep_id = record.dep_id;
+        delete payload.record;
+        let { data } = yield call(queryDepCompareUser, payload);
+        data.forEach(item => {
+          item.key = 'u' + item.user_id;
+          item.dep_id = payload.dep_id;
+        });
+        record.child = [...record.child, ...data];
+        record.isLoad = true;
+      } catch (error) {
+        console.log(error);
+      }
+      const depCompare = yield select(s => s.report.depCompare);
+      yield put({
+        type: 'save',
+        payload: { depCompare: [...depCompare] },
+      });
+    },
+    *queryDepUserProject({ payload }, { call, put }) {
+      const { data } = yield call(queryUserProject, payload);
+      yield put({
+        type: 'save',
+        payload: { depUserProject: data },
+      });
+    },
   },
 
   reducers: {

+ 71 - 36
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/AddModal.js

@@ -1,14 +1,24 @@
 import React, { useState } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { message, Modal, Select, DatePicker } from 'antd';
+import { message, Form, Modal, Select, DatePicker } from 'antd';
+import { connect } from 'dva';
 import moment from 'moment';
 
 const { Option } = Select;
 const { MonthPicker } = DatePicker;
 
 function AddModal(props) {
-  const { visible, onOk, onCancel, time, form, typeList = [], projectList = {}, loading } = props;
+  const {
+    visible,
+    onOk,
+    onCancel,
+    time,
+    form,
+    typeList = [],
+    subTypeList = [],
+    dispatch,
+    project = [],
+    loading,
+  } = props;
   const [type, setType] = useState({});
   const [subType, setSubType] = useState({});
   const handleOk = () => {
@@ -50,36 +60,64 @@ function AddModal(props) {
   };
   const onChangeType = value => {
     let item = typeList.find(t => t.id == value);
-    if (value == 35) {
-      form.setFieldsValue({
-        project: null,
-        subType: item.children[0].id + '',
+    form.setFieldsValue({
+      project: null,
+      subType: null,
+    });
+    setType(item);
+    setSubType({});
+    dispatch({
+      type: 'workload/save',
+      payload: { subTypeList: [] },
+    });
+    if (!item.type) {
+      dispatch({
+        type: 'workload/queryProject',
+        payload: { stage: value },
       });
-      setSubType(item.children[0]);
-      setType(item);
     } else {
-      form.setFieldsValue({
-        project: null,
-        subType: null,
+      dispatch({
+        type: 'workload/querySubType',
+        payload: { parent_id: item.id },
+        callback: subTypeList => {
+          if (value == 33) {
+            form.setFieldsValue({
+              project: null,
+              subType: subTypeList[0].id + '',
+            });
+            setSubType(subTypeList[0]);
+          }
+        },
       });
-      setSubType({});
-      setType(item);
     }
   };
   const onChangeSubType = value => {
-    let item = type.children.find(t => t.id == value);
+    let item = subTypeList.find(t => t.id == value);
     setSubType(item);
   };
 
   //售前支持更改项目时与子类选单联动
   const onChangeProject = value => {
-    if (value == '0') {
-      form.setFieldsValue({ subType: type.children[0].id + '' });
-      setSubType(type.children[0]);
-    } else {
-      form.setFieldsValue({ subType: type.children[1].id + '' });
-      setSubType(type.children[1]);
-    }
+    form.setFieldsValue({ subType: '' });
+    dispatch({
+      type: 'workload/querySubType',
+      payload: { parent_id: type.id, project_id: value },
+      callback: subTypeList => {
+        if (type.id == 35) {
+          form.setFieldsValue({ subType: subTypeList[0].id + '' });
+          setSubType(subTypeList[0]);
+        }
+        if (type.id == 2) {
+          if (value == '0') {
+            form.setFieldsValue({ subType: subTypeList[0].id + '' });
+            setSubType(subTypeList[0]);
+          } else {
+            form.setFieldsValue({ subType: subTypeList[1].id + '' });
+            setSubType(subTypeList[1]);
+          }
+        }
+      },
+    });
   };
 
   //分类选单
@@ -119,21 +157,15 @@ function AddModal(props) {
           //售前支持特殊判断,增加“无项目”,选择项目时与子类联动
           <Select
             showSearch
-            onChange={type.id === 2 ? onChangeProject : null}
+            onChange={onChangeProject}
             placeholder="请选择项目"
             optionFilterProp="children"
             filterOption={(input, option) =>
-              // console.log(option.props.children)
               option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }
           >
             {type.id === 2 && <Option key={'0'}>售前支持(10100)</Option>}
-            {(type.id === 2
-              ? projectList.project0
-              : type.id === 3
-              ? projectList.project1
-              : projectList.project7
-            ).map(item => (
+            {project.map(item => (
               <Option key={String(item.ID)}>{`${item.Name}(${item.Code})`}</Option>
             ))}
           </Select>
@@ -156,11 +188,11 @@ function AddModal(props) {
         })(
           //售前支持特殊判断,子类不可选
           <Select
-            disabled={type.id === 2 || type.id === 35}
+            disabled={type.id === 2}
             onChange={onChangeSubType}
-            placeholder={type.id === 2 || type.id === 35 ? null : '请选择子类'}
+            placeholder={type.id === 2 ? null : '请选择子类'}
           >
-            {(type.children || []).map(item => (
+            {(subTypeList || []).map(item => (
               <Option key={String(item.id)}>{`${item.name}(${item.code})`}</Option>
             ))}
           </Select>
@@ -181,7 +213,7 @@ function AddModal(props) {
     >
       <Form labelCol={{ span: 4 }} wrapperCol={{ span: 16 }}>
         {renderType()}
-        {(type.id === 2 || type.id === 3 || type.id === 35) && renderProject()}
+        {type.type == 0 && renderProject()}
         {renderSubType()}
         <Form.Item label="时间">{time}</Form.Item>
       </Form>
@@ -189,4 +221,7 @@ function AddModal(props) {
   );
 }
 
-export default Form.create()(AddModal);
+export default connect(({ workload }) => ({
+  project: workload.project,
+  subTypeList: workload.subTypeList,
+}))(Form.create()(AddModal));

+ 46 - 11
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/Auth.js

@@ -1,9 +1,8 @@
 import React, { useState, useEffect, useRef } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
 import {
   message,
   InputNumber,
+  Form,
   Table,
   Divider,
   Modal,
@@ -23,7 +22,7 @@ import moment from 'moment';
 import { getList } from '@/services/devOpsScoreRule';
 
 function List(props) {
-  const { typeList, dispatch, loading, form, list, project, currentUser, allType, user } = props;
+  const { dispatch, loading, form, list, project, currentUser, allType, user } = props;
   const [visible, setVisible] = useState(false);
   const [filter, setFilter] = useState({});
   const [current, setCurrent] = useState({
@@ -84,10 +83,32 @@ function List(props) {
 
   const onChangeDate = value => {
     let time = value.format('YYYY-MM-DD');
-    setCurrent({
-      date: value,
-      list: list.filter(item => item.time == time),
-    });
+    if (current.date.format('YYYY-MM') != value.format('YYYY-MM')) {
+      const s_date = value.format('YYYY-MM') + '-01';
+      const e_date = moment(s_date)
+        .add('month', 1)
+        .add('days', -1)
+        .format('YYYY-MM-DD');
+      dispatch({
+        type: 'workload/queryAuthWorkHours',
+        payload: {
+          pageSize: 9999,
+          s_time: s_date + ' 00:00:00',
+          e_time: e_date + ' 23:59:59',
+        },
+        callback: list => {
+          setCurrent({
+            date: value,
+            list: list.filter(item => item.time == time),
+          });
+        },
+      });
+    } else {
+      setCurrent({
+        date: value,
+        list: list.filter(item => item.time == time),
+      });
+    }
   };
 
   const getList = () => {
@@ -105,14 +126,29 @@ function List(props) {
     dispatch({
       type: 'workload/queryWorkType',
       callback: () => {
+        const s_date = current.date.format('YYYY-MM') + '-01';
+        const e_date = moment(s_date)
+          .add('month', 1)
+          .add('days', -1)
+          .format('YYYY-MM-DD');
         dispatch({
           type: 'workload/queryAuthWorkHours',
           payload: {
             pageSize: 9999,
+            s_time: s_date + ' 00:00:00',
+            e_time: e_date + ' 23:59:59',
+          },
+          callback: list => {
+            let time = current.date.format('YYYY-MM-DD');
+            setCurrent({
+              ...current,
+              list: list.filter(item => item.time == time),
+            });
           },
         });
       },
     });
+
     // 查询项目列表
     dispatch({
       type: 'workload/queryProject',
@@ -132,9 +168,9 @@ function List(props) {
     };
   }, [currentUser.ID]);
 
-  useEffect(() => {
-    onChangeDate(current.date);
-  }, [list]);
+  // useEffect(() => {
+  //   onChangeDate(current.date);
+  // }, [list]);
 
   return (
     <div>
@@ -170,7 +206,6 @@ function List(props) {
 
 export default connect(({ workload, user, loading }) => ({
   list: workload.list,
-  typeList: workload.typeList,
   user: user.list,
   allType: workload.allType,
   project: workload.project,

+ 3 - 4
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/AuthWorkList.js

@@ -1,7 +1,6 @@
 import React, { useState } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
 import {
+  Form,
   InputNumber,
   Input,
   Button,
@@ -107,7 +106,7 @@ function AuthWorkList(props) {
       if (id == '0') {
         name = '其他';
       } else {
-        name = project.find(p => p.ID == id)?.Name;
+        name = item.Project.Name;
       }
 
       if (!data[id]) data[id] = { list: [], name, id };
@@ -191,7 +190,7 @@ function AuthWorkList(props) {
                   }
                 >
                   {project.map(item => (
-                    <Option key={item.ID}>{item.Name}</Option>
+                    <Option key={item.ID}>{`${item.Name}(${item.Code})`}</Option>
                   ))}
                 </Select>
               )}

+ 1 - 2
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/CalendarModal.js

@@ -1,12 +1,11 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
 import {
   InputNumber,
   message,
   Modal,
   Calendar,
   Popover,
+  Form,
   Input,
   Row,
   Col,

+ 1 - 3
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/RejectModal.js

@@ -1,7 +1,5 @@
 import React, { useState } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Input, message, Modal } from 'antd';
+import { Input, message, Form, Modal } from 'antd';
 
 function RejectModal(props) {
   const { visible, onOk, onCancel, form, loading } = props;

+ 1 - 3
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/SearchForm.js

@@ -1,7 +1,5 @@
 import React from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Select, DatePicker, Button } from 'antd';
+import { Form, Select, DatePicker, Button } from 'antd';
 import moment from 'moment';
 
 const { Option } = Select;

+ 11 - 13
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/WorkList.js

@@ -1,41 +1,39 @@
 import React, { useState, useEffect } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { InputNumber, Popover, Divider, Table, message } from 'antd';
+import { Form, InputNumber, Popover, Divider, Table, message } from 'antd';
 
 function WorkList(props) {
-  const { list, form, onAuth, onSave, project = [], allType } = props;
+  const { list, form, onAuth, onSave, allType } = props;
   const [expandedRowKeys, setExpandedRowKeys] = useState([]);
   const [dataSource, setDataSource] = useState([]);
   const columns = [
     {
       title: '分类',
       dataIndex: 'type_id',
-      width: 230,
+      width: '30%',
       render: type_id => allType[type_id]?.name,
     },
     {
       title: '所属项目',
-      dataIndex: 'project_id',
-      render: (project_id, item) => {
+      width: '40%',
+      render: item => {
         if (item.zIndex === 0) return '';
-        return project.find(p => p.ID == project_id)?.Name || '-';
+        return item.Project?.Name || '-';
       },
     },
     {
       title: '审核状态',
-      width: 100,
+      width: '15%',
       render: record => renderStatus(record),
     },
     {
       title: '工时',
-      width: 110,
+      width: '15%',
       render: item => {
         if (item.zIndex === 0) {
           return '总计 ' + item.children.reduce((total, item) => total + item.workload, 0) + '小时';
         }
         if (item.audit_state == 1 || item.audit_state == 2) return item?.workload + '小时';
-        return form.getFieldDecorator(`${item.type_id}.${item.time}`, {
+        return form.getFieldDecorator(`${item.type_id}.${item.project_id}.${item.time}`, {
           initialValue: item?.workload,
         })(<InputNumber style={{width: 60}} min={0} step={0.5} />);
       },
@@ -49,12 +47,12 @@ function WorkList(props) {
         return (
           <>
             <a
-              onClick={() => onHandleSave(item, form.getFieldValue(`${item.type_id}.${item.time}`))}
+              onClick={() => onHandleSave(item, form.getFieldValue(`${item.type_id}.${item.project_id}.${item.time}`))}
             >
               保存
             </a>
             <Divider type="vertical"></Divider>
-            <a onClick={() => onAuth(item, form.getFieldValue(`${item.type_id}.${item.time}`))}>
+            <a onClick={() => onAuth(item, form.getFieldValue(`${item.type_id}.${item.project_id}.${item.time}`))}>
               上报审批
             </a>
           </>

+ 12 - 12
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/index.js

@@ -1,7 +1,5 @@
 import React, { useState, useEffect, useRef } from 'react';
-import { Form } from '@ant-design/compatible';
-import '@ant-design/compatible/assets/index.css';
-import { Modal, Button, Calendar, Popover, Spin, Row, Col } from 'antd';
+import { Form, Modal, Button, Calendar, Popover, Spin, Row, Col } from 'antd';
 import AddModal from './AddModal';
 import WorkList from './WorkList';
 import { connect } from 'dva';
@@ -14,7 +12,6 @@ function List(props) {
     loading,
     form,
     dataList,
-    project,
     projectList,
     currentUser,
     allType,
@@ -178,6 +175,13 @@ function List(props) {
     dispatch({
       type: 'workload/addWorkHours',
       payload: params,
+      callback: list => {
+        let time = current.date.format('YYYY-MM-DD');
+        setCurrent({
+          ...current,
+          list: list.filter(item => item.time == time),
+        });
+      },
     });
   };
 
@@ -265,7 +269,7 @@ function List(props) {
       if (!pid) return '';
       name = allType[pid].name;
     } else {
-      name = project?.find(p => p.ID == item.project_id)?.Name;
+      name = item.Project.Name;
     }
     return name;
   };
@@ -300,9 +304,9 @@ function List(props) {
       },
     });
     // 查询项目列表
-    dispatch({
-      type: 'workload/queryProject',
-    });
+    // dispatch({
+    //   type: 'workload/queryProject',
+    // });
     return () => {
       // 清空查询数据
       dispatch({
@@ -355,7 +359,6 @@ function List(props) {
 
             <WorkList
               allType={allType}
-              project={project}
               list={current.list}
               onAuth={onAuth}
               onSave={onSave}
@@ -366,7 +369,6 @@ function List(props) {
 
       <AddModal
         typeList={typeList}
-        projectList={projectList}
         visible={visible}
         onOk={onAddWork}
         time={current.date?.format('YYYY-MM-DD')}
@@ -380,8 +382,6 @@ export default connect(({ workload, user, loading }) => ({
   dataList: workload.dataList,
   typeList: workload.typeList,
   allType: workload.allType,
-  project: workload.project,
-  projectList: workload.projectList,
   currentUser: user.currentUser,
   loading: loading.models.workload,
 }))(Form.create()(List));

+ 25 - 20
src/pages/PurchaseAdmin/PurchaseList/WorkingHours/models/workingHours.js

@@ -18,35 +18,43 @@ export default {
     dataList: [],
     typeList: [],
     allType: {},
+    subTypeList: [],
     project: [],
     projectList: { project0: [], project1: [] },
     filter: {},
   },
 
   effects: {
-    *queryWorkType({ payload, callback }, { call, put }) {
-      const { data } = yield call(queryWorkType, {});
+    *queryWorkType({ callback }, { call, put }) {
+      const { data } = yield call(queryWorkType, { parent_id: -1 });
+      let typeList = [];
       let allType = {};
       for (let i = 0; i < data.length; i++) {
         let item = data[i];
         allType[item.id] = item;
-        let { data: subData } = yield call(queryWorkType, { parent_id: item.id });
-        item.children = subData;
-        subData.forEach(item => {
-          allType[item.id] = item;
-        });
+        if (item.parent_id == 0) {
+          typeList.push(item);
+        }
       }
       yield put({
         type: 'save',
-        payload: { typeList: data, allType },
+        payload: { typeList, allType },
       });
       callback && callback();
     },
+    *querySubType({ payload, callback }, { call, put }) {
+      const { data } = yield call(queryWorkType, payload);
+      callback && callback(data);
+      yield put({
+        type: 'save',
+        payload: { subTypeList: data },
+      });
+    },
     /**
      *
      * payload = {s_time,e_time,user_id}
      */
-    *queryAuthWorkHours({ payload }, { call, put, select }) {
+    *queryAuthWorkHours({ payload, callback }, { call, put, select }) {
       const workload = yield select(s => s.workload);
       const { typeList, filter, allType } = workload;
       // 合并新旧过滤条件
@@ -80,6 +88,9 @@ export default {
       //     });
       //   });
       // });
+
+      callback && callback(data.list);
+
       yield put({
         type: 'save',
         payload: { list: data.list, filter: newFilter },
@@ -163,22 +174,16 @@ export default {
         });
       }
     },
-    *queryProject({ callback }, { call, put }) {
-      const response0 = yield call(queryProject, { stage: 0 });
-      const response1 = yield call(queryProject, { stage: 1 });
-      const response7 = yield call(queryProject, { stage: 7 });
-      if (response0 && response1 && response7) {
+    *queryProject({ payload = {}, callback }, { call, put }) {
+      const res = yield call(queryProject, payload);
+      if (res) {
         yield put({
           type: 'save',
           payload: {
-            project: [...response0.data.list, ...response1.data.list, ...response7.data.list],
-            projectList: {
-              project0: response0.data.list,
-              project1: response1.data.list,
-              project7: response7.data.list,
-            },
+            project: res.data.list,
           },
         });
+        callback && callback();
       }
     },
   },

+ 16 - 0
src/services/approval.js

@@ -88,6 +88,22 @@ export async function startExecution(data) {
   });
 }
 
+//转运营
+export async function startOperate(data) {
+  return request(`/api/v2/project_code/to_opt`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
+//转质保
+export async function startQuality(data) {
+  return request(`/api/v2/project_code/to_wty`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
 //移除成员
 export async function deleteMember(data) {
   return request(`/api/v2/project_code/user/${data.project_code_id}/${data.user_id}`, {

+ 17 - 1
src/services/workHours.js

@@ -79,5 +79,21 @@ export async function queryProjectReport(params) {
 }
 
 export async function queryProjectReportDetail(params) {
-  return request(`/api/v2/workload/project/month/rpt?${stringify(params)}`)
+  return request(`/api/v2/workload/project/month/rpt?${stringify(params)}`);
 }
+
+export async function queryFinanceReport(params) {
+  return request(`/api/v2/workload/finance/rpt?${stringify(params)}`);
+}
+
+export async function queryDepCompare(params) {
+  return request(`/api/v2/workload/dep/compare?${stringify(params)}`);
+}
+
+export async function queryDepCompareUser(params) {
+  return request(`/api/v2/workload/dep/compare/users?${stringify(params)}`);
+}
+
+export async function queryUserProject(params){
+  return request(`/api/v2/workload/dep/compare/users/project?${stringify(params)}`)
+}