浏览代码

迁移人力项目

Renxy 2 年之前
父节点
当前提交
a249197752

+ 20 - 2
.umirc.ts

@@ -1,5 +1,9 @@
 import { defineConfig } from '@umijs/max';
 
+import locale from 'antd/es/calendar/locale/zh_CN';
+import moment from 'moment';
+moment.locale('zh-cn');
+
 export default defineConfig({
   hash: true,
   antd: {},
@@ -12,13 +16,21 @@ export default defineConfig({
     title: '金科环境数字化管理平台',
     locale: false,
   },
+  locale: {
+    antd: true, // 如果项目依赖中包含 `antd`,则默认为 true
+    baseNavigator: true,
+    baseSeparator: '-',
+    default: 'zh-CN',
+    title: false,
+    useLocalStorage: true,
+  },
   title: '金科环境数字化管理平台',
   publicPath: process.env.NODE_ENV == 'development' ? '/' : '/gt-dig/',
   proxy: {
     '/api': {
       // target: 'http://47.96.12.136:8888/',
-      // target: 'http://47.96.12.136:8895/',
-      target: 'http://120.55.44.4:8902/',
+      target: 'http://47.96.12.136:8895/',
+      // target: 'http://120.55.44.4:8902/',
       changeOrigin: true,
     },
   },
@@ -170,6 +182,12 @@ export default defineConfig({
       component: './ManufacturerMng/Firm',
       icon: 'https://gt-digitization.oss-cn-hangzhou.aliyuncs.com/doc/department/2023-04/manufacturerIcon.png',
     },
+    {
+      name: '人日立项',
+      path: '/work-hours',
+      component: './PurchaseList/WorkingHours/index',
+      icon: 'https://gt-digitization.oss-cn-hangzhou.aliyuncs.com/doc/department/2023-04/manufacturerIcon.png',
+    },
   ],
   npmClient: 'yarn',
 });

+ 0 - 1
package.json

@@ -17,7 +17,6 @@
     "@umijs/max": "^4.0.64",
     "antd": "^5.0.0",
     "dayjs": "^1.11.7",
-    "moment": "^2.29.4",
     "qs": "^6.11.1",
     "react-file-viewer": "^1.2.1",
     "react-zmage": "^0.8.5-beta.36",

+ 28 - 1
src/access.ts

@@ -1,6 +1,33 @@
 export default (initialState: any) => {
+  const currentUser = initialState?.user;
+  console.log(currentUser);
+  const checkReport = (state: number) => {
+    if (currentUser?.IsSuper) return true;
+    const manager =
+      currentUser.is_leader || currentUser.is_opt_mgr || currentUser.is_wty_mgr;
+    switch (state) {
+      case 0:
+        return (
+          currentUser.is_accountant ||
+          manager ||
+          currentUser.Permission['func-01-point-works-report']
+        );
+      case 1:
+        return (
+          manager || currentUser.Permission['func-01-point-works-report-p']
+        );
+      case 2:
+        return currentUser.Permission['func-01-point-works-report-d'];
+      case 3:
+        return (
+          currentUser.is_accountant ||
+          currentUser.Permission['func-01-point-works-report-p-s']
+        );
+    }
+  };
+
   return {
-    isAdmin: initialState?.user?.IsSuper,
+    isAdmin: currentUser?.IsSuper,
     appMenu: true,
     adminMenu: false,
   };

+ 4 - 0
src/app.tsx

@@ -7,6 +7,10 @@ import { history } from 'umi';
 import logo from '@/assets/logo.png';
 import { useModel } from '@umijs/max';
 import { queryCurrentV2 } from './services/user';
+import dayjs from 'dayjs';
+import 'dayjs/locale/zh-cn';
+
+dayjs.locale('zh-cn');
 
 // 全局初始化数据配置,用于 Layout 用户信息和权限初始化
 // 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate

+ 2 - 1
src/pages/FileManagement/components/model.jsx

@@ -1,7 +1,8 @@
 import { Input, Modal } from 'antd';
 import { useRef, useState } from 'react';
 
-const AddFileModal = ({ id, visible, handleOk, handleCancel }) => {
+const AddFileModal = (props) => {
+  const { id, visible, handleOk, handleCancel } = props;
   const [value, setValue] = useState('新建文件夹');
 
   const onChange = () => {

+ 3 - 3
src/pages/FileManagement/index.js

@@ -777,14 +777,14 @@ function FileManagement(props) {
         onCancel={() => setAddOpen(false)}
         onOk={(data) => runPer(data)}
       />
-      <AddFileModal
-        id={node?.id}
+      {/* <AddFileModal
+        id={node?.id || ''}
         visible={visible}
         handleOk={(value) => {
           RunCreate({ ...value, user_name: user?.CName });
         }}
         handleCancel={() => setVisible(false)}
-      />
+      /> */}
       <PerModal
         node={node}
         fileNode={tableData?.find((item) => item.key == selectedRowKeys[0])}

+ 153 - 0
src/pages/PurchaseList/Index.js

@@ -0,0 +1,153 @@
+import React, { useEffect } from 'react';
+import { Layout, Menu } from 'antd';
+import { connect } from 'dva';
+import RightContent from './RightContent';
+import Link from 'umi/link';
+
+const { Header, Content, Footer } = Layout;
+const { SubMenu } = Menu;
+
+// 布局
+function LayoutDetail(props) {
+  const { currentUser, permission } = props;
+  const isAdmin = currentUser.UserName == 'admin';
+  var logoStyle = {
+    color: 'white',
+    fontWeight: 600,
+    fontSize: 20,
+    verticalAlign: 'middle',
+    marginRight: 60,
+    width: 120,
+  };
+  useEffect(() => {
+    // 查询用户信息
+    props.dispatch({
+      type: 'user/fetchCurrent',
+    });
+  }, []);
+  const checkReport = (state) => {
+    if (isAdmin) return true;
+    const manager =
+      currentUser.is_leader || currentUser.is_opt_mgr || currentUser.is_wty_mgr;
+    switch (state) {
+      case 0:
+        return (
+          currentUser.is_accountant ||
+          manager ||
+          permission['func-01-point-works-report']
+        );
+      case 1:
+        return manager || permission['func-01-point-works-report-p'];
+      case 2:
+        return permission['func-01-point-works-report-d'];
+      case 3:
+        return (
+          currentUser.is_accountant ||
+          permission['func-01-point-works-report-p-s']
+        );
+    }
+  };
+  return (
+    <Layout>
+      <Header>
+        <div
+          style={{
+            display: 'flex',
+            height: '100%',
+            justifyContent: 'space-between',
+          }}
+        >
+          <div style={{ display: 'flex', width: '70%' }}>
+            <div style={logoStyle}>金科环境</div>
+            <Menu
+              theme="dark"
+              mode="horizontal"
+              defaultSelectedKeys={[props.location.pathname]}
+              style={{ lineHeight: '64px', width: '100%' }}
+            >
+              <SubMenu key="/home/work-hours" title="工时管理">
+                <Menu.Item key="/home/work-hours">
+                  <Link to="/home/work-hours">上报工时</Link>
+                </Menu.Item>
+                <Menu.Item key="/home/work-hours-auth">
+                  <Link to="/home/work-hours-auth">审批工时</Link>
+                </Menu.Item>
+              </SubMenu>
+
+              <SubMenu key="/home/approval" title="项目立项">
+                <Menu.Item key="/home/approval/list">
+                  <Link to="/home/approval/list">项目列表</Link>
+                </Menu.Item>
+                <Menu.Item key="/home/approval/auth">
+                  <Link to="/home/approval/auth">审核列表</Link>
+                </Menu.Item>
+              </SubMenu>
+
+              {checkReport(0) && (
+                <SubMenu key="/home/report" title="工时报表">
+                  {/* <Menu.Item key="/home/report/resource">
+                  <Link to="/home/report/resource">资源报表</Link>
+                </Menu.Item> */}
+                  {checkReport(1) && (
+                    <Menu.Item key="/home/report/project">
+                      <Link to="/home/report/project">项目报表</Link>
+                    </Menu.Item>
+                  )}
+                  {checkReport(2) && (
+                    <Menu.Item key="/home/report/department">
+                      <Link to="/home/report/department">部门报表</Link>
+                    </Menu.Item>
+                  )}
+                  {checkReport(3) && (
+                    // <Menu.Item key="/home/report/finance">
+                    //   <Link to="/home/report/finance">财务报表</Link>
+                    // </Menu.Item>
+                    <SubMenu key="/home/report/finance" title="财务报表">
+                      <Menu.Item key="/home/report/finance">
+                        <Link to="/home/report/finance">资源总表</Link>
+                      </Menu.Item>
+                      <Menu.Item key="/home/report/finance/resources">
+                        <Link to="/home/report/finance/resources">
+                          资源中心人日使用汇总表
+                        </Link>
+                      </Menu.Item>
+                      <Menu.Item key="/home/report/finance/project">
+                        <Link to="/home/report/finance/project">
+                          执行项目人日汇总表
+                        </Link>
+                      </Menu.Item>
+                    </SubMenu>
+                  )}
+                </SubMenu>
+              )}
+
+              {/* <Menu.Item key="/home/demo" title="demo">
+                <Link to="/home/demo">demo</Link>
+              </Menu.Item> */}
+
+              {/* {isAdmin && (
+              <Menu.Item key="/home">
+                <Link to="/home">采购清单</Link>
+              </Menu.Item>
+              )} */}
+              {/* <Menu.Item key="/home/flow-list">
+                <Link to="/home/flow-list">流程图</Link>
+              </Menu.Item> */}
+            </Menu>
+          </div>
+          <RightContent />
+        </div>
+      </Header>
+      <Content style={{ padding: '0 50px', minHeight: 'calc(100vh - 64px)' }}>
+        <div style={{ background: '#fff', padding: 24, minHeight: 280 }}>
+          {props.children}
+        </div>
+      </Content>
+      {/* <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer> */}
+    </Layout>
+  );
+}
+export default connect(({ user }) => ({
+  currentUser: user.currentUser,
+  permission: user.currentUser.Permission,
+}))(LayoutDetail);

+ 61 - 0
src/pages/PurchaseList/RightContent.js

@@ -0,0 +1,61 @@
+import React, { PureComponent } from 'react';
+import { FormattedMessage } from 'umi/locale';
+import { LogoutOutlined } from '@ant-design/icons';
+import { Spin, Tag, Menu, Avatar } from 'antd';
+import HeaderDropdown from '@/components/HeaderDropdown';
+import styles from '@/components/GlobalHeader/index.less';
+import { connect } from 'dva';
+import { Logout } from '@/services/PurchaseList';
+import router from 'umi/router';
+
+@connect(({ user }) => ({
+  currentUser: user.currentUser,
+}))
+class GlobalHeaderRight extends PureComponent {
+  async onMenuClick() {
+    // this.props.dispatch({
+    //   type: 'purchaseList/logout',
+    // });
+    await Logout();
+    router.push('/login');
+  }
+  render() {
+    const { currentUser = {} } = this.props;
+    const theme = 'dark';
+    const menu = (
+      <Menu className={styles.menu} selectedKeys={[]} onClick={() => this.onMenuClick()}>
+        <Menu.Item key="logout">
+          <LogoutOutlined />
+          <FormattedMessage id="menu.account.logout" defaultMessage="logout" />
+        </Menu.Item>
+      </Menu>
+    );
+
+    let className = styles.right;
+    if (theme === 'dark') {
+      className = `${styles.right}  ${styles.dark}`;
+    }
+    return (
+      <div className={className}>
+        {currentUser.CName ? (
+          <div className={styles.userbox}>
+            <HeaderDropdown overlay={menu}>
+              <span className={`${styles.action} ${styles.account}`}>
+                <Avatar
+                  size="small"
+                  className={styles.avatar}
+                  src="https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png"
+                  alt="avatar"
+                />
+                <span className={styles.name}>{currentUser.CName}</span>
+              </span>
+            </HeaderDropdown>
+          </div>
+        ) : (
+          <Spin size="small" style={{ marginLeft: 8, marginRight: 8 }} />
+        )}
+      </div>
+    );
+  }
+}
+export default GlobalHeaderRight;

+ 257 - 0
src/pages/PurchaseList/WorkingHours/AddModal.js

@@ -0,0 +1,257 @@
+import React, { useState } from 'react';
+import { message, Form, Modal, Select, DatePicker, TreeSelect } from 'antd';
+import { connect } from 'dva';
+import moment from 'moment';
+
+const { Option } = Select;
+const { MonthPicker } = DatePicker;
+const { TreeNode } = TreeSelect;
+
+function AddModal(props) {
+  const {
+    visible,
+    onOk,
+    onCancel,
+    time,
+    typeList = [],
+    subTypeList = [],
+    dispatch,
+    project = [],
+    loading,
+    depUserTree,
+  } = props;
+  const [form] = Form.useForm();
+  const [type, setType] = useState({});
+  const [subType, setSubType] = useState({});
+  const [showDepSelect, setShowDepSelect] = useState(false);
+  const handleOk = () => {
+    form.validateFields().then((values) => {
+      if (type.type == 0 || subType.type == 0) {
+        if (!values.project) {
+          message.error('请选择项目');
+          return;
+        }
+      } else {
+        values.project = '0';
+      }
+
+      let params = [
+        {
+          type_id: Number(values.subType),
+          comment: '',
+          // 父级id
+          parent_type_id: Number(values.type),
+          data: [
+            {
+              project_id: Number(values.project),
+              workload: 0,
+              day: time,
+            },
+          ],
+        },
+      ];
+      if (type.type == 0 && values.project == '0') {
+        params[0].data[0].pay_dep_id = Number(values.pay_dep_id);
+      }
+      onOk(params);
+
+      //将分类设为空,避免再次打开时有项目选单
+      setType({});
+      setSubType({});
+      setShowDepSelect(false);
+      form.resetFields();
+    });
+  };
+  //同为将分类设为空
+  const handleCancel = () => {
+    setType({});
+    setSubType({});
+    setShowDepSelect(false);
+    form.resetFields();
+    onCancel();
+  };
+  const onChangeType = (value) => {
+    let item = typeList.find((t) => t.id == value);
+    form.setFieldsValue({
+      project: null,
+      subType: null,
+    });
+    setType(item);
+    setSubType({});
+    setShowDepSelect(false);
+    dispatch({
+      type: 'workload/save',
+      payload: { subTypeList: [] },
+    });
+    if (!item.type) {
+      dispatch({
+        type: 'workload/queryProject',
+        payload: { stage: value },
+      });
+    } else {
+      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]);
+          }
+        },
+      });
+    }
+  };
+  const onChangeSubType = (value) => {
+    let item = subTypeList.find((t) => t.id == value);
+    setSubType(item);
+  };
+
+  //售前支持更改项目时与子类选单联动
+  const onChangeProject = (value) => {
+    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]);
+          setShowDepSelect(false);
+        }
+        if (type.id == 2) {
+          if (value == '0') {
+            form.setFieldsValue({ subType: subTypeList[0].id + '' });
+            setSubType(subTypeList[0]);
+            setShowDepSelect(true);
+          } else {
+            form.setFieldsValue({ subType: subTypeList[1].id + '' });
+            setSubType(subTypeList[1]);
+            setShowDepSelect(false);
+          }
+        }
+      },
+    });
+  };
+
+  //分类选单
+  const renderType = () => {
+    return (
+      <Form.Item
+        label="分类"
+        name="type"
+        rules={[{ required: true, message: '请选择分类' }]}
+      >
+        <Select onChange={onChangeType} placeholder="请选择分类">
+          {typeList.map((item) => (
+            <Option key={String(item.id)}>{item.name}</Option>
+          ))}
+        </Select>
+      </Form.Item>
+    );
+  };
+
+  //项目选单
+  const renderProject = () => {
+    return (
+      <Form.Item
+        label="项目"
+        name="project"
+        rules={[{ required: true, message: '请选择项目' }]}
+      >
+        {/* 售前支持特殊判断,增加“无项目”,选择项目时与子类联动 */}
+        <Select
+          showSearch
+          onChange={onChangeProject}
+          placeholder="请选择项目"
+          optionFilterProp="children"
+          filterOption={(input, option) =>
+            option.props.children.toLowerCase().indexOf(input.toLowerCase()) >=
+            0
+          }
+        >
+          {type.id === 2 && <Option key={'0'}>售前支持(10100)</Option>}
+          {project.map((item) => (
+            <Option
+              key={String(item.ID)}
+            >{`${item.Name}(${item.Code})`}</Option>
+          ))}
+        </Select>
+      </Form.Item>
+    );
+  };
+
+  //子类选单
+  const renderSubType = () => {
+    return (
+      <Form.Item
+        label="子类"
+        name="subType"
+        rules={[{ required: true, message: '请选择子类' }]}
+      >
+        {/* 售前支持特殊判断,子类不可选 */}
+        <Select
+          disabled={type.id === 2}
+          onChange={onChangeSubType}
+          placeholder={type.id === 2 ? null : '请选择子类'}
+        >
+          {(subTypeList || []).map((item) => (
+            <Option
+              key={String(item.id)}
+            >{`${item.name}(${item.code})`}</Option>
+          ))}
+        </Select>
+      </Form.Item>
+    );
+  };
+
+  //部门选择
+  const renderDepSelect = () => {
+    return (
+      <Form.Item
+        label="部门"
+        name="pay_dep_id"
+        rules={[{ required: true, message: '请选择部门' }]}
+      >
+        <TreeSelect
+          showSearch
+          allowClear
+          // style={{ width: 240 }}
+          placeholder="请选择部门"
+          multiple={false}
+          treeData={depUserTree}
+          filterTreeNode={(input, option) => {
+            return option.props.title === input;
+          }}
+        />
+      </Form.Item>
+    );
+  };
+
+  return (
+    <Modal
+      title="工时"
+      open={visible}
+      confirmLoading={loading}
+      onCancel={handleCancel}
+      maskClosable={false}
+      onOk={handleOk}
+      destroyOnClose
+    >
+      <Form labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} form={form}>
+        {renderType()}
+        {type.type == 0 && renderProject()}
+        {showDepSelect && renderDepSelect()}
+        {renderSubType()}
+        <Form.Item label="时间">{time}</Form.Item>
+      </Form>
+    </Modal>
+  );
+}
+
+export default connect(({ workload }) => ({
+  project: workload.project,
+  subTypeList: workload.subTypeList,
+}))(AddModal);

+ 229 - 0
src/pages/PurchaseList/WorkingHours/Auth.js

@@ -0,0 +1,229 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+  message,
+  InputNumber,
+  Table,
+  Divider,
+  Modal,
+  Button,
+  Input,
+  Row,
+  Col,
+  Calendar,
+  Spin,
+} from 'antd';
+// import SearchForm from './SearchForm';
+import RejectModal from './RejectModal';
+import AuthWorkList from './AuthWorkList';
+import { connect } from 'dva';
+import styles from './index.less';
+import moment from 'moment';
+
+function List(props) {
+  const { dispatch, loading, list, project, currentUser, allType, user } =
+    props;
+  const [visible, setVisible] = useState(false);
+  const [filter, setFilter] = useState({});
+  const [current, setCurrent] = useState({
+    date: moment(),
+    list: [],
+  });
+
+  const rejectRef = useRef({});
+
+  const onAgree = (records) => {
+    Modal.confirm({
+      title: '提示',
+      content: '是否确认通过审批?',
+      okText: '通过',
+      cancelText: '取消',
+      onOk() {
+        let params = records.map((item) => ({
+          id: item.id,
+          status: 2,
+          desc: '',
+        }));
+        let time = current.date.format('YYYY-MM-DD');
+        dispatch({
+          type: 'workload/authWorkload',
+          payload: params,
+          callback: (list) => {
+            setCurrent({
+              ...current,
+              list: list.filter((item) => item.time == time),
+            });
+          },
+        });
+      },
+    });
+  };
+  const onReject = (data) => {
+    let items = rejectRef.current;
+    let params = items.map((item) => ({
+      id: item.id,
+      ts: item.ts,
+      user_id: item.User.ID,
+      workload: item.workload,
+      status: 3,
+      desc: data.desc || '',
+    }));
+    let time = current.date.format('YYYY-MM-DD');
+    dispatch({
+      type: 'workload/authWorkload',
+      payload: params,
+      callback: (list) => {
+        setVisible(false);
+        setCurrent({
+          ...current,
+          list: list.filter((item) => item.time == time),
+        });
+      },
+    });
+  };
+  const dateCellRender = (value) => {
+    let current = value.format('YYYY-MM-DD');
+    let timeList = list.filter((item) => item.time == current);
+    let total = timeList.reduce((total, item) => {
+      if (item.audit_state != 1) return total;
+      return total + item.workload;
+    }, 0);
+    if (timeList.length == 0 || total == 0) return;
+
+    return <span style={{ color: '#1890ff' }}>{total} 小时</span>;
+  };
+
+  const onChangeDate = (value) => {
+    let time = value.format('YYYY-MM-DD');
+    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 = () => {
+    return current.list.filter((item) => {
+      if (filter.userId && item.User.ID != filter.userId) return false;
+      if (filter.projectId && item.project_id != filter.projectId) return false;
+
+      return true;
+    });
+  };
+
+  useEffect(() => {
+    if (!currentUser.ID) return;
+    // 查询分类以及工时
+    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',
+    });
+    dispatch({
+      type: 'user/fetch',
+    });
+    return () => {
+      // 清空查询数据
+      dispatch({
+        type: 'workload/save',
+        payload: {
+          filter: {},
+          list: [],
+        },
+      });
+    };
+  }, [currentUser.ID]);
+
+  // useEffect(() => {
+  //   onChangeDate(current.date);
+  // }, [list]);
+
+  return (
+    <div>
+      <Spin spinning={loading}>
+        <Row gutter={8}>
+          <Col span={12}>
+            <Calendar
+              value={current.date}
+              cellRender={dateCellRender}
+              onChange={onChangeDate}
+            />
+          </Col>
+          <Col span={12}>
+            <AuthWorkList
+              allType={allType}
+              project={project}
+              list={getList()}
+              onAgree={onAgree}
+              onSearch={setFilter}
+              user={user}
+              onReject={(records) => {
+                setVisible(true);
+                rejectRef.current = records;
+              }}
+            />
+          </Col>
+        </Row>
+      </Spin>
+      <RejectModal
+        visible={visible}
+        onOk={onReject}
+        onCancel={() => setVisible(false)}
+      />
+    </div>
+  );
+}
+
+export default connect(({ workload, user, loading }) => ({
+  list: workload.list,
+  user: user.list,
+  allType: workload.allType,
+  project: workload.project,
+  currentUser: user.currentUser,
+  loading: loading.models.workload,
+}))(List);

+ 232 - 0
src/pages/PurchaseList/WorkingHours/AuthWorkList.js

@@ -0,0 +1,232 @@
+import React, { useState } from 'react';
+import {
+  Form,
+  InputNumber,
+  Input,
+  Button,
+  Popover,
+  Divider,
+  Collapse,
+  List,
+  Table,
+  Select,
+  Row,
+  Col,
+} from 'antd';
+import styles from './index.less';
+
+const { Panel } = Collapse;
+const { Option } = Select;
+
+function AuthWorkList(props) {
+  const { list, onAuth, onSearch, onSave, project = [], allType, onAgree, onReject, user } = props;
+  const [form] = Form.useForm();
+  const [edit, setEdit] = useState(false);
+  const columns = [
+    {
+      title: '分类',
+      dataIndex: 'type_id',
+      render: type_id => allType[type_id]?.name,
+    },
+    {
+      title: '提交人',
+      dataIndex: ['User', 'CName'],
+    },
+    {
+      title: '审核状态',
+      render: record => renderStatus(record),
+    },
+    {
+      title: '工时',
+      width: 110,
+      render: item => {
+        if (item.zIndex === 0) {
+          return '总计 ' + item.children.reduce((total, item) => total + item.workload, 0) + '小时';
+        }
+        return item?.workload + '小时';
+      },
+    },
+    {
+      title: '操作',
+      render: item => {
+        if (item.zIndex === 0) return '';
+        if (item.audit_state != 1) return;
+        return (
+          <>
+            <a onClick={() => onAgree([item], true)}>通过</a>
+            <Divider type="vertical"></Divider>
+            <a style={{ color: '#f5222d' }} onClick={() => onReject([item])}>
+              拒绝
+            </a>
+          </>
+        );
+      },
+    },
+  ];
+  const renderStatus = item => {
+    // auditState  0-未提审 1- 已提交审核,2-审核通过,3-审核拒绝
+    switch (item.audit_state) {
+      case 0:
+        return <span>未提审</span>;
+      case 1:
+        return <span style={{ color: '#1890ff' }}>待审核</span>;
+      case 2:
+        return <span style={{ color: '#a0d911' }}>审核通过</span>;
+      case 3:
+        return (
+          <Popover content={`拒绝原因: ${item.audit_desc}`}>
+            <span style={{ color: '#f5222d' }}>审核拒绝</span>
+          </Popover>
+        );
+      default:
+        return '';
+    }
+  };
+
+  const getList = arr => {
+    let data = {};
+    arr.forEach(item => {
+      let userId = item.User.ID;
+      let id = item.project_id;
+      let name;
+      if (id == '0') {
+        name = '其他';
+      } else {
+        name = item.Project.Name;
+      }
+
+      if (!data[id]) data[id] = { list: [], name, id };
+      data[id].list.push(item);
+    });
+    let allList = Object.values(data);
+    // 将id为0的项 放置到数组最后
+    let index = allList.findIndex(item => item.id == '0');
+    if (index != -1) {
+      var temp = allList.splice(index, 1);
+      allList.push(temp[0]);
+    }
+    allList.forEach(item => {
+      const { list, audit } = getTableInfo(item.list);
+      item.list = list;
+      item.audit = audit;
+    });
+    return allList;
+  };
+
+  const getTableInfo = list => {
+    let data = {},
+      audit = [];
+    list.forEach(item => {
+      let pid = allType[item.type_id]?.parent_id;
+      if (!data[pid]) data[pid] = [];
+      data[pid].push(item);
+      // 判断是否处于待审批状态
+      if (item.audit_state == 1) {
+        // 保存可审批的项
+        audit.push(item);
+      }
+    });
+    let newList = Object.keys(data).map(pid => ({
+      zIndex: 0,
+      id: pid,
+      type_id: allType[pid]?.id,
+      children: data[pid],
+    }));
+    return {
+      list: newList,
+      audit,
+    };
+  };
+
+  const renderSearch = () => {
+    const onHandleSearch = () => {
+      form.validateFields().then(values => {
+        onSearch(values);
+      });
+    };
+    return (
+      <Form style={{ marginBottom: 20 }} form={form}>
+        <Row gutter={12}>
+          <Col span={12}>
+            <Form.Item label="人员" name="userId">
+              <Select
+                allowClear
+                showSearch
+                filterOption={(input, option) =>
+                  option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+              >
+                {user.map(item => (
+                  <Option key={item.ID}>{item.CName}</Option>
+                ))}
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="项目" name="projectId">
+              <Select
+                allowClear
+                showSearch
+                filterOption={(input, option) =>
+                  option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+              >
+                {project.map(item => (
+                  <Option key={item.ID}>{`${item.Name}(${item.Code})`}</Option>
+                ))}
+              </Select>
+            </Form.Item>
+          </Col>
+        </Row>
+        <Row>
+          <Col span={24}>
+            <Button onClick={onHandleSearch} type="primary">
+              搜索
+            </Button>
+          </Col>
+        </Row>
+      </Form>
+    );
+  };
+
+  const dataList = getList(list);
+
+  return (
+    <div style={{ height: 730, overflowY: 'auto', overflowX: 'hidden' }}>
+      {renderSearch()}
+      {dataList.length > 0 && (
+        <Collapse>
+          {dataList.map(item => {
+            // 合计未审核时间
+            let auditTime = item.audit.reduce((total, item) => total + item.workload, 0);
+            let extra;
+            if (auditTime > 0) {
+              extra = (
+                <>
+                  <a onClick={(e) => {
+                    e.stopPropagation();
+                    onAgree(item.audit, true)
+                  }}>全部通过</a>
+                  <Divider type="vertical"></Divider>
+                  <a style={{ color: '#f5222d' }} onClick={() => {
+                    e.stopPropagation();
+                    onReject(item.audit)
+                  }}>
+                    全部拒绝
+                  </a>
+                </>
+              );
+            }
+            return (
+              <Panel header={`${item.name} (待审核:${auditTime}小时)`} key={item.id} extra={extra}>
+                <Table rowKey="id" columns={columns} dataSource={item.list} />
+              </Panel>
+            );
+          })}
+        </Collapse>
+      )}
+    </div>
+  );
+}
+
+export default AuthWorkList;

+ 192 - 0
src/pages/PurchaseList/WorkingHours/CalendarModal.js

@@ -0,0 +1,192 @@
+import React, { useState, useEffect } from 'react';
+import {
+  InputNumber,
+  message,
+  Modal,
+  Calendar,
+  Popover,
+  Form,
+  Input,
+  Row,
+  Col,
+  List,
+  Button,
+} from 'antd';
+import styles from './index.less';
+import moment from 'moment';
+
+function CalendarModal(props) {
+  const {
+    allType,
+    projectList,
+    visible,
+    onOk,
+    onCancel,
+    data,
+    title,
+    footer,
+    loading,
+  } = props;
+  const [form] = Form.useForm();
+  const [type, setType] = useState({});
+  const [edit, setEdit] = useState(false);
+  const [validRange, setValidRange] = useState(null);
+  const [current, setCurrent] = useState({
+    date: null,
+    list: [],
+  });
+  const handleOk = () => {
+    form.validateFields().then((values) => {
+      let params = [];
+      Object.keys(values).forEach((type_id) => {
+        if (type_id == 'comment') return;
+        let item = values[type_id];
+        params.push({
+          type_id: Number(type_id),
+          comment: values.comment,
+          data: data.time.map((obj) => ({
+            id: obj.id,
+            project_id: Number(obj.project_id),
+            workload: item[obj.time],
+            day: obj.time,
+          })),
+        });
+      });
+      onOk(params);
+    });
+  };
+  const itemRender = (item) => {
+    let name;
+    if (item.project_id == '0') {
+      let pid = allType[item.type_id].parent_id;
+      name = allType[pid].name;
+    } else {
+      name = projectList.find((p) => p.ID == item.project_id)?.Name;
+    }
+    // if (edit) {
+    //   return (
+    //     <div>
+    //       {allType[item.type_id].name}
+    //       {form.getFieldDecorator(`${item.type_id}.${item.time}`, {
+    //         initialValue: item?.workload,
+    //       })(<InputNumber min={0} max={8}></InputNumber>)}
+    //     </div>
+    //   );
+    // } else {
+    return (
+      <div>
+        {name}-{allType[item.type_id].name}:{item.workload}小时
+      </div>
+    );
+    // }
+  };
+
+  const dateCellRender = (value) => {
+    let current = value.format('YYYY-MM-DD');
+    let list = data.time.filter((item) => item.time == current);
+    let total = list.reduce((total, item) => total + item.workload, 0);
+    if (list.length == 0) return;
+    // if (!edit) {
+    return (
+      <Popover content={<div>{list.map(itemRender)}</div>}>
+        本日工时: {total}{' '}
+      </Popover>
+    );
+    // } else {
+    //   return <div>{list.map(itemRender)}</div>;
+    // }
+  };
+
+  const onChangeDate = (value) => {
+    let time = value.format('YYYY-MM-DD');
+    setCurrent({
+      date: value,
+      list: data.time.filter((item) => item.time == time),
+    });
+  };
+
+  useEffect(() => {
+    if (data.time.length == 0) return;
+    let month = moment(data.time[0].ts).month();
+    let start = moment().month(month).date(1);
+    let end = moment()
+      .month(month + 1)
+      .date(0);
+    setValidRange([start, end]);
+
+    let time = moment(data.date).format('YYYY-MM-DD');
+    setCurrent({
+      date: moment(data.date),
+      list: data.time.filter((item) => item.time == time),
+    });
+  }, [data.time]);
+
+  return (
+    <Modal
+      width={1400}
+      title={`${data.date}工时`}
+      open={visible}
+      confirmLoading={loading}
+      onCancel={onCancel}
+      maskClosable={false}
+      onOk={handleOk}
+      footer={footer}
+      destroyOnClose
+    >
+      <Row gutter={8}>
+        <Col span={18}>
+          <Calendar
+            validRange={validRange}
+            value={current.date}
+            headerRender={() => {}}
+            cellRender={dateCellRender}
+            onChange={onChangeDate}
+          />
+        </Col>
+        <Col span={6}>
+          <Button>添加</Button>
+          {current.list.map((item) => (
+            <Form.Item
+              label={item.TypeInfo?.name}
+              name={`${item.type_id}.${item.time}`}
+              initialValue={item?.workload}
+            >
+              <InputNumber min={0} max={8} disabled={!edit} />
+            </Form.Item>
+          ))}
+          <Form.Item
+            label="日志"
+            name="comment"
+            initialValue={current.list[0]?.comment}
+          >
+            <Input.TextArea rows={5} disabled={!edit} />
+          </Form.Item>
+
+          <div className={styles.btns}>
+            {edit ? (
+              <>
+                <Button
+                  onClick={() => {
+                    form.resetFields();
+                    setEdit(false);
+                  }}
+                >
+                  取消
+                </Button>
+                <Button onClick={() => setEdit(false)} type="primary">
+                  保存
+                </Button>
+              </>
+            ) : (
+              <Button type="primary" onClick={() => setEdit(true)}>
+                编辑
+              </Button>
+            )}
+          </div>
+        </Col>
+      </Row>
+    </Modal>
+  );
+}
+
+export default CalendarModal;

+ 33 - 0
src/pages/PurchaseList/WorkingHours/RejectModal.js

@@ -0,0 +1,33 @@
+import React, { useState } from 'react';
+import { Input, message, Form, Modal } from 'antd';
+
+function RejectModal(props) {
+  const { visible, onOk, onCancel, loading } = props;
+  const [form] = Form.useForm();
+  const [type, setType] = useState({});
+  const handleOk = () => {
+    form.validateFields().then((values) => {
+      onOk(values);
+    });
+  };
+
+  return (
+    <Modal
+      title="是否确认拒绝"
+      open={visible}
+      confirmLoading={loading}
+      onCancel={onCancel}
+      maskClosable={false}
+      onOk={handleOk}
+      destroyOnClose
+    >
+      <Form labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} form={form}>
+        <Form.Item label="拒绝理由" name="desc">
+          <Input.TextArea />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+}
+
+export default RejectModal;

+ 96 - 0
src/pages/PurchaseList/WorkingHours/SearchForm.js

@@ -0,0 +1,96 @@
+import React from 'react';
+import { Form, Select, DatePicker, Button } from 'antd';
+import moment from 'moment';
+
+const { Option } = Select;
+const { MonthPicker } = DatePicker;
+
+function SearchForm(props) {
+  const { form, onSearch, typeList, project, loading } = props;
+  const formItemLayout = {
+    labelCol: { span: 5 },
+    wrapperCol: { span: 18 },
+  };
+  // const handleSearch = () => {
+  //   form.validateFields((error, values) => {
+  //     if (error) return;
+  //     var weekOfday = moment(values.time).format('E'); //计算指定日期是这周第几天
+  //     var last_monday = moment(values.time)
+  //       .subtract(weekOfday - 1, 'days')
+  //       .format('YYYY-MM-DD'); //周一日期
+  //     var last_sunday = moment(values.time)
+  //       .add(7 - weekOfday, 'days')
+  //       .format('YYYY-MM-DD'); //周日日期
+
+  //     onSearch({
+  //       s_time: last_monday,
+  //       e_time: last_sunday,
+  //       parent_id: values.type,
+  //       project_id: values.project,
+  //     });
+  //   });
+  // };
+
+  const handleChange = data => {
+    var month = moment(data).month(); //计算指定日期是这周第几天
+    var start = moment(data)
+      .date(1)
+      .format('YYYY-MM-DD'); //本月第一天
+    var end = moment(data)
+      .month(month + 1)
+      .date(0)
+      .format('YYYY-MM-DD'); //本月最后一天
+
+    onSearch({
+      s_time: start,
+      e_time: end,
+    });
+  };
+
+  return (
+    <Form layout="inline" {...formItemLayout}>
+      {/* <Form.Item label="分类">
+        {form.getFieldDecorator('type', {
+          rules: [
+            {
+              require: true,
+              message: '请选择分类',
+            },
+          ],
+        })(
+          <Select style={{ width: 200 }}>
+            {typeList.map(item => (
+              <Option key={item.id + ''}>{item.name}</Option>
+            ))}
+          </Select>
+        )}
+      </Form.Item>
+      {form.getFieldValue('type') == 3 && (
+        <Form.Item label="项目">
+          {form.getFieldDecorator('project', {
+            initialValue: String(project[0].ID),
+          })(
+            <Select style={{ width: 200 }}>
+              {project.map(item => (
+                <Option key={String(item.ID)}>{item.Name}</Option>
+              ))}
+            </Select>
+          )}
+        </Form.Item>
+      )} */}
+
+      <Form.Item label="时间">
+        {form.getFieldDecorator('time', {})(
+          <MonthPicker onChange={data => handleChange(data)} placeholder="选择月份" />
+        )}
+      </Form.Item>
+      {/* <Form.Item>
+        <Button type="primary" loading={loading} onClick={handleSearch}>
+          查询
+        </Button>
+      </Form.Item> */}
+    </Form>
+  );
+}
+
+export default Form.create()(SearchForm);

+ 157 - 0
src/pages/PurchaseList/WorkingHours/WorkList.js

@@ -0,0 +1,157 @@
+import React, { useState, useEffect } from 'react';
+import { InputNumber, Popover, Divider, Table, message } from 'antd';
+
+function WorkList(props) {
+  const { list, onAuth, onSave, onDelete, allType, depUserMap } = props;
+  const [expandedRowKeys, setExpandedRowKeys] = useState([]);
+  const [dataSource, setDataSource] = useState([]);
+  let workHour = {};
+  const columns = [
+    {
+      title: '分类',
+      dataIndex: 'type_id',
+      width: '20%',
+      render: type_id => allType[type_id]?.name,
+    },
+    {
+      title: '所属项目/所属部门',
+      width: '30%',
+      render: item => {
+        if (item.zIndex === 0) return '';
+        return item.Project?.Name || depUserMap?.get(item.pay_dep_id)?.Name || '-';
+      },
+    },
+    {
+      title: '审核状态',
+      width: '15%',
+      render: record => renderStatus(record),
+    },
+    {
+      title: '工时',
+      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 + '小时';
+        workHour[`${item.id}`] = item?.workload;
+        return (
+          <InputNumber
+            style={{ width: 60 }}
+            min={0}
+            step={0.5}
+            defaultValue={item?.workload}
+            onChange={value => {
+              workHour[`${item.id}`] = value;
+            }}
+          />
+        );
+      },
+    },
+    {
+      title: '操作',
+      width: '20%',
+      render: item => {
+        if (item.zIndex === 0) return '';
+        if (item.audit_state == 2) return;
+        if (item.audit_state == 1) return <a onClick={() => handleDelete(item)}>删除</a>;
+        return (
+          <>
+            <a
+              onClick={() =>
+                onHandleSave(item, workHour[`${item.id}`])
+              }
+            >
+              保存
+            </a>
+            <Divider type="vertical"></Divider>
+            <a
+              onClick={() =>
+                onAuth(item, workHour[`${item.id}`])
+              }
+            >
+              上报审批
+            </a>
+            <Divider type="vertical"></Divider>
+            <a onClick={() => handleDelete(item)}>删除</a>
+          </>
+        );
+      },
+    },
+  ];
+  const renderStatus = item => {
+    if (item.zIndex === 0) return '';
+    // auditState  0-未提审 1- 已提交审核,2-审核通过,3-审核拒绝
+    switch (item.audit_state) {
+      case 0:
+        return <span>未提审</span>;
+      case 1:
+        return <span style={{ color: '#1890ff' }}>待审核</span>;
+      case 2:
+        return <span style={{ color: '#a0d911' }}>审核通过</span>;
+      case 3:
+        return (
+          <Popover content={`拒绝原因: ${item.audit_desc}`}>
+            <span style={{ color: '#f5222d' }}>审核拒绝</span>
+          </Popover>
+        );
+      default:
+        return '';
+    }
+  };
+
+  const onHandleSave = (item, data) => {
+    let currentWorkload = list
+      .filter(item => item.status == 0)
+      .reduce((total, temp) => {
+        if (temp.id == item.id) return total;
+        return total + temp.workload;
+      }, 0);
+    if (data + currentWorkload > 8) return message.error('每日工时不能超过8小时!');
+    onSave(item, data);
+  };
+
+  const handleDelete = item => {
+    onDelete(item);
+  };
+
+  const getList = () => {
+    let data = {},
+      expandedRowKeys = [];
+    list
+      .filter(item => item.status == 0)
+      .forEach(item => {
+        let pid = allType[item.type_id]?.parent_id;
+        expandedRowKeys.push(pid);
+        if (!data[pid]) data[pid] = [];
+        data[pid].push(item);
+      });
+    let dataSource = Object.keys(data).map(pid => ({
+      zIndex: 0,
+      id: allType[pid]?.id,
+      type_id: allType[pid]?.id,
+      children: data[pid],
+    }));
+
+    setDataSource(dataSource);
+    setExpandedRowKeys([...new Set(expandedRowKeys)]);
+  };
+
+  useEffect(() => {
+    getList();
+  }, [list]);
+
+  return (
+    <div>
+      <Table
+        expandedRowKeys={expandedRowKeys}
+        rowKey="id"
+        columns={columns}
+        onExpandedRowsChange={keys => setExpandedRowKeys(keys)}
+        dataSource={dataSource}
+      />
+    </div>
+  );
+}
+
+export default WorkList;

+ 450 - 0
src/pages/PurchaseList/WorkingHours/index.js

@@ -0,0 +1,450 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+  Modal,
+  Button,
+  Calendar,
+  Popover,
+  Spin,
+  Row,
+  Col,
+  message,
+} from 'antd';
+import AddModal from './AddModal';
+import WorkList from './WorkList';
+import { connect } from 'dva';
+import dayjs from 'dayjs';
+import { useRequest, useModel } from '@umijs/max';
+
+function List(props) {
+  const {
+    typeList,
+    dispatch,
+    loading,
+    dataList,
+    projectList,
+    // currentUser,
+    allType,
+    depUserTree,
+    depUserMap,
+  } = props;
+  const {
+    initialState: { user },
+  } = useModel('@@initialState');
+  const [visible, setVisible] = useState(false);
+  const [current, setCurrent] = useState({
+    date: dayjs(),
+    list: [],
+  });
+
+  const onAuth = (item, workload) => {
+    if (workload == 0) return message.error('请上报有效工时');
+    Modal.confirm({
+      title: '提示',
+      content: '是否上报审批?',
+      okText: '确认',
+      cancelText: '取消',
+      onOk() {
+        console.log(item);
+        let params = [
+          {
+            type_id: Number(item.type_id),
+            // 父级id
+            parent_type_id: allType[item.type_id].parent_id,
+            data: [
+              {
+                id: item.id,
+                project_id: item.project_id,
+                workload: workload,
+                day: item.time,
+                pay_dep_id: item.pay_dep_id,
+              },
+            ],
+          },
+        ];
+        dispatch({
+          type: 'workload/addAuthWorkHours',
+          payload: params,
+          callback: (list) => {
+            let time = current.date.format('YYYY-MM-DD');
+            setCurrent({
+              ...current,
+              list: list.filter((item) => item.time == time),
+            });
+          },
+        });
+      },
+    });
+  };
+
+  const MultiAuth = () => {
+    Modal.confirm({
+      title: '提示',
+      content: '是否上报全部审批?',
+      okText: '确认',
+      cancelText: '取消',
+      onOk() {
+        let params = [];
+        debugger;
+        for (let i = 0; i < dataList.length; i++) {
+          const element = dataList[i];
+          if (element.audit_state != 0) continue;
+          var arr = params.find((arr) => arr.type_id == element.type_id);
+          if (arr) {
+            console.log(arr);
+            arr.parent_type_id = allType[element.type_id].parent_id;
+            arr.data.push({
+              id: element.id,
+              project_id: element.project_id,
+              workload: element.workload,
+              day: element.time,
+              pay_dep_id: element.pay_dep_id,
+            });
+          } else {
+            let data = [];
+            data.push({
+              id: element.id,
+              project_id: element.project_id,
+              workload: element.workload,
+              day: element.time,
+              pay_dep_id: element.pay_dep_id,
+            });
+            params.push({
+              type_id: Number(element.type_id),
+              // 父级id
+              parent_type_id: allType[element.type_id].parent_id,
+              data: data,
+            });
+          }
+        }
+        console.log(params);
+        dispatch({
+          type: 'workload/addAuthWorkHours',
+          payload: params,
+        });
+      },
+    });
+  };
+
+  const currentDayAuth = () => {
+    var list = current.list;
+    Modal.confirm({
+      title: '提示',
+      content: '是否上报本日审批?',
+      okText: '确认',
+      cancelText: '取消',
+      onOk() {
+        let params = [];
+        for (let i = 0; i < list.length; i++) {
+          const element = list[i];
+          if (element.audit_state != 0) continue;
+          var arr = params.find((arr) => arr.type_id == element.type_id);
+          if (arr) {
+            console.log(arr);
+            arr.parent_type_id = allType[element.type_id].parent_id;
+            arr.data.push({
+              id: element.id,
+              project_id: element.project_id,
+              workload: element.workload,
+              day: element.time,
+              pay_dep_id: element.pay_dep_id,
+            });
+          } else {
+            let data = [];
+            data.push({
+              id: element.id,
+              project_id: element.project_id,
+              workload: element.workload,
+              day: element.time,
+              pay_dep_id: element.pay_dep_id,
+            });
+            params.push({
+              type_id: Number(element.type_id),
+              // 父级id
+              parent_type_id: allType[element.type_id].parent_id,
+              data: data,
+            });
+          }
+        }
+        console.log(params);
+        dispatch({
+          type: 'workload/addAuthWorkHours',
+          payload: params,
+        });
+      },
+    });
+  };
+
+  const onSave = (item, workload) => {
+    let params = [
+      {
+        type_id: Number(item.type_id),
+        comment: '',
+        // 父级id
+        parent_type_id: allType[item.type_id].parent_id,
+        data: [
+          {
+            id: item.id,
+            project_id: Number(item.project_id),
+            workload: workload,
+            day: item.time,
+            pay_dep_id: item.pay_dep_id,
+          },
+        ],
+      },
+    ];
+
+    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),
+        });
+      },
+    });
+  };
+
+  const onDelete = (item) => {
+    Modal.confirm({
+      title: '提示',
+      content: '确认删除工时?',
+      okText: '确认',
+      cancelText: '取消',
+      onOk() {
+        dispatch({
+          type: 'workload/deleteWorkHour',
+          payload: { id: item.id },
+          callback: (list) => {
+            let time = current.date.format('YYYY-MM-DD');
+            setCurrent({
+              ...current,
+              list: list.filter((item) => item.time == time),
+            });
+          },
+        });
+      },
+    });
+  };
+
+  const onAddWork = (data) => {
+    dispatch({
+      type: 'workload/addWorkHours',
+      payload: data,
+      callback: (list) => {
+        setVisible(false);
+        let time = current.date.format('YYYY-MM-DD');
+        setCurrent({
+          ...current,
+          list: list.filter((item) => item.time == time),
+        });
+      },
+    });
+  };
+
+  const dateCellRender = (value, info) => {
+    let current = value.format('YYYY-MM-DD');
+    // console.log(value);
+    let list = dataList.filter(
+      (item) => item.time == current && item.status == 0,
+    );
+    // let total = list.reduce((total, item) => total + item.workload, 0);
+    let waitTotal = 0,
+      successTotal = 0;
+    list.forEach((item) => {
+      if (item.audit_state == 2) {
+        successTotal += item.workload;
+      } else {
+        waitTotal += item.workload;
+      }
+    });
+    if (list.length == 0) return info.origin;
+    let content = (
+      <div>
+        {list.map((item) => (
+          <div key={item.type_id}>
+            {getName(item)}-{item.TypeInfo?.name}:{item.workload}小时
+          </div>
+        ))}
+      </div>
+    );
+    return (
+      <Popover content={content}>
+        {successTotal !== 0 && <div>已审批: {successTotal}</div>}
+        {waitTotal !== 0 && (
+          <div style={{ color: '#e43d33' }}>待处理: {waitTotal}</div>
+        )}
+      </Popover>
+    );
+  };
+
+  const onChangeDate = (value) => {
+    console.log(value);
+    let time = value.format('YYYY-MM-DD');
+    if (current.date.format('YYYY-MM') != value.format('YYYY-MM')) {
+      const s_date = value.format('YYYY-MM') + '-01';
+      const e_date = dayjs(s_date)
+        .add(1, 'month')
+        .add(-1, 'days')
+        .format('YYYY-MM-DD');
+      dispatch({
+        type: 'workload/queryWorkHours',
+        payload: {
+          pageSize: 9999,
+          user_id: user.ID,
+          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: dataList.filter((item) => item.time == time),
+      });
+    }
+  };
+
+  const getName = (item) => {
+    let name;
+    if (item.project_id == '0') {
+      let pid = allType[item.type_id]?.parent_id;
+      if (!pid) return '';
+      name = allType[pid].name;
+    } else {
+      name = item.Project.Name;
+    }
+    return name;
+  };
+
+  useEffect(() => {
+    if (!user.ID) return;
+    // 查询分类以及工时
+    dispatch({
+      type: 'workload/queryWorkType',
+      callback: () => {
+        const s_date = current.date.format('YYYY-MM') + '-01';
+        const e_date = dayjs(s_date)
+          .add(1, 'month')
+          .add(-1, 'days')
+          .format('YYYY-MM-DD');
+        dispatch({
+          type: 'workload/queryWorkHours',
+          payload: {
+            pageSize: 9999,
+            user_id: user.ID,
+            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',
+    // });
+    return () => {
+      // 清空查询数据
+      dispatch({
+        type: 'workload/save',
+        payload: {
+          filter: {},
+          list: [],
+        },
+      });
+    };
+  }, [user.ID]);
+  console.log(current.date);
+
+  // useEffect(() => {
+  //   onChangeDate(current.date);
+  // }, [dataList]);
+  useEffect(() => {
+    dispatch({
+      type: 'workload/fetchDepV2',
+    });
+  }, []);
+  return (
+    <div>
+      <Spin spinning={loading}>
+        <Row gutter={8}>
+          <Col span={10}>
+            <Calendar
+              // value={current.date}
+              // cellRender={dateCellRender}
+              onChange={onChangeDate}
+            />
+          </Col>
+          <Col span={14}>
+            <div>
+              <Button
+                type="primary"
+                style={{ marginBottom: 20 }}
+                onClick={() => setVisible(true)}
+              >
+                新增
+              </Button>
+              <Button
+                type="primary"
+                style={{ marginBottom: 20, marginLeft: 20 }}
+                onClick={() => MultiAuth()}
+              >
+                上报所有工时
+              </Button>
+              {current.list.length > 0 && (
+                <Button
+                  type="primary"
+                  style={{ marginBottom: 20, marginLeft: 20, float: 'right' }}
+                  onClick={() => currentDayAuth()}
+                >
+                  上报今日工时
+                </Button>
+              )}
+            </div>
+
+            <WorkList
+              allType={allType}
+              list={current.list}
+              onAuth={onAuth}
+              onSave={onSave}
+              onDelete={onDelete}
+              depUserMap={depUserMap}
+            />
+          </Col>
+        </Row>
+      </Spin>
+
+      <AddModal
+        typeList={typeList}
+        visible={visible}
+        onOk={onAddWork}
+        time={current.date?.format('YYYY-MM-DD')}
+        onCancel={() => setVisible(false)}
+        depUserTree={depUserTree}
+      />
+    </div>
+  );
+}
+
+export default connect(({ workload, user, loading }) => ({
+  dataList: workload.dataList,
+  typeList: workload.typeList,
+  allType: workload.allType,
+  // currentUser: user.currentUser,
+  loading: loading.models.workload,
+  depUserTree: workload.depUserTree,
+  depUserMap: workload.depUserMap,
+}))(List);

+ 9 - 0
src/pages/PurchaseList/WorkingHours/index.less

@@ -0,0 +1,9 @@
+.btns {
+  margin: 20px 0;
+  display: flex;
+  :global {
+    .ant-btn {
+      margin-right: 10px;
+    }
+  }
+}

+ 307 - 0
src/pages/PurchaseList/WorkingHours/models/workingHours.js

@@ -0,0 +1,307 @@
+import {
+  queryWorkType,
+  queryWorkHours,
+  queryAuthWorkHours,
+  addWorkHours,
+  addAuthWorkHours,
+  authWorkload,
+  queryProject,
+  deleteWorkHour,
+} from '@/services/workHours.js';
+import { queryRole } from '@/services/SysAdmin';
+import { queryDepV2 } from '@/services/approval';
+import { message } from 'antd';
+import moment from 'moment';
+
+function getDepUserTree(data, map) {
+  data.title = `${data.Name}`;
+  data.key = `${data.ID}`;
+  data.value = `${data.ID}`;
+  map.set(data.ID, data);
+  if (!data.children) data.children = new Array();
+
+  if (data.children) {
+    data.children.forEach((item) => {
+      getDepUserTree(item, map);
+    });
+  }
+  return data;
+}
+
+export default {
+  namespace: 'workload',
+  state: {
+    list: [],
+    dataList: [],
+    typeList: [],
+    allType: {},
+    subTypeList: [],
+    project: [],
+    projectList: { project0: [], project1: [] },
+    filter: {},
+    depUserTree: [],
+  },
+
+  effects: {
+    *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;
+        if (item.parent_id == 0) {
+          typeList.push(item);
+        }
+      }
+      yield put({
+        type: 'save',
+        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, callback }, { call, put, select }) {
+      const workload = yield select((s) => s.workload);
+      const { typeList, filter, allType } = workload;
+      // 合并新旧过滤条件
+      const newFilter = {
+        ...filter,
+        ...payload,
+      };
+      let { data } = yield call(queryAuthWorkHours, newFilter);
+      // let tempList = {};
+      // // 根据userid与date将所有数据分类
+      data.list.forEach((item) => {
+        // let date = moment(item.ts).format('YYYY-MM');
+        // let userId = item.User.ID;
+        // if (!tempList[userId]) tempList[userId] = {};
+        // if (!tempList[userId][date]) tempList[userId][date] = [];
+        // tempList[userId][date].push(item);
+        item.time = moment(item.ts).format('YYYY-MM-DD');
+      });
+      // let workList = [];
+      // // 重组数据结构
+      // Object.keys(tempList).forEach(userId => {
+      //   Object.keys(tempList[userId]).forEach(date => {
+      //     let item = tempList[userId][date][0];
+      //     workList.push({
+      //       date,
+      //       id: item.type_id,
+      //       auditState: item.audit_state,
+      //       comment: item.comment,
+      //       user: item.User,
+      //       time: tempList[userId][date],
+      //     });
+      //   });
+      // });
+
+      callback && callback(data.list);
+
+      yield put({
+        type: 'save',
+        payload: { list: data.list, filter: newFilter },
+      });
+    },
+    *queryWorkHours({ payload, callback }, { call, put, select }) {
+      const workload = yield select((s) => s.workload);
+      const { typeList, filter, allType } = workload;
+      // 合并新旧过滤条件
+      const newFilter = {
+        ...filter,
+        ...payload,
+      };
+      // 有userId则查询本人工时列表,没有则查询全部待审核工时列表
+      let { data } = yield call(queryWorkHours, newFilter);
+      // let tempList = {};
+      data.list.forEach((item) => {
+        // let date = moment(item.ts).format('YYYY-MM');
+        // if (!tempList[date]) tempList[date] = [];
+        // tempList[date].push(item);
+        item.time = moment(item.ts).format('YYYY-MM-DD');
+      });
+      // let workList = [];
+      // Object.keys(tempList).forEach(date => {
+      //   let item = tempList[date][0];
+      //   workList.push({
+      //     date,
+      //     id: item.type_id,
+      //     auditState: item.audit_state,
+      //     comment: item.comment,
+      //     time: tempList[date],
+      //   });
+      // });
+      // let list = getData(data.list, allType);
+      // workList = workList.concat(list);
+      // console.log(workList);
+
+      callback && callback(data.list);
+
+      yield put({
+        type: 'save',
+        payload: { dataList: data.list, filter: newFilter },
+      });
+    },
+    // 创建工时
+    *addWorkHours({ payload, callback }, { call, put }) {
+      const res = yield call(addWorkHours, payload);
+      if (res) {
+        // callback && callback();
+        message.success('保存成功');
+        // 创建成功
+        yield put({
+          type: 'queryWorkHours',
+          payload: {},
+          callback,
+        });
+      }
+    },
+    // 提交审核工时
+    *addAuthWorkHours({ payload, callback }, { call, put }) {
+      const res = yield call(addAuthWorkHours, payload);
+      if (res) {
+        message.success('上报成功');
+        // 上报成功
+        yield put({
+          type: 'queryWorkHours',
+          payload: {},
+          callback,
+        });
+      }
+    },
+    // 审核上报的工时
+    *authWorkload({ payload, callback }, { call, put }) {
+      const res = yield call(authWorkload, payload);
+      if (res) {
+        message.success('操作成功');
+        yield put({
+          type: 'queryAuthWorkHours',
+          payload: {},
+          callback,
+        });
+      }
+    },
+    *queryProject({ payload = {}, callback }, { call, put }) {
+      const res = yield call(queryProject, payload);
+      if (res) {
+        yield put({
+          type: 'save',
+          payload: {
+            project: res.data.list,
+          },
+        });
+        callback && callback();
+      }
+    },
+    *deleteWorkHour({ payload, callback }, { call, put }) {
+      const res = yield call(deleteWorkHour, payload);
+      if (!res) return;
+      yield put({
+        type: 'queryWorkHours',
+        payload: {},
+        callback,
+      });
+    },
+    *fetchDepV2({ payload, callback }, { call, put }) {
+      const response = yield call(queryDepV2, { pageSize: 999999 });
+      if (response) {
+        const depUserMap = new Map();
+        const depUserTree = response.data.list.map((item) => {
+          return getDepUserTree(item, depUserMap);
+        });
+        yield put({
+          type: 'save',
+          payload: { depUserTree, depUserMap },
+        });
+      }
+    },
+  },
+
+  reducers: {
+    save(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+  },
+};
+
+function getData(list, allType) {
+  var data = {},
+    resData = [];
+  // 将数据根据项目以及类别进行分类
+  list.forEach((item) => {
+    let { project_id, type_id } = item;
+    // 初始化数据
+    if (!data[project_id]) {
+      data[project_id] = {};
+    }
+    if (!data[project_id][type_id]) {
+      data[project_id][type_id] = [];
+    }
+
+    data[project_id][type_id].push(item);
+  });
+  Object.keys(data).forEach((projectId) => {
+    let projectData = {
+      id: projectId,
+      type: 1,
+      key: `${1}-${projectId}`,
+      auditState: null,
+      time: [], // 项目不需要工时
+      children: [],
+    };
+    Object.keys(data[projectId]).forEach((typeId) => {
+      let time = data[projectId][typeId].sort(
+        (a, b) => new Date(a.ts) - new Date(b.ts),
+      );
+      let typeData = {
+        id: typeId,
+        type: 2,
+        key: time[0].id,
+        auditState: time[0].audit_state,
+        time,
+      };
+      projectData.children.push(typeData);
+    });
+    resData.push(projectData);
+  });
+  // projectId = '0' 表示无项目,须根据一级分类展示
+  let index = resData.findIndex((item) => item.id == '0');
+  if (index != -1) {
+    let data = {};
+    let subTypeArr = resData[index].children;
+    // 清空
+    resData.splice(index, 1);
+    subTypeArr.forEach((item) => {
+      let parentId = allType[item.id].parent_id;
+      if (!data[parentId]) data[parentId] = [];
+
+      data[parentId].push(item);
+    });
+    Object.keys(data).forEach((typeId) => {
+      resData.push({
+        id: typeId, //大分类 ID
+        type: 0,
+        key: `${0}-${typeId}`,
+        auditState: null,
+        time: [], // 大分类不需要工时
+        children: data[typeId],
+      });
+    });
+  }
+
+  return resData;
+}

+ 112 - 0
src/pages/PurchaseList/WorkloadIndex.js

@@ -0,0 +1,112 @@
+import React, { useEffect } from 'react';
+import { Layout, Menu } from 'antd';
+import { connect } from 'dva';
+import RightContent from './RightContent';
+import Link from 'umi/link';
+
+const { Header, Content, Footer } = Layout;
+const { SubMenu } = Menu;
+
+// 布局
+function LayoutDetail(props) {
+  const { currentUser, permission } = props;
+  const isAdmin = currentUser.UserName == 'admin';
+  var logoStyle = {
+    color: 'white',
+    fontWeight: 600,
+    fontSize: 20,
+    verticalAlign: 'middle',
+    marginRight: 60,
+    width: 120,
+  };
+  useEffect(() => {
+    // 查询用户信息
+    props.dispatch({
+      type: 'user/fetchCurrent',
+    });
+    localStorage.type = 'workload'
+  }, []);
+  const checkReport = state => {
+    if (isAdmin) return true;
+    const manager = currentUser.is_leader || currentUser.is_opt_mgr || currentUser.is_wty_mgr;
+    switch (state) {
+      case 0:
+        return currentUser.is_accountant || manager || permission['func-01-point-works-report'];
+      case 1:
+        return manager || permission['func-01-point-works-report-p'];
+      case 2:
+        return permission['func-01-point-works-report-d'];
+      case 3:
+        return currentUser.is_accountant || permission['func-01-point-works-report-p-s'];
+      case 4:
+        return permission['func-01-point-bom-flow'];
+    }
+  };
+  return (
+    <Layout>
+      <Header>
+        <div style={{ display: 'flex', height: '100%', justifyContent: 'space-between' }}>
+          <div style={{ display: 'flex', width: '70%' }}>
+            <div style={logoStyle}>金科环境</div>
+            <Menu
+              theme="dark"
+              mode="horizontal"
+              defaultSelectedKeys={[props.location.pathname]}
+              style={{ lineHeight: '64px', width: '100%' }}
+            >
+              <SubMenu key="/home/work-hours" title="工时管理">
+                <Menu.Item key="/home/work-hours">
+                  <Link to="/home/work-hours">上报工时</Link>
+                </Menu.Item>
+                <Menu.Item key="/home/work-hours-auth">
+                  <Link to="/home/work-hours-auth">审批工时</Link>
+                </Menu.Item>
+              </SubMenu>
+
+              <SubMenu key="/home/approval" title="项目立项">
+                <Menu.Item key="/home/approval/list">
+                  <Link to="/home/approval/list">项目列表</Link>
+                </Menu.Item>
+                <Menu.Item key="/home/approval/auth">
+                  <Link to="/home/approval/auth">审核列表</Link>
+                </Menu.Item>
+              </SubMenu>
+
+              {checkReport(0) && (
+                <SubMenu key="/home/report" title="工时报表">
+                  {/* <Menu.Item key="/home/report/resource">
+                  <Link to="/home/report/resource">资源报表</Link>
+                </Menu.Item> */}
+                  {checkReport(1) && (
+                    <Menu.Item key="/home/report/project">
+                      <Link to="/home/report/project">项目报表</Link>
+                    </Menu.Item>
+                  )}
+                  {checkReport(2) && (
+                    <Menu.Item key="/home/report/department">
+                      <Link to="/home/report/department">部门报表</Link>
+                    </Menu.Item>
+                  )}
+                  {checkReport(3) && (
+                    <Menu.Item key="/home/report/finance">
+                      <Link to="/home/report/finance">财务报表</Link>
+                    </Menu.Item>
+                  )}
+                </SubMenu>
+              )}
+            </Menu>
+          </div>
+          <RightContent />
+        </div>
+      </Header>
+      <Content style={{ padding: '0 50px', minHeight: 'calc(100vh - 64px)' }}>
+        <div style={{ background: '#fff', padding: 24, minHeight: 280 }}>{props.children}</div>
+      </Content>
+      {/* <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer> */}
+    </Layout>
+  );
+}
+export default connect(({ user }) => ({
+  currentUser: user.currentUser,
+  permission: user.currentUser.Permission,
+}))(LayoutDetail);

+ 135 - 0
src/services/workHours.js

@@ -0,0 +1,135 @@
+import { request } from 'umi';
+import { stringify } from 'qs';
+
+export async function queryWorkType(params) {
+  return request(`/api/v2/workload/type/info?${stringify(params)}`);
+}
+/**
+ * 
+    + pageSize:每页记录数
+    + currentPage:当前页
+    + project_id:项目id
+    + user_id:用户id
+    + dep_id:部门id
+    + type_id:工作分项id
+    + audit_status:审核状态
+    + s_time:开始时间
+    + e_time:结束时间 
+ */
+// 查询本人工时列表
+export async function queryWorkHours(params) {
+  return request(`/api/v2/workload/record?${stringify(params)}`);
+}
+// 审核员查询待审核工时列表
+export async function queryAuthWorkHours(params) {
+  return request(`/api/v2/workload/list/auth?${stringify(params)}`);
+}
+export async function queryProject(params) {
+  return request(`/api/v2/workload/project?${stringify(params)}`);
+}
+/** id,status,desc */
+export async function authWorkload(data) {
+  return request(`/api/v2/workload/auths`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
+// export async function addWorkHours(data) {
+//   return request(`/api/v2/workload/record`,{
+//     method: 'POST',
+//     body: data
+//   });
+// }
+
+export async function addWorkHours(data) {
+  return request(`/api/v2/workload/record`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
+export async function addAuthWorkHours(data) {
+  return request(`/api/v2/workload/records`, {
+    method: 'POST',
+    body: data,
+  });
+}
+
+export async function queryResReport(params) {
+  return request(`/api/v2/workload/user/rpt?${stringify(params)}`);
+}
+
+export async function queryUserReport(params) {
+  return request(`/api/v2/workload/dep/hr/rpt?${stringify(params)}`);
+}
+
+export async function queryUserProjectReport(params) {
+  return request(
+    `/api/v2/workload/project/month/rpt/user?${stringify(params)}`,
+  );
+}
+
+export async function queryDepReport(params) {
+  return request(`/api/v2/workload/dep/sum/hr/rpt?${stringify(params)}`);
+}
+
+export async function queryProjectReport(params) {
+  return request(`/api/v2/workload/rpt/projects?${stringify(params)}`);
+  // return request(`/api/v2/workload/project/month/rpt?${stringify(params)}`);
+  // return request(`/api/v2/workload/project/rpt?${stringify(params)}`);
+}
+
+export async function queryProjectReportDetail(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)}`,
+  );
+}
+
+//项目报表-新
+export async function queryProjectReportNew(params) {
+  return request(`/api/v2/workload/rpt/projects_news?${stringify(params)}`);
+}
+
+//删除工时
+export async function deleteWorkHour(params) {
+  return request(`/api/v2/workload/unaudit/delete/${params.id}`, {
+    method: 'DELETE',
+  });
+}
+
+//财务报表资源中心
+//params: s_time, e_time按月份
+export async function queryFinanceResReport(params) {
+  return request(`/api/v2/workload/finance/resource/rpt?${stringify(params)}`);
+}
+
+//财务报表执行项目
+//params: s_time, e_time按月份
+export async function queryFinanceProjReport(params) {
+  return request(`/api/v2/workload/finance/exe/rpt?${stringify(params)}`);
+}
+
+//财务报表执行项目详情
+//params: s_time, e_time按月份, project_id
+export async function queryFinanceProjDetail(params) {
+  return request(
+    `/api/v2/workload/finance/exe/rpt/detail?${stringify(params)}`,
+  );
+}

+ 54 - 0
src/utils/authority.js

@@ -0,0 +1,54 @@
+// use localStorage to store the authority info, which might be sent from server in actual project.
+export function getAuthority() {
+  const authorityString = localStorage.getItem('antd-pro-authority') ? localStorage.getItem('antd-pro-authority'):  "[]";
+  let authority;
+  try {
+    authority = JSON.parse(authorityString);
+  } catch (e) {
+    authority = [];
+  }
+  return authority;
+}
+
+export function setAuthority(authority) {
+  return localStorage.setItem('antd-pro-authority', JSON.stringify(Object.getOwnPropertyNames(authority)));
+}
+
+export function setCurrentUser(user) {
+  return localStorage.setItem('currentUser', JSON.stringify(user));
+}
+
+export function getCurrentUser() {
+  let currentUser;
+  try {
+    currentUser = JSON.parse(localStorage.getItem('currentUser'));
+  } catch (e) {
+    currentUser = {};
+  }
+  return currentUser;
+}
+
+export function hasAuthority(func){
+  const currentUser = getCurrentUser();
+  if(currentUser && currentUser.IsSuper)
+    return true;
+  const funcs = getAuthority()
+  for(var i=0; i<funcs.length;i++){
+    if(funcs[i] === func)
+      return true;
+  }
+  return false;
+}
+
+export function hasfunctionAuthority(authority,item){
+  if(authority === undefined || authority === null) return ;
+  const individual = Object.keys(authority).filter(other => other === item)[0];
+  if (individual) return true;
+}
+
+export function hasfunctionArrayAuthority(authority,item){
+  if(authority === undefined || authority === null) return ;
+  const individual = authority.filter((each) => each === item);
+  if (Number(individual) !== 0) return true;
+}
+