Browse Source

fix:人员管理 解决冲突

zhaojing 1 year ago
parent
commit
736215ab87
32 changed files with 1016 additions and 118 deletions
  1. 2 1
      .vscode/settings.json
  2. 5 0
      config/routes.ts
  3. 2 2
      package.json
  4. 4 0
      src/assets/images/page-home/environment.svg
  5. 10 0
      src/assets/images/page-home/equipment.svg
  6. BIN
      src/assets/images/page-home/qiyeLogo.png
  7. 11 0
      src/assets/images/page-home/runtime.svg
  8. 10 0
      src/assets/images/page-home/set.svg
  9. 3 0
      src/assets/images/page-home/staff.svg
  10. 3 0
      src/assets/images/page-person-import/close-icon.svg
  11. 3 0
      src/assets/images/page-person-import/excel-icon.svg
  12. 18 0
      src/assets/images/page-person-import/import-icon.svg
  13. 9 0
      src/config/sagacare/sagacare_image.js
  14. 43 4
      src/hooks/useMenuList.ts
  15. 0 1
      src/pages/Home/index.less
  16. 3 0
      src/pages/Information/index.jsx
  17. 113 0
      src/pages/Member/components/StartImport/ImportError/index.jsx
  18. 155 0
      src/pages/Member/components/StartImport/ImportError/index.less
  19. 32 0
      src/pages/Member/components/StartImport/ImportSuccess/index.jsx
  20. 60 0
      src/pages/Member/components/StartImport/ImportSuccess/index.less
  21. 6 0
      src/pages/Member/components/StartImport/Uploader/icon-excel-error.svg
  22. 3 0
      src/pages/Member/components/StartImport/Uploader/icon-excel.svg
  23. 171 0
      src/pages/Member/components/StartImport/Uploader/index.jsx
  24. 65 0
      src/pages/Member/components/StartImport/Uploader/index.less
  25. 32 0
      src/pages/Member/components/StartImport/Uploader/service.js
  26. 80 0
      src/pages/Member/components/StartImport/index.jsx
  27. 95 0
      src/pages/Member/components/StartImport/index.less
  28. 0 53
      src/pages/Member/components/importModal/index.jsx
  29. 0 36
      src/pages/Member/components/importModal/index.less
  30. 39 2
      src/pages/Member/index.tsx
  31. 11 0
      src/sagacare_components/navMenu/index.less
  32. 28 19
      src/sagacare_components/navMenu/index.tsx

+ 2 - 1
.vscode/settings.json

@@ -1,5 +1,6 @@
 {
   "editor.formatOnSave": true,
   "prettier.requireConfig": true,
-  "editor.defaultFormatter": "esbenp.prettier-vscode"
+  "editor.defaultFormatter": "esbenp.prettier-vscode",
+  "screencastMode.fontSize": 24
 }

+ 5 - 0
config/routes.ts

@@ -27,6 +27,11 @@
         component: './Member',
       },
       {
+        path: '/information',
+        name: 'information',
+        component: './Information',
+      },
+      {
         path: '/member/invalid',
         name: 'member.invalid',
         component: './Member/invalid',

+ 2 - 2
package.json

@@ -55,12 +55,12 @@
     "@ant-design/pro-table": "^2.61.0",
     "@umijs/route-utils": "^2.0.0",
     "ahooks": "^3.1.9",
-    "antd": "^4.17.0",
-    "qrcodejs2": "^0.0.2",
+    "antd": "^4.24.14",
     "classnames": "^2.3.0",
     "lodash": "^4.17.0",
     "moment": "^2.29.0",
     "omit.js": "^2.0.2",
+    "qrcodejs2": "^0.0.2",
     "rc-menu": "^9.1.0",
     "rc-util": "^5.16.0",
     "react": "^17.0.0",

File diff suppressed because it is too large
+ 4 - 0
src/assets/images/page-home/environment.svg


File diff suppressed because it is too large
+ 10 - 0
src/assets/images/page-home/equipment.svg


BIN
src/assets/images/page-home/qiyeLogo.png


+ 11 - 0
src/assets/images/page-home/runtime.svg

@@ -0,0 +1,11 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_6537_41398)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 2.625C9.50862 2.625 2.625 9.50862 2.625 18C2.625 26.4914 9.50862 33.375 18 33.375C26.4914 33.375 33.375 26.4914 33.375 18C33.375 9.50862 26.4914 2.625 18 2.625ZM0.375 18C0.375 8.26598 8.26598 0.375 18 0.375C27.734 0.375 35.625 8.26598 35.625 18C35.625 27.734 27.734 35.625 18 35.625C8.26598 35.625 0.375 27.734 0.375 18Z" fill="#4D5262"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18 7.875C18.6213 7.875 19.125 8.37868 19.125 9V17.3824L26.854 22.3009C27.3782 22.6345 27.5327 23.3298 27.1991 23.854C26.8655 24.3782 26.1702 24.5327 25.646 24.1991L17.396 18.9491C17.0715 18.7426 16.875 18.3846 16.875 18V9C16.875 8.37868 17.3787 7.875 18 7.875Z" fill="#4D5262"/>
+</g>
+<defs>
+<clipPath id="clip0_6537_41398">
+<rect width="36" height="36" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 10 - 0
src/assets/images/page-home/set.svg


File diff suppressed because it is too large
+ 3 - 0
src/assets/images/page-home/staff.svg


File diff suppressed because it is too large
+ 3 - 0
src/assets/images/page-person-import/close-icon.svg


File diff suppressed because it is too large
+ 3 - 0
src/assets/images/page-person-import/excel-icon.svg


+ 18 - 0
src/assets/images/page-person-import/import-icon.svg

@@ -0,0 +1,18 @@
+<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="44" cy="44" r="44" fill="#FFE823"/>
+<mask id="mask0_6604_40090" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="88" height="88">
+<circle cx="44" cy="44" r="44" fill="#F0DA21"/>
+</mask>
+<g mask="url(#mask0_6604_40090)">
+<rect x="-3.5" y="27.5" width="33" height="33" rx="4.5" stroke="white" stroke-width="3"/>
+<line x1="-3" y1="36.5" x2="29" y2="36.5" stroke="white" stroke-width="3"/>
+<line x1="-3" y1="44.5" x2="29" y2="44.5" stroke="white" stroke-width="3"/>
+<line x1="-3" y1="52.5" x2="29" y2="52.5" stroke="white" stroke-width="3"/>
+<line x1="18.5" y1="29" x2="18.5" y2="60" stroke="white" stroke-width="3"/>
+<line x1="7.5" y1="28" x2="7.5" y2="59" stroke="white" stroke-width="3"/>
+<rect x="58.5" y="20.5" width="33" height="47" rx="4.5" stroke="white" stroke-width="3"/>
+<path d="M55 28V28C53.3431 28 52 29.3431 52 31V57C52 58.6569 53.3431 60 55 60V60" stroke="white" stroke-width="3"/>
+<path d="M28 44.4502H42.5833M42.5833 44.4502L36.3333 38.2002M42.5833 44.4502L36.3333 50.7002" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M71.5 39.5C71.5 41.981 73.519 44 76 44C78.481 44 80.5 41.981 80.5 39.5C80.5 37.019 78.481 35 76 35C73.519 35 71.5 37.019 71.5 39.5ZM84 54H85V53C85 49.141 81.859 46 78 46H74C70.14 46 67 49.141 67 53V54H84Z" fill="white"/>
+</g>
+</svg>

+ 9 - 0
src/config/sagacare/sagacare_image.js

@@ -4,6 +4,15 @@ import envir_co2 from '@/assets/images/page-enviroment/co2.svg';
 import envir_pm25 from '@/assets/images/page-enviroment/pm25.svg';
 import envir_jiaquan from '@/assets/images/page-enviroment/jiaquan.svg';
 import envir_tvoc from '@/assets/images/page-enviroment/tvoc.svg';
+
+export const homeImgMap = {
+    staff: require('@/assets/images/page-home/staff.svg'),
+    environment: require('@/assets/images/page-home/environment.svg'),
+    equipment: require('@/assets/images/page-home/equipment.svg'),
+    runtime: require('@/assets/images/page-home/runtime.svg'),
+    set: require('@/assets/images/page-home/set.svg'),
+    qiyeLogo: require('@/assets/images/page-home/qiyeLogo.png'),
+}
 export const equipImageMap = {
   //   temperMapTop: require('@/assets/images/page-enviroment/temperMapTop.png'),
   //   co2MapTop: require('@/assets/images/page-enviroment/co2MapTop.png'),

+ 43 - 4
src/hooks/useMenuList.ts

@@ -1,4 +1,5 @@
 import { useState, useEffect } from 'react';
+import { homeImgMap } from '@/config/sagacare/sagacare_image.js';
 
 export type menutype = {
   name: string;
@@ -9,11 +10,49 @@ export default function () {
   //进入时赋值
   useEffect(() => {
     setMenuList([
-      // { name: '环境信息', id: 'environment' },
-      { name: '设备管理', id: 'equipment' },
-      { name: '人员管理', id: 'member' },
-      // { name: '运行时间', id: 'runtime' },
+      {
+        title: '',
+        menus: [
+          {
+            id: 'member',
+            title: '成员',
+            icon: homeImgMap.staff,
+          },
+          {
+            id: 'information',
+            title: '企业信息',
+            icon: homeImgMap.staff,
+          },
+        ],
+      },
+
+      {
+        title: '运营服务',
+        menus: [
+          {
+            id: 'environment',
+            title: '环境信息',
+            icon: homeImgMap.environment,
+          },
+          {
+            id: 'equipment',
+            title: '设备管理',
+            icon: homeImgMap.equipment,
+          },
+          {
+            id: 'runtime',
+            title: '运行时间',
+            icon: homeImgMap.runtime,
+          },
+        ],
+      },
     ]);
+    // setMenuList([
+    //   // { name: '环境信息', id: 'environment' },
+    //   { name: '设备管理', id: 'equipment' },
+    //   { name: '人员管理', id: 'member' },
+    //   // { name: '运行时间', id: 'runtime' },
+    // ]);
   }, []);
 
   return { menuList };

+ 0 - 1
src/pages/Home/index.less

@@ -6,7 +6,6 @@
   }
   .right {
     width: 400px;
-    height: 300px;
     margin-top: -60px;
     overflow-y: auto;
     background: #fff;

+ 3 - 0
src/pages/Information/index.jsx

@@ -0,0 +1,3 @@
+export default () => {
+  return <div>企业信息页面</div>;
+};

+ 113 - 0
src/pages/Member/components/StartImport/ImportError/index.jsx

@@ -0,0 +1,113 @@
+import React, { useState } from 'react';
+import { Modal, Button } from 'antd';
+import { useModel } from 'umi';
+import iconClose from '@/assets/images/page-person-import/close-icon.svg';
+import styles from './index.less';
+import { exportUsersError } from '../Uploader/service';
+const importTitleIcon = require('@/assets/images/page-person-import/import-icon.svg');
+
+export default ({ info, onClose }) => {
+  const { initialState } = useModel('@@initialState');
+
+  const downloadExcel = () => {
+    let param = {
+      companyId: initialState?.currentUser?.companyId,
+      key: info.key,
+    };
+    exportUsersError(param)
+      .then((resData) => {
+        console.log('resData==', resData);
+        let headers = resData.response && resData.response.headers;
+        console.log(headers.get('content-disposition'));
+        let disposition = headers.get('content-disposition');
+        let filename = disposition
+          ? decodeURI(disposition.split(';')[1].split('filename=')[1])
+          : '错误报告.xls';
+        const link = document.createElement('a');
+        let data = resData.data;
+        let blob = new Blob([resData], {
+          // type: 'application/xlsx',
+          type: 'application/msexcel',
+        });
+        link.style.display = 'none';
+        link.href = URL.createObjectURL(blob);
+        console.log(filename);
+        link.download = filename;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        window.URL.revokeObjectURL(link);
+      })
+      .catch(() => {});
+  };
+
+  return (
+    <Modal
+      open
+      width={760}
+      footer={[]}
+      closable={false}
+      className={styles.importError}
+      onClose={onClose}
+    >
+      <div className={styles.header}>
+        <div
+          className={styles.close}
+          onClick={() => {
+            onClose();
+          }}
+        >
+          <img src={iconClose} alt="" />
+        </div>
+
+        <div className={styles.title}>
+          <img src={importTitleIcon} alt="" />
+        </div>
+      </div>
+      <div className={styles.listW}>
+        <div className={styles.info}>{info.message}</div>
+        <div className={styles.list}>
+          <div className={styles.row}>
+            <div className={styles.col1}>姓名</div>
+            <div className={styles.col2}>类型</div>
+            <div className={styles.col3}>手机号</div>
+            <div className={styles.col4}>职务</div>
+            <div className={styles.col5}>原因</div>
+          </div>
+          {(info.data || []).map((item, itemIndex) => {
+            return (
+              // eslint-disable-next-line react/no-array-index-key
+              <div className={styles.row} key={itemIndex}>
+                <div className={styles.col1}>{item.name}</div>
+                <div className={styles.col2}>{item.manageUserType}</div>
+                <div className={styles.col3}>{item.phone}</div>
+                <div className={styles.col4}>{item.job}</div>
+                <div className={styles.col5}>
+                  {(Array.from(item.remark) || []).map((remark) => {
+                    <div>{remark}</div>;
+                  })}
+                </div>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+
+      <div className={styles.footer}>
+        <div className={styles.footerLeft}>
+          您可以
+          <span onClick={downloadExcel}>下载错误报告,</span>
+          修改后重新导入
+        </div>
+        <div
+          className={styles.footerRight}
+          onClick={() => {
+            onClose(2);
+          }}
+        >
+          重新导入
+        </div>
+      </div>
+    </Modal>
+  );
+};

+ 155 - 0
src/pages/Member/components/StartImport/ImportError/index.less

@@ -0,0 +1,155 @@
+.importError {
+  :global {
+    .ant-modal-body {
+      background: #fff;
+      border-radius: 20px;
+    }
+  }
+  .header {
+    position: relative;
+    z-index: 1;
+    height: 120px;
+    background: #f3f3f3;
+    border-radius: 20px 20px 0 0;
+    .title {
+      position: absolute;
+      top: 52px;
+      // top: -40px;
+      left: 50%;
+      text-align: center;
+      transform: translateX(-50%);
+      img {
+        display: block;
+        width: 88px;
+        height: 88px;
+        margin: 0 auto;
+      }
+      span {
+        display: block;
+        padding-top: 10px;
+        font-weight: 600;
+        font-size: 18px;
+        font-family: PingFang SC;
+        line-height: 25px;
+        text-align: center;
+      }
+    }
+  }
+  .close {
+    position: absolute;
+    top: 15px;
+    right: 15px;
+    z-index: 2;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 24px;
+    height: 24px;
+    border-radius: 100%;
+    cursor: pointer;
+  }
+
+  .footer {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-top: 22px;
+    padding: 10px;
+    color: #656872;
+    font-size: 16px;
+    background: #fff;
+    // border: 1px solid #c4c4c466;
+    .footerLeft {
+      padding-left: 66px;
+      font-weight: 400;
+      font-size: 16px;
+      font-family: PingFang SC;
+      line-height: 34px;
+      text-align: left;
+      span {
+        padding: 0 4px;
+        color: #000;
+        border-bottom: 1px solid #000;
+      }
+    }
+    .footerRight {
+      width: 116px;
+      height: 50px;
+      // padding: 17px 30px 17px 30px;
+      color: #000;
+      line-height: 50px;
+      text-align: center;
+      background: #ffe823;
+      border-radius: 30px;
+    }
+  }
+
+  .listW {
+    max-height: 550px;
+    padding: 65px;
+    padding-top: 40px;
+    overflow: hidden;
+    color: #656872;
+    background: #fff;
+    border: 1px solid #f3f3f3;
+    border-radius: 4px;
+  }
+
+  .info {
+    padding: 0 20px;
+    font-size: 16px;
+    line-height: 90px;
+    background: #f3f3f3;
+    border-bottom: 1px solid #f3f3f3;
+  }
+
+  .list {
+    background: #fff;
+    border: 1px solid #f3f3f3;
+    .row {
+      display: flex;
+      line-height: 24px;
+      border-bottom: 1px solid #f3f3f3;
+      &:first-child {
+        color: #656872;
+        font-weight: 600;
+      }
+      &:last-child {
+        border-bottom: none;
+      }
+      & > div {
+        min-width: 0;
+        padding: 10px;
+        line-height: 24px;
+        word-break: break-all;
+        border-right: 1px solid #f3f3f3;
+        &:last-child {
+          border-right: none;
+        }
+      }
+      .col1 {
+        width: 150px;
+        color: #000 !important;
+        font-weight: 600;
+      }
+      .col2 {
+        width: 100px;
+      }
+      .col3 {
+        width: 130px;
+      }
+      .col4 {
+        width: 100px;
+      }
+      .col5 {
+        flex: 1;
+        div {
+          display: -webkit-box;
+          overflow: hidden;
+          -webkit-line-clamp: 5;
+          -webkit-box-orient: vertical;
+        }
+      }
+    }
+  }
+}

+ 32 - 0
src/pages/Member/components/StartImport/ImportSuccess/index.jsx

@@ -0,0 +1,32 @@
+import React from 'react';
+import { Modal, Button } from 'antd';
+// import Icon from '@/tenants-ui/Icon';
+import iconClose from '@/assets/images/page-person-import/close-icon.svg';
+import iconImport from '@/assets/images/page-person-import/import-icon.svg';
+import styles from './index.less';
+
+export default ({ info, onClose }) => {
+  return (
+    <div className={styles.c}>
+      <Modal
+        open
+        width={320}
+        maskClosable={false}
+        closable={false}
+        footer={null}
+        getContainer={false}
+      >
+        <div className={styles.header}>
+          <img src={iconImport} />
+          <img src={iconClose} className={styles.close} onClick={onClose}></img>
+        </div>
+        <div className={styles.info}>{info.message}</div>
+        <div className={styles.f}>
+          <Button type="primary" size="large" onClick={onClose}>
+            完成
+          </Button>
+        </div>
+      </Modal>
+    </div>
+  );
+};

+ 60 - 0
src/pages/Member/components/StartImport/ImportSuccess/index.less

@@ -0,0 +1,60 @@
+.c {
+  :global {
+    .ant-modal-body {
+      background: #fff;
+      border-radius: 20px;
+    }
+  }
+}
+
+.header {
+  position: relative;
+  height: 90px;
+  // margin: -24px -24px 0;
+  padding-top: 40px;
+  text-align: center;
+  background: rgba(196, 196, 196, 0.2);
+  border-radius: 20px 20px 0 0;
+  img {
+    width: 88px;
+    height: 88px;
+    background: #f0da21;
+    border-radius: 100%;
+  }
+  .close {
+    position: absolute;
+    top: 15px;
+    right: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 24px;
+    height: 24px;
+    color: #c4c4c4;
+    font-size: 20px;
+    background: transparent;
+    border-radius: 100%;
+    cursor: pointer;
+  }
+}
+
+.info {
+  // margin: 50px 0 70px;
+  margin-top: 80px;
+  margin-bottom: 70px;
+  font-size: 16px;
+  text-align: center;
+}
+
+.f {
+  padding-bottom: 40px;
+  text-align: center;
+  button {
+    font-weight: 500;
+    font-size: 14px;
+    font-family: PingFang SC;
+    line-height: 16px;
+    text-align: center;
+    background: #ffe823;
+  }
+}

File diff suppressed because it is too large
+ 6 - 0
src/pages/Member/components/StartImport/Uploader/icon-excel-error.svg


File diff suppressed because it is too large
+ 3 - 0
src/pages/Member/components/StartImport/Uploader/icon-excel.svg


+ 171 - 0
src/pages/Member/components/StartImport/Uploader/index.jsx

@@ -0,0 +1,171 @@
+import React, { useState, useCallback, useRef } from 'react';
+import cx from 'classnames';
+import { Button, Checkbox } from 'antd';
+import { importUsers } from './service';
+import { useModel, history } from 'umi';
+import iconExcel from './icon-excel.svg';
+import iconErrorExcel from './icon-excel-error.svg';
+import styles from './index.less';
+
+const isExcel = (type) => {
+  return (
+    [
+      'text/csv',
+      '.csv',
+      'application/vnd.ms-excel',
+      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+    ].indexOf(type) >= 0
+  );
+};
+
+export default ({ onUploadDone }) => {
+  const [draggingOver, setDraggingOver] = useState(false);
+
+  const [fileInfo, setFileInfo] = useState({});
+  const [autoSendActiveMsg, setAutoSendActiveMsg] = useState(true);
+  const [errorInfo, setErrorInfo] = useState('');
+  const [uploading, setUploading] = useState(false);
+  const fileInputRef = useRef();
+  /**
+   * 0: 开始选择
+   * 1: 文件选择之后,且符合规范
+   * 2: 文件选择之后,不符合规范
+   * 3: 文件上传失败
+   */
+  const [uploadState, setUploadState] = useState(0);
+  const { initialState } = useModel('@@initialState');
+  const handleFileSelected = useCallback((e) => {
+    const file = e.target.files[0];
+
+    setFileInfo({
+      file,
+      name: file.name,
+      size: `${Math.ceil(file.size / 1024)}K`,
+    });
+    if (!isExcel(file.type)) {
+      setErrorInfo('文件格式不正确');
+      setUploadState(2);
+    } else if (file.size > 1024 * 1024 * 10) {
+      setErrorInfo('文件大小不能超过 10MB');
+      setUploadState(2);
+    } else {
+      setUploadState(1);
+    }
+    e.target.value = '';
+  }, []);
+
+  const beginUpload = useCallback(() => {
+    setUploading(true);
+    let params = {
+      companyId: initialState?.currentUser?.companyId,
+    };
+    importUsers(fileInfo.file, params)
+      .then((res) => {
+        setUploading(false);
+        onUploadDone(res);
+      })
+      .catch(() => {
+        // 文件上传失败
+        setUploading(false);
+        // setErrorInfo('导入失败');
+        setUploadState(1);
+      });
+  }, [fileInfo, autoSendActiveMsg, onUploadDone]);
+
+  return (
+    <div
+      className={cx(styles.c, {
+        [styles.dragging]: draggingOver,
+      })}
+      onDragEnter={(e) => {
+        e.preventDefault();
+        e.stopPropagation();
+        setDraggingOver(true);
+      }}
+      onDragOver={(e) => {
+        setDraggingOver(true);
+        e.preventDefault();
+        e.stopPropagation();
+        return false;
+      }}
+      onDragLeave={(e) => {
+        e.preventDefault();
+        e.stopPropagation();
+        setDraggingOver(false);
+      }}
+      onDrop={(e) => {
+        e.preventDefault();
+        e.stopPropagation();
+        setDraggingOver(false);
+        handleFileSelected({
+          target: {
+            files: e.dataTransfer.files,
+          },
+        });
+      }}
+    >
+      <input type="file" hidden ref={fileInputRef} onChange={(e) => handleFileSelected(e)} />
+      <div className={styles.title}>
+        <span>2&nbsp;/</span>
+        上传填写完后的表格文件
+      </div>
+
+      {uploadState === 0 && (
+        <div className={styles.fileTrigger}>
+          <img src={iconExcel} />
+          <div>
+            <span onClick={() => fileInputRef.current.click()}>点击选择文件</span>
+            ,或将文件拖拽到此处
+          </div>
+        </div>
+      )}
+
+      {uploadState === 1 && (
+        <div className={styles.fileInfo}>
+          <img src={iconExcel} />
+          <div>
+            {fileInfo.name} ({fileInfo.size})&nbsp;&nbsp;
+            <span onClick={() => fileInputRef.current.click()}>重新选择文件</span>
+          </div>
+          <section>
+            <Button
+              type="primary"
+              size="large"
+              style={{ margin: '30px 0 20px' }}
+              loading={uploading}
+              onClick={() => {
+                beginUpload();
+              }}
+            >
+              开始上传
+            </Button>
+            <br />
+            {/* <Checkbox checked={autoSendActiveMsg} onChange={e => setAutoSendActiveMsg(e.target.checked)}>导入后自动发送短信提醒其激活账号</Checkbox> */}
+          </section>
+        </div>
+      )}
+
+      {uploadState === 2 && (
+        <div className={styles.fileError}>
+          <img src={iconErrorExcel} />
+          <div>
+            {errorInfo},<span onClick={() => fileInputRef.current.click()}>重新选择文件</span>
+          </div>
+        </div>
+      )}
+
+      {uploadState === 3 && (
+        <div className={styles.fileError}>
+          <img src={iconErrorExcel} />
+          <div>
+            {fileInfo.name} ({fileInfo.size})
+          </div>
+          <div>
+            文件上传失败,请<span onClick={beginUpload}>再次上传</span>或
+            <span onClick={() => fileInputRef.current.click()}>重新选择文件</span>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};

+ 65 - 0
src/pages/Member/components/StartImport/Uploader/index.less

@@ -0,0 +1,65 @@
+.c {
+  padding-bottom: 30px;
+  border: 1px solid #f3f3f3;
+  border-top: none;
+  border-radius: 0 0 4px 4px;
+  &.dragging {
+    background-color: #f3f3f3;
+  }
+}
+.title {
+  display: flex;
+  align-items: center;
+  padding: 30px 90px 20px 25px;
+  color: #656872;
+  span {
+    margin-right: 15px;
+    color: #f0da21;
+    font-size: 24px;
+  }
+}
+
+.fileTrigger {
+  padding: 20px 0;
+  text-align: center;
+  div {
+    margin-top: 20px;
+    color: #656872;
+    span {
+      color: #000;
+      text-decoration: underline;
+      cursor: pointer;
+    }
+  }
+}
+
+.fileInfo {
+  padding: 20px 0;
+  text-align: center;
+  & > div {
+    margin-top: 20px;
+    color: #656872;
+    span {
+      color: #000;
+      text-decoration: underline;
+      cursor: pointer;
+    }
+  }
+  button {
+    background: #ffe823;
+  }
+}
+
+.fileError {
+  padding: 20px 0;
+  text-align: center;
+  div {
+    margin-top: 20px;
+    color: #656872;
+    span {
+      color: #000;
+      text-decoration: underline;
+      cursor: pointer;
+    }
+  }
+}

+ 32 - 0
src/pages/Member/components/StartImport/Uploader/service.js

@@ -0,0 +1,32 @@
+import { request } from 'umi';
+
+export const importUsers = (file, param) => {
+  // console.log(params);
+  const data = new FormData();
+  data.append('file', file);
+  let param1 = JSON.stringify(param);
+  data.append('param', param1);
+  return request('/xiaojing/server/common/userUpload', {
+    data,
+    method: 'post',
+    parseResponse: true,
+  });
+};
+
+// 导出失败
+export const exportUsersError = (param) => {
+  // // console.log(params);
+  const data = new FormData();
+  // data.append('file', file);
+  let param1 = JSON.stringify(param);
+  data.append('param', param1);
+  return request('/xiaojing/server/common/userErroDownload', {
+    data,
+    method: 'post',
+    responseType: 'blob',
+    headers: {
+      contentType: 'application/json;charset=UTF-8',
+    },
+    getResponse: true,
+  });
+};

+ 80 - 0
src/pages/Member/components/StartImport/index.jsx

@@ -0,0 +1,80 @@
+import React, { useState, useEffect } from 'react';
+import {
+  Modal,
+  Form,
+  Select,
+  Radio,
+  Checkbox,
+  Input,
+  Row,
+  Col,
+  Button,
+  InputNumber,
+  Tree,
+  Tooltip,
+  Dropdown,
+  message,
+} from 'antd';
+import Icon from '@/tenants-ui/SgIcon';
+import styles from './index.less';
+import cx from 'classnames';
+import { useModel, history } from 'umi';
+import { EllipsisOutlined } from '@ant-design/icons';
+import { ExclamationCircleOutlined } from '@ant-design/icons';
+import AreaTree from '../areaTree';
+import iconClose from '@/assets/images/page-person-import/close-icon.svg';
+
+import moment from 'moment';
+const importTitleIcon = require('@/assets/images/page-person-import/import-icon.svg');
+import { queryUserInfo, saveUserInfo, deleteUser } from '@/services/sagacare_service/member';
+import Uploader from './Uploader';
+
+const ImportModal = ({ onClose, onUploadDone }) => {
+  const { initialState } = useModel('@@initialState');
+
+  return (
+    <>
+      <Modal open closable={false} footer={null} width={760}>
+        <div className={styles.modalmain}>
+          <div className={styles.header}>
+            <div
+              className={styles.close}
+              onClick={() => {
+                onClose();
+              }}
+            >
+              <img src={iconClose} alt="" />
+            </div>
+
+            <div className={styles.title}>
+              <img src={importTitleIcon} alt="" />
+              <span>批量导入成员</span>
+            </div>
+          </div>
+          <div className={styles.content}>
+            <div className={styles.comTitle}>
+              <span>1/</span>
+              <span>下载表格模版,将需要导入的成员信息填入模版</span>
+              <a
+                href={`http://wechat.sagacloud.cn/saga/image/sagaManagerControl/成员导入模板.xls`}
+                target="downloadFile"
+                download
+                rel="noreferrer"
+              >
+                下载表格模版
+              </a>
+            </div>
+            <Uploader
+              onUploadDone={(info) => {
+                console.log(info);
+                onUploadDone(info);
+              }}
+            />
+          </div>
+        </div>
+      </Modal>
+    </>
+  );
+};
+
+export default ImportModal;

+ 95 - 0
src/pages/Member/components/StartImport/index.less

@@ -0,0 +1,95 @@
+.modalmain {
+  .header {
+    position: relative;
+    z-index: 1;
+    height: 120px;
+    background: #f3f3f3;
+    border-radius: 20px 20px 0 0;
+    .title {
+      position: absolute;
+      top: 52px;
+      // top: -40px;
+      left: 50%;
+      text-align: center;
+      transform: translateX(-50%);
+      img {
+        display: block;
+        width: 88px;
+        height: 88px;
+        margin: 0 auto;
+      }
+      span {
+        display: block;
+        padding-top: 10px;
+        font-weight: 600;
+        font-size: 18px;
+        font-family: PingFang SC;
+        line-height: 25px;
+        text-align: center;
+      }
+    }
+  }
+  .close {
+    position: absolute;
+    top: 15px;
+    right: 15px;
+    z-index: 2;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 24px;
+    height: 24px;
+    border-radius: 100%;
+    cursor: pointer;
+  }
+  .content {
+    position: relative;
+    padding: 65px;
+    padding-top: 95px;
+    background-color: #fff;
+    .comTitle {
+      padding: 47px 25px;
+      border: 1px solid #f3f3f3;
+      span {
+        &:nth-child(1) {
+          display: inline-block;
+          color: #f0da21;
+          font-weight: 500;
+          font-size: 24px;
+          font-family: Montserrat;
+          line-height: 34px;
+          text-align: left;
+          vertical-align: middle;
+        }
+        &:nth-child(2) {
+          display: inline-block;
+          padding-left: 11px;
+          color: #656872;
+          font-weight: 400;
+          font-size: 14px;
+          font-family: PingFang SC;
+          line-height: 34px;
+          text-align: left;
+          vertical-align: middle;
+        }
+      }
+      a {
+        width: 152px;
+        margin-left: 67px;
+        padding: 17px 30px;
+        color: #4d5262;
+        font-size: 14px;
+        // height: 50px;
+        line-height: 16px;
+        background: #fff;
+        border: 1px solid #c4c4c4;
+        border-radius: 30px;
+      }
+      &:nth-child(2) {
+        border-top: none;
+      }
+    }
+    .contentTemp {
+    }
+  }
+}

+ 0 - 53
src/pages/Member/components/importModal/index.jsx

@@ -1,53 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import {
-  Modal,
-  Form,
-  Select,
-  Radio,
-  Checkbox,
-  Input,
-  Row,
-  Col,
-  Button,
-  InputNumber,
-  Tree,
-  Tooltip,
-  Dropdown,
-  message,
-} from 'antd';
-import Icon from '@/tenants-ui/SgIcon';
-import styles from './index.less';
-import cx from 'classnames';
-import { useModel, history } from 'umi';
-import { EllipsisOutlined } from '@ant-design/icons';
-import { ExclamationCircleOutlined } from '@ant-design/icons';
-import AreaTree from '../areaTree';
-
-import moment from 'moment';
-import { queryUserInfo, saveUserInfo, deleteUser } from '@/services/sagacare_service/member';
-
-const ImportModal = ({ onClose }) => {
-  const { initialState } = useModel('@@initialState');
-
-  return (
-    <>
-      <Modal open closable={false} footer={null} width={760}>
-        <div className={styles.modalmain}>
-          <div className={styles.header}>
-            <div
-              className={styles.close}
-              onClick={() => {
-                onClose();
-              }}
-            >
-              <Icon type="close" />
-            </div>
-          </div>
-          <div className={styles.content}></div>
-        </div>
-      </Modal>
-    </>
-  );
-};
-
-export default ImportModal;

+ 0 - 36
src/pages/Member/components/importModal/index.less

@@ -1,36 +0,0 @@
-.modalmain {
-  .header {
-    position: relative;
-    z-index: 1;
-    height: 120px;
-    background: #f3f3f3;
-    border-radius: 20px 20px 0 0;
-    .title {
-      color: #000;
-      font-weight: 400;
-      font-size: 22px;
-    }
-  }
-  .close {
-    position: absolute;
-    top: 15px;
-    right: 15px;
-    z-index: 2;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    width: 36px;
-    height: 36px;
-    color: #c4c4c4;
-    font-size: 20px;
-    border-radius: 100%;
-    cursor: pointer;
-    transition: background-color 0.1s cubic-bezier(0.075, 0.82, 0.165, 1);
-    &:hover {
-      background-color: #fff;
-    }
-  }
-  .content {
-    padding: 50px 60px 20px;
-  }
-}

+ 39 - 2
src/pages/Member/index.tsx

@@ -8,9 +8,11 @@ import Search from '@/sagacare_components/Search';
 import Table from '@/sagacare_components/Table';
 import Empty from './components/Empty';
 import PersonModal from './components/personModal';
-import ImportModal from './components/importModal';
 import { EllipsisOutlined, InfoCircleOutlined } from '@ant-design/icons';
 import Icon from '@/tenants-ui/SgIcon';
+import ImportModal from './components/StartImport';
+import ImportError from './components/StartImport/ImportError';
+import ImportSuccess from './components/StartImport/ImportSuccess';
 
 import {
   queryUserList,
@@ -192,6 +194,7 @@ const Member: React.FC = () => {
   const showImport = () => {
     setImportVisible(true);
   };
+  const [uploadResult, setUploadResult] = useState(null);
   useEffect(() => {
     var paramObj = {
       criteria: {
@@ -386,7 +389,41 @@ const Member: React.FC = () => {
           refreshList={refreshList}
         ></PersonModal>
       )}
-      {importVisible && <ImportModal onClose={onImClose}></ImportModal>}
+      {importVisible && (
+        <ImportModal
+          onUploadDone={(res) => {
+            setImportVisible(false);
+            setUploadResult(res);
+            console.log(res);
+            console.log(uploadResult);
+          }}
+          onClose={onImClose}
+        ></ImportModal>
+      )}
+
+      {uploadResult && uploadResult.result == 'fail' && (
+        <ImportError
+          info={uploadResult}
+          onClose={(type) => {
+            console.log(type);
+            if (type == 2) {
+              setImportVisible(true);
+              setUploadResult(null);
+            } else {
+              setUploadResult(null);
+            }
+          }}
+        />
+      )}
+
+      {uploadResult && uploadResult.result == 'success' && (
+        <ImportSuccess
+          info={uploadResult}
+          onClose={() => {
+            setUploadResult(null);
+          }}
+        />
+      )}
     </div>
   );
 };

+ 11 - 0
src/sagacare_components/navMenu/index.less

@@ -1,6 +1,7 @@
 .drawerDiv {
   //   width: 380px;
   //   height: 340px;
+  position: relative;
   padding: 0 24px;
   background-color: #fff;
   border-radius: 10px;
@@ -10,6 +11,16 @@
     height: 120px;
     font-size: 22px;
   }
+  .setOut {
+    position: absolute;
+    top: 42px;
+    right: 36px;
+  }
+  .groupTitle {
+    margin: 0 0 13px 10px;
+    color: #c4c4c4;
+    font-size: 16px;
+  }
   .menuList {
     padding-bottom: 60px;
 

+ 28 - 19
src/sagacare_components/navMenu/index.tsx

@@ -1,9 +1,10 @@
 import React, { useEffect } from 'react';
-import { Row, Col } from 'antd';
+import { Row, Col, Image } from 'antd';
 import styles from './index.less';
 import Icon from '@/tenants-ui/SgIcon';
 import useMenuList, { menutype } from '@/hooks/useMenuList';
 import { history, useModel, useLocation } from 'umi';
+import { homeImgMap } from '@/config/sagacare/sagacare_image.js';
 
 const NavMenu: React.FC = () => {
   const logo = require('@/assets/images/logo.png');
@@ -24,25 +25,33 @@ const NavMenu: React.FC = () => {
       <div className={styles.title}>
         <img src={logo} style={{ height: 70 }}></img>
       </div>
-      <div className={styles.menuList}>
-        <Row gutter={[20, 20]}>
-          {(menuList || []).map((item, index) => {
-            return (
-              <Col span={8} key={'col' + index}>
-                <div
-                  className={styles.menuItem}
-                  onClick={() => {
-                    menuClick(item);
-                  }}
-                >
-                  <Icon className="" type={item.id} style={{ fontSize: 26 }}></Icon>
-                  <div className={styles.name}>{item.name}</div>
-                </div>
-              </Col>
-            );
-          })}
-        </Row>
+      <div className={styles.setOut}>
+        <img src={homeImgMap.set} />
       </div>
+      {menuList?.map((group, fidx) => {
+        return (
+          <div className={styles.menuList} key={fidx}>
+            {group.title && <div className={styles.groupTitle}>{group.title}</div>}
+            <Row gutter={[20, 20]}>
+              {(group.menus || []).map((item, index) => {
+                return (
+                  <Col span={8} key={'col' + index}>
+                    <div
+                      className={styles.menuItem}
+                      onClick={() => {
+                        menuClick(item);
+                      }}
+                    >
+                      <Image width={36} preview={false} src={item.icon} />
+                      <div className={styles.name}>{item.title}</div>
+                    </div>
+                  </Col>
+                );
+              })}
+            </Row>
+          </div>
+        );
+      })}
     </div>
   );
 };