ソースを参照

Merge branch 'master' of http://39.106.8.246:3003/sagacloud/sagacloud-manager-control

anxiaoxia 3 年 前
コミット
ff1a9f7f89
41 ファイル変更1915 行追加698 行削除
  1. 613 0
      mock/environment.ts
  2. 0 0
      src/assets/css/antd-reset.less
  3. 0 0
      src/assets/css/antd-tenants.less
  4. 77 0
      src/assets/css/map.less
  5. 0 16
      src/components/PageHeader/index.jsx
  6. 14 14
      src/components/PageHeader/index.less
  7. 33 0
      src/components/PageHeader/index.tsx
  8. 0 0
      src/components/map/index.less
  9. 28 0
      src/components/map/index.tsx
  10. 33 0
      src/components/navMenu/index.less
  11. 43 0
      src/components/navMenu/index.tsx
  12. 2 2
      src/global.less
  13. 0 16
      src/hooks/useMenuList.js
  14. 21 0
      src/hooks/useMenuList.ts
  15. 44 5
      src/layouts/index.jsx
  16. 37 5
      src/layouts/index.less
  17. 17 0
      src/models/controller.ts
  18. 0 19
      src/models/global.js
  19. 5 5
      src/pages/Environment/components/chartModal/index.jsx
  20. 0 97
      src/pages/Environment/components/topNavigator/index.jsx
  21. 3 1
      src/pages/Environment/components/topNavigator/index.less
  22. 152 0
      src/pages/Environment/components/topNavigator/index.tsx
  23. 0 155
      src/pages/Environment/index.jsx
  24. 31 21
      src/pages/Environment/index.less
  25. 230 0
      src/pages/Environment/index.tsx
  26. 6 6
      src/pages/Equipment/components/deviceModal/index.jsx
  27. 20 29
      src/pages/Equipment/components/topNavRight/index.jsx
  28. 0 148
      src/pages/Equipment/index.jsx
  29. 19 21
      src/pages/Equipment/index.less
  30. 214 0
      src/pages/Equipment/index.tsx
  31. 1 1
      src/pages/Runtime/RecordList/index.less
  32. 0 49
      src/pages/Runtime/components/topNavRight/index.jsx
  33. 14 8
      src/pages/Runtime/components/topNavRight/index.less
  34. 48 0
      src/pages/Runtime/components/topNavRight/index.tsx
  35. 0 57
      src/pages/Runtime/index.jsx
  36. 13 22
      src/pages/Runtime/index.less
  37. 135 0
      src/pages/Runtime/index.tsx
  38. 35 0
      src/services/ant-design-pro/environment.ts
  39. 2 0
      src/services/ant-design-pro/index.ts
  40. 24 0
      src/services/ant-design-pro/typings.d.ts
  41. 1 1
      src/tenants-ui/Icon/index.jsx

+ 613 - 0
mock/environment.ts

@@ -0,0 +1,613 @@
+import { AlignLeftOutlined } from '@ant-design/icons';
+import { Request, Response } from 'express';
+export default {
+  // 支持值为 Object 和 Array
+  'POST /api/environment/map': (req: Request, res: Response) => {
+    //debugger;
+    res.send({
+      data: [
+        {
+          left: 0,
+          top: 400,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '第一办公区',
+          roomFuncType: 'work',
+          lamp: 'all',
+          curtain: 'all',
+          air: 'all',
+          device: [
+            { type: 'lamp', status: 'all' },
+            { type: 'curtain', status: 'all' },
+          ],
+        },
+        {
+          left: 500,
+          top: 400,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '收发室',
+          roomFuncType: 'letter',
+          air: 'all',
+          curtain: 'all',
+          lamp: 'all',
+          device: [{ type: 'lamp', status: 'all' }],
+        },
+        {
+          left: 1000,
+          top: 400,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '走廊',
+          roomFuncType: 'walk',
+          lamp: 'all',
+          air: 'all',
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'part' },
+            { type: 'air', status: 'close' },
+            { type: 'curtain', status: 'close' },
+          ],
+        },
+        {
+          left: 0,
+          top: 800,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '第一办公区',
+          roomFuncType: 'work',
+          lamp: 'all',
+          air: 'all',
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'close' },
+            { type: 'air', status: 'close' },
+            { type: 'curtain', status: 'close' },
+          ],
+        },
+        {
+          left: 500,
+          top: 800,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '收发室',
+          roomFuncType: 'letter',
+          lamp: 'all',
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'part' },
+            { type: 'air', status: 'part' },
+            { type: 'curtain', status: 'part' },
+          ],
+        },
+        {
+          left: 1000,
+          top: 800,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '走廊',
+          roomFuncType: 'walk',
+          device: [],
+          lamp: 'all',
+          air: 'all',
+          curtain: 'all',
+        },
+        {
+          left: 1500,
+          top: 0,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '董事办公室',
+          roomFuncType: 'office',
+          lamp: 'all',
+          air: 'all',
+          curtain: 'all',
+          device: [{ type: 'curtain', status: 'all' }],
+        },
+        {
+          left: 2000,
+          top: 0,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '休息区',
+          roomFuncType: 'rest',
+          lamp: 'all',
+          air: 'all',
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'all' },
+            { type: 'air', status: 'all' },
+            { type: 'curtain', status: 'all' },
+          ],
+        },
+        {
+          left: 2500,
+          top: 0,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '会议室',
+          roomFuncType: 'meeting',
+
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'all' },
+            { type: 'air', status: 'close' },
+            { type: 'curtain', status: 'part' },
+          ],
+        },
+        {
+          left: 1500,
+          top: 400,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '第一办公区',
+          roomFuncType: 'work',
+          lamp: 'all',
+          air: 'all',
+          device: [
+            { type: 'lamp', status: 'all' },
+            { type: 'air', status: 'part' },
+            { type: 'curtain', status: 'close' },
+          ],
+        },
+        {
+          left: 2000,
+          top: 400,
+          width: 500,
+          height: 400,
+          Tdb: 27,
+          RH: 27,
+          CO2: 27,
+          PM2d5: 27,
+          HCHO: 27,
+          localName: '收发室',
+          roomFuncType: 'letter',
+          lamp: 'all',
+          curtain: 'all',
+          device: [
+            { type: 'lamp', status: 'close' },
+            { type: 'air', status: 'close' },
+            { type: 'curtain', status: 'part' },
+          ],
+        },
+      ],
+      //   data: [
+      //     {
+      //       left: 0,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 17,
+      //       RH: 26,
+      //       CO2: 2800,
+      //       PM2d5: 28,
+      //       HCHO: 0.01,
+      //       localName: '董事办公室',
+      //       roomFuncType: 'office',
+      //       lamp: 'all', //灯的不同状态会显示 不同的颜色
+      //       air: 'part',
+      //       curtain: 'part',
+      //       //device 到时候需要生成
+      //       //当前设备的状态status :all全部开启 part部分开启 close全都关闭
+      //       //还有设备类型type :包括air lamp curtain
+      //       device: [
+      //         { type: 'lamp', status: 'close' },
+      //         { type: 'air', status: 'part' },
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //       timeList: ['08:00-16:00', '08:00-16:00'],
+      //       //   normalRun
+      //       //   overtimeWork
+      //       //   overtimeBook
+      //       runTimeStatus: 'normalRun',
+      //     },
+      //     {
+      //       left: 500,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 65,
+      //       CO2: 1008,
+      //       PM2d5: 38,
+      //       HCHO: 0.03,
+      //       localName: '休息区',
+      //       roomFuncType: 'rest',
+      //       lamp: 'part',
+      //       air: 'close',
+      //       curtain: 'close',
+      //       device: [
+      //         { type: 'lamp', status: 'close' },
+      //         { type: 'air', status: 'close' },
+      //       ],
+      //       runTimeStatus: 'overtimeWork',
+      //     },
+      //     {
+      //       left: 1000,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 20,
+      //       RH: 53,
+      //       CO2: 1808,
+      //       PM2d5: 88,
+      //       HCHO: 0.06,
+      //       localName: '会议室',
+      //       roomFuncType: 'meeting',
+      //       lamp: 'close',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'part' },
+      //         { type: 'curtain', status: 'part' },
+      //       ],
+      //       runTimeStatus: 'overtimeBook',
+      //     },
+      //     {
+      //       left: 0,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 27,
+      //       RH: 37,
+      //       CO2: 28,
+      //       PM2d5: 68,
+      //       HCHO: 0.1,
+      //       localName: '第一办公区',
+      //       roomFuncType: 'work',
+      //       lamp: 'all',
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //     },
+      //     {
+      //       left: 500,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 30,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 0.17,
+      //       localName: '收发室',
+      //       roomFuncType: 'letter',
+      //       air: 'close',
+      //       curtain: 'part',
+      //       device: [{ type: 'lamp', status: 'all' }],
+      //     },
+      //     {
+      //       left: 1000,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 12,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '走廊',
+      //       roomFuncType: 'walk',
+      //       lamp: 'close',
+      //       air: 'part',
+      //       curtain: 'close',
+      //       device: [
+      //         { type: 'lamp', status: 'part' },
+      //         { type: 'air', status: 'close' },
+      //         { type: 'curtain', status: 'close' },
+      //       ],
+      //     },
+      //     {
+      //       left: 0,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '第一办公区',
+      //       roomFuncType: 'work',
+      //       lamp: 'close',
+      //       air: 'all',
+      //       curtain: 'part',
+      //       device: [
+      //         { type: 'lamp', status: 'close' },
+      //         { type: 'air', status: 'close' },
+      //         { type: 'curtain', status: 'close' },
+      //       ],
+      //     },
+      //     {
+      //       left: 500,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 2000,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '收发室',
+      //       roomFuncType: 'letter',
+      //       lamp: 'all',
+
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'part' },
+      //         { type: 'air', status: 'part' },
+      //         { type: 'curtain', status: 'part' },
+      //       ],
+      //     },
+      //     {
+      //       left: 1000,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '走廊',
+      //       roomFuncType: 'walk',
+      //       device: [],
+      //       lamp: 'all',
+      //       air: 'all',
+      //       curtain: 'all',
+      //     },
+      //     {
+      //       left: 1500,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '董事办公室',
+      //       roomFuncType: 'office',
+      //       lamp: 'close',
+      //       air: 'part',
+      //       curtain: 'close',
+      //       device: [{ type: 'curtain', status: 'all' }],
+      //     },
+      //     {
+      //       left: 2000,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '休息区',
+      //       roomFuncType: 'rest',
+      //       lamp: 'part',
+      //       air: 'part',
+      //       curtain: 'part',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'all' },
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //     },
+      //     {
+      //       left: 2500,
+      //       top: 0,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 2000,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '会议室',
+      //       roomFuncType: 'meeting',
+
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'close' },
+      //         { type: 'curtain', status: 'part' },
+      //       ],
+      //     },
+      //     {
+      //       left: 1500,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '第一办公区',
+      //       roomFuncType: 'work',
+      //       lamp: 'all',
+      //       air: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'part' },
+      //         { type: 'curtain', status: 'close' },
+      //       ],
+      //     },
+      //     {
+      //       left: 2000,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 2000,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '收发室',
+      //       roomFuncType: 'letter',
+      //       lamp: 'close',
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'close' },
+      //         { type: 'air', status: 'close' },
+      //         { type: 'curtain', status: 'part' },
+      //       ],
+      //     },
+      //     {
+      //       left: 2500,
+      //       top: 400,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 28,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '走廊',
+      //       roomFuncType: 'walk',
+      //       lamp: 'all',
+      //       air: 'part',
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'part' },
+      //         { type: 'air', status: 'close' },
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //     },
+      //     {
+      //       left: 1500,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 2000,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '第一办公区',
+      //       roomFuncType: 'work',
+      //       device: [{ type: 'curtain', status: 'all' }],
+      //     },
+      //     {
+      //       left: 2000,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 2000,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '收发室',
+      //       roomFuncType: 'letter',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'all' },
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //     },
+      //     {
+      //       left: 2500,
+      //       top: 800,
+      //       width: 500,
+      //       height: 400,
+      //       Tdb: 25,
+      //       RH: 26,
+      //       CO2: 700,
+      //       PM2d5: 28,
+      //       HCHO: 39,
+      //       localName: '走廊',
+      //       roomFuncType: 'walk',
+      //       lamp: 'part',
+      //       air: 'part',
+      //       curtain: 'all',
+      //       device: [
+      //         { type: 'lamp', status: 'all' },
+      //         { type: 'air', status: 'all' },
+      //         { type: 'curtain', status: 'all' },
+      //       ],
+      //     },
+      //   ],
+      success: true,
+    });
+  },
+  'POST /api/environment/Building': (req: Request, res: Response) => {
+    res.send({
+      data: [
+        { localName: '11#', id: '11111' },
+        { localName: '22#', id: '22222' },
+        { localName: '33#', id: '33333' },
+        { localName: '44#', id: '444444' },
+      ],
+      success: true,
+    });
+  },
+  'POST /api/environment/Floor': (req: Request, res: Response) => {
+    res.send({
+      data: [
+        { localName: 'F1', id: 'ffff1' },
+        { localName: 'F2', id: 'ffff2' },
+        { localName: 'F3', id: 'ffff3' },
+        { localName: 'F4', id: 'ffff4' },
+        { localName: 'F5', id: 'ffff5' },
+        { localName: 'F6', id: 'ffff6' },
+        { localName: 'F7', id: 'ffff7' },
+        { localName: 'F8', id: 'ffff8' },
+      ],
+      success: true,
+    });
+  },
+};

src/antd-reset.less → src/assets/css/antd-reset.less


src/antd-tenants.less → src/assets/css/antd-tenants.less


+ 77 - 0
src/assets/css/map.less

@@ -0,0 +1,77 @@
+.map {
+  position: relative;
+  .house {
+    position: absolute;
+    border: 2px solid #fff;
+    border-radius: 6px;
+    .allDevice {
+      position: absolute;
+      top: 6px;
+      left: 6px;
+      z-index: 1;
+      display: flex;
+      width: 100%;
+      .icon {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 22px;
+        height: 22px;
+        margin-right: 3px;
+        color: #ffffff;
+        font-size: 11px;
+        background: transparent;
+        border-radius: 22px;
+      }
+    }
+    .allTime {
+      position: absolute;
+      top: 4px;
+      left: 4px;
+      z-index: 1;
+      display: flex;
+      width: 100%;
+      color: #4d5262;
+      font-size: 10px;
+      text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white;
+    }
+    .content {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      height: 100%;
+      .name {
+        color: #000000;
+        font-size: 13px;
+        //text-shadow: 1px 1px 1px #ffffff;
+        text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white;
+      }
+    }
+  }
+}
+
+.equipmentMap {
+  .house {
+    cursor: pointer;
+
+    .showModal {
+      position: absolute;
+      right: 6px;
+      bottom: 7px;
+      z-index: 2;
+      display: flex;
+      display: none;
+      align-items: center;
+      justify-content: center;
+      width: 22px;
+      height: 22px;
+      font-size: 12px;
+      background: #ffffff;
+      border-radius: 22px;
+    }
+    &:hover .showModal {
+      display: flex;
+    }
+  }
+}

+ 0 - 16
src/components/PageHeader/index.jsx

@@ -1,16 +0,0 @@
-import React from 'react';
-import styles from './index.less';
-
-export default ({
-  title,
-  action,
-}) => {
-  return (
-    <div className={styles.c}>
-      <div className={styles.title}>{ title }</div>
-      {
-        action
-      }
-    </div>
-  )
-}

+ 14 - 14
src/components/PageHeader/index.less

@@ -1,18 +1,18 @@
-.c {
+.navHeader {
   display: flex;
   align-items: center;
   justify-content: space-between;
-}
-
-.title {
-  font-size: 24px;
-  color: #000;
-  font-weight: 600;
-  &::before {
-    content: "/";
-    font-size: 32px;
-    font-weight: normal;
-    color: #f0da21;
-    margin-right: 10px;
+  height: 50px;
+  .title {
+    color: #000;
+    font-weight: 600;
+    font-size: 24px;
+    &::before {
+      margin-right: 10px;
+      color: #f0da21;
+      font-weight: normal;
+      font-size: 32px;
+      content: '/';
+    }
   }
-}
+}

+ 33 - 0
src/components/PageHeader/index.tsx

@@ -0,0 +1,33 @@
+import React, { useEffect } from 'react';
+import styles from './index.less';
+
+import { useModel, history } from 'umi';
+
+type PageHeaderProps = {
+  title: string;
+  action: React.ReactNode;
+};
+const PageHeader: React.FC<PageHeaderProps> = ({ title, action }) => {
+  //console.log('PageHeader');
+
+  useEffect(() => {
+    //console.log('PageHeader-useEffect');
+    const unlisten = history.listen((location, action) => {
+      //console.log('location,action', location, action);
+    });
+    return () => {
+      // console.log('un--PageHeader--useEffect');
+      unlisten();
+    };
+  }, []);
+  return (
+    <>
+      <div className={styles.navHeader}>
+        <div className={styles.title}>{title}</div>
+        {action}
+      </div>
+    </>
+  );
+};
+
+export default PageHeader;

+ 0 - 0
src/components/map/index.less


+ 28 - 0
src/components/map/index.tsx

@@ -0,0 +1,28 @@
+import React, { useState, useEffect } from 'react';
+import mapstyles from '@/assets/css/map.less';
+import { getMapList } from '@/services/ant-design-pro/environment';
+import cx from 'classnames';
+
+type MapProps = {
+  //mapList: API.MapInfo[];
+  type: string;
+  render: (item: API.MapInfo, index: number) => React.ReactNode;
+};
+
+const Map: React.FC<MapProps> = ({ type, render }) => {
+  const [mapList, setMapList] = useState<API.MapInfo[]>([]);
+  useEffect(() => {
+    getMapList({ floorId: 'sffsdfdsfdf', floorNumber: 22 }).then(function (res) {
+      var data: API.MapInfo[] = res.data || [];
+      setMapList(data);
+    });
+  }, []);
+  return (
+    <div className={cx(mapstyles.map, { [mapstyles.equipmentMap]: type == 'equipment' })}>
+      {mapList.map(function (item, index) {
+        return render(item, index);
+      })}
+    </div>
+  );
+};
+export default Map;

+ 33 - 0
src/components/navMenu/index.less

@@ -0,0 +1,33 @@
+.drawerDiv {
+  //   width: 380px;
+  //   height: 340px;
+  padding: 0 24px;
+  background-color: #ffffff;
+  border-radius: 10px;
+  .title {
+    display: flex;
+    align-items: center;
+    height: 100px;
+    font-size: 22px;
+  }
+  .menuList {
+    padding-bottom: 60px;
+
+    .menuItem {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      box-sizing: border-box;
+      height: 120px;
+      color: #656872;
+      font-size: 14px;
+      border: 1px solid #eceff4;
+      border-radius: 20px;
+      cursor: pointer;
+      .name {
+        padding-top: 14px;
+      }
+    }
+  }
+}

+ 43 - 0
src/components/navMenu/index.tsx

@@ -0,0 +1,43 @@
+import React, { useEffect } from 'react';
+import { Row, Col } from 'antd';
+import styles from './index.less';
+import Icon from '@/tenants-ui/Icon';
+import useMenuList, { menutype } from '@/hooks/useMenuList';
+import { history, useModel } from 'umi';
+
+const NavMenu: React.FC = () => {
+  const { closeMenu } = useModel('controller');
+  const { menuList } = useMenuList();
+  //debugger;
+  const menuClick = (item: menutype) => {
+    closeMenu();
+    history.push(`/${item.id}`);
+  };
+  //   console.log('navMenu');
+
+  return (
+    <div className={styles.drawerDiv}>
+      <div className={styles.title}>头部</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>
+    </div>
+  );
+};
+export default NavMenu;

+ 2 - 2
src/global.less

@@ -1,7 +1,7 @@
 @import '~antd/es/style/variable.less';
 @import '~antd/dist/antd.less'; // 引入官方提供的 less 样式入口文件
-@import './antd-tenants.less';
-@import './antd-reset.less';
+@import './assets/css/antd-tenants.less';
+@import './assets/css/antd-reset.less';
 
 html,
 body,

+ 0 - 16
src/hooks/useMenuList.js

@@ -1,16 +0,0 @@
-import { useState, useEffect } from 'react';
-import { getPanelMenu } from '../services';
-
-export default function useMenuList() {
-  const [list, setList] = useState([]);
-  useEffect(() => {
-    getPanelMenu().then((res) => {
-      if (res.code === 1) {
-        setList(res.data);
-      }
-    });
-  }, []);
-  return {
-    list,
-  };
-}

+ 21 - 0
src/hooks/useMenuList.ts

@@ -0,0 +1,21 @@
+import { useState, useEffect } from 'react';
+
+export type menutype = {
+  name: string;
+  id?: string;
+};
+export default function () {
+  const [menuList, setMenuList] = useState<menutype[]>();
+  useEffect(() => {
+    //debugger;
+    setTimeout(() => {
+      setMenuList([
+        { name: '环境信息', id: 'environment' },
+        { name: '设备管理', id: 'equipment' },
+        { name: '运行时间', id: 'runtime' },
+      ]);
+    }, 0);
+  }, []);
+
+  return { menuList };
+}

+ 44 - 5
src/layouts/index.jsx

@@ -1,13 +1,52 @@
-import React, { useEffect } from 'react';
+import React, { useState, useEffect } from 'react';
 import { useModel, useLocation, useIntl } from 'umi';
 import styles from './index.less';
+import { MenuFoldOutlined } from '@ant-design/icons';
+import Icon from '@/tenants-ui/Icon';
+
+import { Drawer } from 'antd';
+import NavMenu from '@/components/navMenu';
 
 export default (props) => {
+  const { menuVisible, closeMenu, toggleMenu } = useModel('controller');
+
+  const showMenuClick = () => {
+    // toggleMenu();
+  };
   return (
-    <>
-      <div className={styles.layout}>
-        <div className={styles.content}>{props.children}</div>
+    <div className={styles.layout}>
+      <div className={styles.header}>
+        <div className={styles.title}>之江项目</div>
+        <div className={styles.right}>
+          <div className={styles.picture}></div>
+          <Icon className={styles.notify} type="notify" style={{ fontSize: 21 }}></Icon>
+          <MenuFoldOutlined style={{ fontSize: 24 }} onClick={showMenuClick} />
+        </div>
       </div>
-    </>
+
+      {props.children}
+      <Drawer
+        title=""
+        placement="right"
+        closable={false}
+        onClose={closeMenu}
+        visible={menuVisible}
+        drawerStyle={
+          {
+            //   height: 340,
+            //   width: 380,
+            //   background: 'transparent',
+            //   transform: 'translate(-50px,20px)',
+            // top: 100,
+            //right: 20,
+          }
+        }
+        maskStyle={{ background: 'transparent' }}
+        contentWrapperStyle={{ boxShadow: 'none' }}
+        bodyStyle={{ transform: 'translate(-20px,20px)' }}
+      >
+        <NavMenu></NavMenu>
+      </Drawer>
+    </div>
   );
 };

+ 37 - 5
src/layouts/index.less

@@ -1,15 +1,47 @@
 .layout {
+  box-sizing: border-box;
   width: 1340px;
+  height: 100%;
   margin: 0 auto;
+  padding: 20px;
+}
+
+:global {
+  .ant-drawer-content {
+    overflow: visible;
+    //  border-radius: 10px;
+    background: transparent;
+  }
+  .ant-drawer-content-wrapper {
+    // border-radius: 10px;
+  }
 }
 
 .header {
-  padding: 42px 0 35px;
   display: flex;
   align-items: center;
   justify-content: space-between;
+  box-sizing: border-box;
+  height: 100px;
+  padding: 20px 0 40px 0;
+  .title {
+    color: #000000;
+    font-weight: 600;
+    font-size: 24px;
+  }
+  .right {
+    display: flex;
+    align-items: center;
+    .picture {
+      width: 40px;
+      height: 40px;
+      margin-right: 24px;
+      background-color: #4f4f4f;
+      border-radius: 40px;
+    }
+    .notify {
+      margin-right: 24px;
+      cursor: pointer;
+    }
+  }
 }
-
-.footer {
-  padding-top: 80px;
-}

+ 17 - 0
src/models/controller.ts

@@ -0,0 +1,17 @@
+import React, { useState, useCallback } from 'react';
+
+export default function () {
+  const [menuVisible, setMenuVisible] = useState<boolean>(false);
+  const closeMenu: () => void = () => {
+    setMenuVisible(false);
+  };
+  const toggleMenu: () => void = () => {
+    //debugger;
+    setMenuVisible(!menuVisible);
+  };
+  return {
+    menuVisible,
+    toggleMenu,
+    closeMenu,
+  };
+}

+ 0 - 19
src/models/global.js

@@ -1,19 +0,0 @@
-import { useState, useCallback } from 'react';
-
-export default function useGlobalModel() {
-  const [controlCenter, setControlCenter] = useState(false);
-
-  const toggleControlCenter = useCallback(() => {
-    setControlCenter(!controlCenter);
-  }, [controlCenter]);
-
-  const closeControlCenter = useCallback(() => {
-    setControlCenter(false);
-  }, []);
-
-  return {
-    controlCenter,
-    toggleControlCenter,
-    closeControlCenter,
-  };
-}

+ 5 - 5
src/pages/Environment/components/chartModal/index.jsx

@@ -10,25 +10,25 @@ export default ({ onClose }) => {
   const [paramList, setParamList] = useState([
     {
       name: '温湿度',
-      id: 'temperature&wet',
+      id: 'Tdb&RH',
       content:
         '温度表示空气的冷热程度,夏季高温高湿会使人烦躁、疲倦,冬季湿度过高时,人体感觉越冷,冬季湿度低易引起呼吸道不舒适,室外温度较高时,宜遮阳关窗',
     },
     {
       name: 'CO₂',
-      id: 'co2',
+      id: 'CO2',
       content:
         'CO₂一种无色无味气体,CO₂的标准为1000ppm,CO₂过高,影响人体呼吸系统,大脑易疲劳,工作效率降低,室内人员增多,新风量不足时,室内CO₂的浓度就会上升,宜开窗通风或增加送风量来降低CO₂浓度',
     },
     {
       name: 'PM2.5',
-      id: 'pm25',
+      id: 'PM2d5',
       content:
         'PM2.5,直径≤2.5微米的颗粒,PM2.5的平均值在0-35μg/m3,PM2.5浓度过高,影响人体,呼吸系统和心血管系统健康,应开启除尘净化设备',
     },
     {
       name: '甲醛',
-      id: 'jiaquan',
+      id: 'HCHO',
       content:
         '甲醛(HCHO)是无色有刺激性有机气体。甲醛的主要危害表现为对皮肤和黏膜的刺激作用。由于家具,胶水中的甲醛挥发是一个长期缓慢的过程,因此甲醛超标常表现为室内甲醛浓度长期处于较高的数值。',
     },
@@ -39,7 +39,7 @@ export default ({ onClose }) => {
         'TVOC,有毒有刺激性气味TVOC的标准为0.60mg/m3,TVOC超标,影响人体视觉,抽烟、涂料、燃烧、清洁剂,等会导致TVOC浓度升高,可通过通风、绿植、活性炭吸,附等方式减轻TVOC污染程度',
     },
   ]);
-  //const [paramId, setParamId] = useState('temperature&wet');
+  //const [paramId, setParamId] = useState('Tdb&RH');
   const [paramObj, setParamObj] = useState({});
   var data = [
     { Date: '2010-01', scales: 1998 },

+ 0 - 97
src/pages/Environment/components/topNavigator/index.jsx

@@ -1,97 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import styles from './index.less';
-import cx from 'classnames';
-import { Select } from 'antd';
-import EquipNavRight from '@/pages/Equipment/components/topNavRight';
-import RunNavRight from '@/pages/Runtime/components/topNavRight';
-
-const FloorList = [
-  {
-    label: 'F1',
-    value: 'F1',
-  },
-  {
-    label: 'F2',
-    value: 'F2',
-  },
-  {
-    label: 'F3',
-    value: 'F3',
-  },
-  {
-    label: 'F4',
-    value: 'F4',
-  },
-];
-
-export default ({ navigatorList = [], type, navigatorChange }) => {
-  const [currentFloor, setCurrentFloor] = useState('F2');
-
-  //const [selId, setSelId] = useState('temperature');
-  const [selParamObj, setSelParamObj] = useState({});
-
-  useEffect(() => {
-    console.log('chartModal-effect');
-    setSelParamObj(navigatorList[0]);
-  }, []);
-
-  const changHandle = useCallback((val) => {
-    console.log('select-onChange', val);
-    setCurrentFloor(val);
-  }, []);
-  //当前切换导航条时
-  const itemClick = (item) => {
-    //setSelId(item.id);
-    setSelParamObj(item);
-    console.log('itemclick', item);
-    navigatorChange && navigatorChange(item);
-  };
-
-  console.log('top渲染');
-
-  return (
-    <div className={styles.topnavigator}>
-      <div className={styles.floor}>
-        <Select
-          options={FloorList}
-          value={currentFloor}
-          onChange={changHandle}
-          size="large"
-          dropdownMatchSelectWidth={true}
-          style={{ width: '100%' }}
-          bordered={false}
-        />
-      </div>
-      <div className={styles.navigator}>
-        {navigatorList.map((item, index) => {
-          return (
-            <div
-              className={cx(styles.navItem, { [styles.sel]: item.id === selParamObj.id })}
-              style={{
-                borderBottom: item.id === selParamObj.id ? `3px solid  ${item.color}` : null,
-              }}
-              key={'nav' + index}
-              onClick={() => {
-                itemClick(item);
-              }}
-            >
-              <img src={item.src} style={{ height: 20 }}></img>
-              <div className={styles.text}>{item.name}</div>
-            </div>
-          );
-        })}
-      </div>
-      {type === 'enviroment' && (
-        <div className={styles.right}>
-          <div className={styles.firstLine} style={{ color: selParamObj.color }}>
-            <span className={styles.value}>{selParamObj.num}</span>{' '}
-            <span className={styles.unit}>{selParamObj.unit}</span>
-          </div>
-          <div className={styles.text}>当前楼层平均{selParamObj.name}</div>
-        </div>
-      )}
-      {type === 'equipment' && <EquipNavRight selParamObj={selParamObj}></EquipNavRight>}
-      {type === 'runtime' && <RunNavRight></RunNavRight>}
-    </div>
-  );
-};

+ 3 - 1
src/pages/Environment/components/topNavigator/index.less

@@ -5,14 +5,16 @@
   box-sizing: border-box;
   //   display: flex;
   height: 68px;
+  margin-top: 18px;
   background: #fff;
   .floor {
     display: flex;
     align-items: center;
     float: left;
     //justify-content: center;
-    width: 140px;
+    width: 120px;
     height: 100%;
+    border-right: 1px solid rgba(196, 196, 196, 0.4);
   }
   .navigator {
     display: flex;

+ 152 - 0
src/pages/Environment/components/topNavigator/index.tsx

@@ -0,0 +1,152 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import styles from './index.less';
+import cx from 'classnames';
+import { Select } from 'antd';
+
+import type { navigatorItem } from '@/pages/Environment/index';
+import { getBuildingList, getFloorList } from '@/services/ant-design-pro/environment';
+
+export type topNavigatorProps = {
+  navigatorList?: navigatorItem[];
+  type: string;
+  navigatorChange?(item: navigatorItem): void;
+  // action?: React.ReactElement<topNavRightProps>[];
+  action?: React.ReactNode;
+};
+type floorItem = {
+  value: string;
+  [key: string]: any;
+};
+type buildItem = {
+  value: string;
+  [key: string]: any;
+};
+const TopNavigator: React.FC<topNavigatorProps> = ({
+  navigatorList = [],
+  type,
+  navigatorChange,
+  action,
+}) => {
+  const [buildList, setBuildList] = useState<buildItem[]>([]);
+  const [currentBuild, setCurrentBuild] = useState<any>();
+
+  const [floorList, setFloorList] = useState<floorItem[]>([]);
+  const [currentFloor, setCurrentFloor] = useState<string>();
+
+  const [selParamObj, setSelParamObj] = useState(navigatorList[0]);
+
+  useEffect(() => {
+    //console.log('TopNavigator----getBuildingList');
+    getBuildingList({}).then((res) => {
+      //debugger;
+      var resData = res.data || [];
+      setBuildList(
+        (resData || []).map((item) => {
+          return { label: item.name, value: item.id };
+        }),
+      );
+      // debugger;
+      setCurrentBuild((resData[0] || {}).id); //设置第一个值
+    });
+  }, []);
+
+  useEffect(() => {
+    if (currentBuild) {
+      getFloorList({ buildId: currentBuild }).then((res) => {
+        //debugger;
+        var resData = res.data || [];
+        setFloorList(
+          (resData || []).map((item) => {
+            return { label: item.name, value: item.id };
+          }),
+        );
+        //debugger;
+        setCurrentFloor((resData[0] || {}).id); //设置第一个值
+      });
+    }
+  }, [currentBuild]);
+
+  const changBuildHandle = useCallback((val) => {
+    //console.log('select-onChange', val);
+    //debugger;
+    setCurrentBuild(val);
+  }, []);
+  const changFloorHandle = useCallback((val) => {
+    //console.log('select-onChange', val);
+    //debugger;
+    setCurrentFloor(val);
+  }, []);
+
+  //当前切换导航条时
+  const itemClick = (item: navigatorItem) => {
+    setSelParamObj(item);
+    console.log('itemclick', item);
+    navigatorChange && navigatorChange(item);
+  };
+
+  console.log('top渲染');
+
+  return (
+    <div className={styles.topnavigator}>
+      <div className={styles.floor}>
+        <Select
+          options={buildList}
+          placeholder="楼栋"
+          value={currentBuild}
+          onChange={changBuildHandle}
+          size="large"
+          dropdownMatchSelectWidth={true}
+          style={{ width: '100%' }}
+          bordered={false}
+          //   labelInValue={true}
+          // fieldNames={{ label: 'name', value: 'id' }}
+        />
+      </div>
+      <div className={styles.floor}>
+        <Select
+          options={floorList}
+          placeholder="楼层"
+          value={currentFloor}
+          onChange={changFloorHandle}
+          size="large"
+          dropdownMatchSelectWidth={true}
+          style={{ width: '100%' }}
+          bordered={false}
+        />
+      </div>
+      <div className={styles.navigator}>
+        {navigatorList.map((item, index) => {
+          return (
+            <div
+              className={cx(styles.navItem, { [styles.sel]: item.id === selParamObj.id })}
+              style={{
+                borderBottom:
+                  item.id === selParamObj.id ? `3px solid  ${item.color}` : '3px solid #ffffff',
+              }}
+              key={'nav' + index}
+              onClick={() => {
+                itemClick(item);
+              }}
+            >
+              <img src={item.src} style={{ height: 20 }}></img>
+              <div className={styles.text}>{item.name}</div>
+            </div>
+          );
+        })}
+      </div>
+      {type === 'enviroment' && (
+        <div className={styles.right}>
+          <div className={styles.firstLine} style={{ color: selParamObj.color }}>
+            <span className={styles.value}>{selParamObj.num}</span>{' '}
+            <span className={styles.unit}>{selParamObj.unit}</span>
+          </div>
+          <div className={styles.text}>当前楼层平均{selParamObj.name}</div>
+        </div>
+      )}
+      {type === 'equipment' && action}
+      {type === 'runtime' && action}
+    </div>
+  );
+};
+
+export default TopNavigator;

+ 0 - 155
src/pages/Environment/index.jsx

@@ -1,155 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import { FormattedMessage } from 'umi';
-import PageHeader from '@/components/PageHeader';
-// import SearchInput from '@/tenants-ui/SearchInput';
-import TopNavigator from './components/topNavigator';
-import ChartModal from './components/chartModal';
-import { equipImageMap } from '@/config/image.js';
-import { Input } from 'antd';
-
-import styles from './index.less';
-
-export default function Environment() {
-  const {
-    temperMapTop,
-    co2MapTop,
-    maptemp1,
-    maptemp2,
-    envir_temperature,
-    envir_wet,
-    envir_co2,
-    envir_pm25,
-    envir_jiaquan,
-    envir_tvoc,
-  } = equipImageMap;
-
-  const [searchText, setSearchText] = useState();
-  const [showChart, setShowChart] = useState(false);
-  const [explainImg, setExplainImg] = useState(temperMapTop);
-  const [mapImg, setMapImg] = useState(maptemp1);
-
-  const [navigatorList] = useState([
-    {
-      name: '温度',
-      id: 'temperature',
-      src: envir_temperature,
-      sel: false,
-      unit: '℃',
-      num: 24.3,
-      color: '#E5574F',
-    },
-    {
-      name: '湿度',
-      id: 'wet',
-      src: envir_wet,
-      sel: false,
-      unit: '%',
-      num: 70,
-      color: '#9FB7CD',
-    },
-    {
-      name: 'CO₂',
-      id: 'co2',
-      src: envir_co2,
-      sel: false,
-      unit: 'ppm',
-      num: 600,
-      color: '#00DC23',
-    },
-    {
-      name: 'PM2.5',
-      id: 'pm25',
-      src: envir_pm25,
-      sel: false,
-      unit: 'ug/m³',
-      num: 33,
-      color: '#E89E32',
-    },
-    {
-      name: '甲醛',
-      id: 'jiaquan',
-      src: envir_jiaquan,
-      sel: false,
-      unit: 'mg/m³',
-      num: 50,
-      color: '#140050',
-    },
-    {
-      name: 'TVOC',
-      id: 'tvoc',
-      src: envir_tvoc,
-      sel: false,
-      unit: 'mg/m³',
-      num: 36,
-      color: '#2D553E',
-    },
-  ]);
-  const navigatorChange = (item) => {
-    if (item.id == 'temperature') {
-      setExplainImg(temperMapTop);
-      setMapImg(maptemp1);
-    }
-    if (item.id == 'co2') {
-      setExplainImg(co2MapTop);
-      setMapImg(maptemp2);
-    }
-  };
-  const specialRoomClick = () => {
-    console.log('showChart');
-    setShowChart(true);
-  };
-  return (
-    <div>
-      <PageHeader
-        title={<FormattedMessage id="menu.environment" />}
-        action={
-          //   <SearchInput
-          //     placeholder="搜索空间"
-          //     onSearch={(s) => {
-          //       setSearchText(s || undefined);
-          //     }}
-          //     onCancel={() => {
-          //       setSearchText();
-          //     }}
-          //   />
-
-          <Input.Search
-            size="large"
-            allowClear
-            style={{ width: '20%' }}
-            placeholder={'搜索空间'}
-            onSearch={(s) => {
-              setSearchText(s || undefined);
-            }}
-          />
-        }
-      />
-      <div style={{ marginTop: '26px' }}>
-        <TopNavigator
-          navigatorList={navigatorList}
-          type={'enviroment'}
-          navigatorChange={navigatorChange}
-        ></TopNavigator>
-        <div className={styles.mapwrap}>
-          <div className={styles.top}>
-            <div className={styles.right}>
-              <img style={{ width: '100%' }} src={explainImg}></img>
-            </div>
-          </div>
-          <div className={styles.map}>
-            <div className={styles.specialRoom} onClick={specialRoomClick}></div>
-            <img src={mapImg}></img>
-          </div>
-        </div>
-      </div>
-
-      {showChart && (
-        <ChartModal
-          onClose={() => {
-            setShowChart(false);
-          }}
-        ></ChartModal>
-      )}
-    </div>
-  );
-}

+ 31 - 21
src/pages/Environment/index.less

@@ -1,27 +1,37 @@
 .label {
   color: #c4c4c4;
 }
-.mapwrap {
-  .top {
-    box-sizing: border-box;
-    height: 47px;
-    padding-top: 10px;
-    padding-right: 20px;
-    .right {
-      float: right;
-      width: 210px;
+.maptop {
+  box-sizing: border-box;
+  height: 44px;
+  padding-top: 10px;
+  padding-right: 20px;
+  .right {
+    display: flex;
+    float: right;
+    .indicatorBox {
+      .text {
+        padding-bottom: 7px;
+        color: #4d5262;
+        font-size: 11px;
+        line-height: 1;
+      }
+      .color {
+        width: 40px;
+        height: 4px;
+      }
     }
   }
-  .map {
-    position: relative;
-    width: 100%;
-    overflow: hidden;
-  }
-  .specialRoom {
-    position: absolute;
-    top: 0;
-    right: 0;
-    width: 180px;
-    height: 170px;
-  }
 }
+.mapwrap {
+  height: calc(100% - 280px);
+  overflow: hidden;
+}
+
+//   .specialRoom {
+//     position: absolute;
+//     top: 0;
+//     right: 0;
+//     width: 180px;
+//     height: 170px;
+//   }

+ 230 - 0
src/pages/Environment/index.tsx

@@ -0,0 +1,230 @@
+import React, { useState, useEffect } from 'react';
+import { FormattedMessage } from 'umi';
+import PageHeader from '@/components/PageHeader';
+import Map from '@/components/map';
+// import SearchInput from '@/tenants-ui/SearchInput';
+import TopNavigator from './components/topNavigator';
+import ChartModal from './components/chartModal';
+
+import { equipImageMap } from '@/config/image.js';
+import { Input } from 'antd';
+import Icon from '@/tenants-ui/Icon';
+
+import styles from './index.less';
+import mapstyles from '@/assets/css/map.less';
+
+export type navigatorItem = {
+  name: string;
+  id: string;
+  src: any;
+  unit?: string;
+  num?: number;
+  color: string;
+  colorStr: string;
+  opacity?: number;
+  indicator?: number[];
+  [key: string]: any;
+};
+const Environment: React.FC = () => {
+  const { envir_temperature, envir_wet, envir_co2, envir_pm25, envir_jiaquan } = equipImageMap;
+
+  const [searchText, setSearchText] = useState<string>();
+
+  const [showChart, setShowChart] = useState(false);
+  const [navigatorList] = useState<navigatorItem[]>([
+    {
+      name: '温度',
+      id: 'Tdb',
+      src: envir_temperature,
+      unit: '℃',
+      num: 24.3,
+      color: '#E5574F',
+      colorStr: '229,87,79,',
+      opacity: 0.2,
+      indicator: [18, 21, 24, 27, 30],
+    },
+    {
+      name: '湿度',
+      id: 'RH',
+      src: envir_wet,
+      unit: '%',
+      num: 70,
+      color: '#9FB7CD',
+      colorStr: '159,183,205,',
+      opacity: 0.2,
+      indicator: [25, 35, 45, 55, 65],
+    },
+    {
+      name: 'CO₂',
+      id: 'CO2',
+      src: envir_co2,
+      unit: 'ppm',
+      num: 600,
+      color: '#00DC23',
+      colorStr: '0,220,35,',
+      opacity: 0.2,
+      indicator: [500, 700, 1000, 2000, 3000],
+    },
+    {
+      name: 'PM2.5',
+      id: 'PM2d5',
+      src: envir_pm25,
+      unit: 'ug/m³',
+      num: 33,
+      color: '#E89E32',
+      colorStr: '232,158,50,',
+      opacity: 0.2,
+      indicator: [15, 25, 35, 75, 90],
+    },
+    {
+      name: '甲醛',
+      id: 'HCHO',
+      src: envir_jiaquan,
+      unit: 'mg/m³',
+      num: 50,
+      color: '#140050',
+      colorStr: '20,0,80,',
+      opacity: 0.25,
+      indicator: [0.02, 0.05, 0.1, 0.15], //指标梯度
+    },
+  ]);
+  const [selNav, setSelNav] = useState<navigatorItem>(navigatorList[0]);
+  const navigatorChange = (item: navigatorItem) => {
+    if (item.id == 'Tdb') {
+    }
+    setSelNav(item);
+  };
+  // const specialRoomClick = () => {
+  //   console.log('showChart');
+  //   setShowChart(true);
+  // };
+
+  //根据指标的值  得到不同的透明度等级 第一级0.2 第二级0.4 依次往后
+  const getColorOpacity = (value: number): number => {
+    var indicator: number[] = selNav.indicator || []; //指标梯度
+    var number = indicator.length;
+    var res = 1;
+    for (var i = 0; i < number; i++) {
+      if (value >= indicator[i] && value < indicator[i + 1]) {
+        res = i + 1;
+        break;
+      }
+    }
+    return res;
+  };
+
+  return (
+    <>
+      <PageHeader
+        title={<FormattedMessage id="menu.environment" />}
+        action={
+          //   <SearchInput
+          //     placeholder="搜索空间"
+          //     onSearch={(s) => {
+          //       setSearchText(s || undefined);
+          //     }}
+          //     onCancel={() => {
+          //       setSearchText();
+          //     }}
+          //   />
+
+          <Input.Search
+            size="large"
+            allowClear
+            style={{ width: '230px' }}
+            placeholder={'搜索空间'}
+            onSearch={(s) => {
+              setSearchText(s || undefined);
+            }}
+          />
+        }
+      />
+      <TopNavigator
+        navigatorList={navigatorList}
+        type={'enviroment'}
+        navigatorChange={navigatorChange}
+      ></TopNavigator>
+      <div className={styles.maptop}>
+        <div className={styles.right}>
+          {(selNav.indicator || []).map((item, index) => {
+            return (
+              <div className={styles.indicatorBox} key={index + 'indicator'}>
+                <div className={styles.text}>{item}</div>
+                <div
+                  className={styles.color}
+                  style={{
+                    backgroundColor:
+                      'rgba(' + selNav.colorStr + (selNav.opacity as number) * (index + 1) + ')',
+                  }}
+                ></div>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+      <div className={styles.mapwrap}>
+        <Map
+          type={'enviroment'}
+          render={(item: API.MapInfo, index: number) => {
+            return (
+              <div
+                key={index + 'house'}
+                className={mapstyles.house}
+                style={{
+                  left: item.left,
+                  top: item.top,
+                  width: item.width,
+                  height: item.height,
+                  backgroundColor:
+                    'rgba(' +
+                    selNav.colorStr +
+                    (selNav.opacity as number) * getColorOpacity(item[selNav.id]) +
+                    ')',
+                }}
+              >
+                <div className={mapstyles.content}>
+                  <Icon className="" type={item.roomFuncType} style={{ fontSize: 20 }}></Icon>
+                  <div className={mapstyles.name}>{item.localName}</div>
+                </div>
+              </div>
+            );
+          }}
+        ></Map>
+        {/* {mapList.map(function (item, index) {
+              return (
+                <div
+                  key={index + 'house'}
+                  className={mapstyles.house}
+                  style={{
+                    left: item.left,
+                    top: item.top,
+                    width: item.width,
+                    height: item.height,
+                    backgroundColor:
+                      'rgba(' +
+                      selNav.colorStr +
+                      (selNav.opacity as number) * getColorOpacity(item.value) +
+                      ')',
+                  }}
+                >
+                  <div className={mapstyles.content}>
+                    <Icon className="" type={item.type} style={{ fontSize: 20 }}></Icon>
+                    <div className={mapstyles.name}>{item.name}</div>
+                  </div>
+                </div>
+              );
+            })} */}
+      </div>
+
+      {showChart && (
+        <ChartModal
+          onClose={() => {
+            setShowChart(false);
+          }}
+        ></ChartModal>
+      )}
+    </>
+  );
+};
+
+export default Environment;

+ 6 - 6
src/pages/Equipment/components/deviceModal/index.jsx

@@ -30,37 +30,37 @@ export default ({ onClose }) => {
   const [paramList, setParamList] = useState([
     {
       name: '温度',
-      id: 'temperature',
+      id: 'Tdb',
       num: 24,
       describe: '温度适宜',
     },
     {
       name: 'CO₂',
-      id: 'co2',
+      id: 'CO2',
       num: 410,
       describe: '空气清新',
     },
     {
       name: 'PM2.5',
-      id: 'pm25',
+      id: 'PM2d5',
       num: 0.3,
       describe: '优秀',
     },
     {
       name: '甲醛',
-      id: 'jiaquan',
+      id: 'HCHO',
       num: 0.5,
       describe: '健康',
     },
     {
       name: '湿度',
-      id: 'wet',
+      id: 'RH',
       num: 26,
       unit: '%',
       describe: '健康',
     },
   ]);
-  //const [paramId, setParamId] = useState('temperature&wet');
+  //const [paramId, setParamId] = useState('Tdb&RH');
   const [paramObj, setParamObj] = useState({});
   const [editVisible, setEditVisible] = useState(false);
   useEffect(() => {

+ 20 - 29
src/pages/Equipment/components/topNavRight/index.jsx

@@ -1,35 +1,24 @@
 // 设备管理 导航条的右侧
 import React, { useState, useEffect } from 'react';
-import { Dropdown, Menu, Modal, Button } from 'antd';
+import { Dropdown, Menu, Modal } from 'antd';
 import Icon from '@/tenants-ui/Icon';
 import { EllipsisOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
 import styles from './index.less';
-import { equipImageMap } from '@/config/image.js';
+//import { equipImageMap } from '@/config/image.js';
+import type { navigatorItem } from '@/pages/Environment/index';
 
-export default ({ selParamObj }) => {
-  const { equip_air, equip_lamp, equip_water } = equipImageMap;
-  const [modalvisible, setModalvisible] = useState(false);
-  const [allList] = useState([
-    {
-      name: '空调',
-      id: 'air',
-      src: equip_air,
-      num: 7,
-    },
-    {
-      name: '照明',
-      id: 'lamp',
-      src: equip_lamp,
-      num: 60,
-    },
-    {
-      name: '水机',
-      id: 'water',
-      src: equip_water,
-      num: 33,
-    },
-  ]);
+export type topNavRightProps = {
+  selNavObj: navigatorItem;
+  navigatorList: navigatorItem[];
+};
 
+const TopNavRight: React.FC<topNavRightProps> = ({ selNavObj, navigatorList }) => {
+  // const { equip_air, equip_lamp, equip_water } = equipImageMap;
+  const [modalvisible, setModalvisible] = useState<boolean>(false);
+  const [allList, setAllList] = useState<navigatorItem[]>([]);
+  useEffect(() => {
+    setAllList(navigatorList.slice(1));
+  }, []);
   const menuClick = (key) => {
     console.log('key', key);
   };
@@ -52,7 +41,7 @@ export default ({ selParamObj }) => {
   return (
     <>
       {/* 全部设备 */}
-      {selParamObj.id === 'all' && (
+      {selNavObj.id === 'all' && (
         <div className={styles.right}>
           <div className={styles.title}>运行中的设备</div>
           <div className={styles.allList}>
@@ -68,15 +57,15 @@ export default ({ selParamObj }) => {
         </div>
       )}
       {/* 设备 */}
-      {selParamObj.id !== 'all' && (
+      {selNavObj.id !== 'all' && (
         <div className={styles.right}>
           <div className={styles.title}>运行中的设备</div>
-          <div className={styles.onlyNum}>{selParamObj.num}</div>
+          <div className={styles.onlyNum}>{selNavObj.num}</div>
           <div className={styles.closeButton} onClick={showModal}>
             {/* <Button type="primary"  size={'large'}  icon={<Icon type="closeall"  style={{color:'red',fontSize:20}} />} style={{'background':'rgba(196, 196, 196, 0.2)',border:'none'}} >
               全部关闭
             </Button> */}
-            <Icon type="closeall" style={{ fontSize: 20 }} />
+            <Icon className="" type="closeall" style={{ fontSize: 20 }} />
             <span className={styles.text}>全部关闭</span>
           </div>
           <div className={styles.moreWrap}>
@@ -111,3 +100,5 @@ export default ({ selParamObj }) => {
     </>
   );
 };
+
+export default TopNavRight;

+ 0 - 148
src/pages/Equipment/index.jsx

@@ -1,148 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import { FormattedMessage } from 'umi';
-
-// import SearchInput from '@/tenants-ui/SearchInput';
-import PageHeader from '@/components/PageHeader';
-import TopNavigator from '../Environment/components/topNavigator';
-import DeviceModal from './components/deviceModal';
-
-import styles from './index.less';
-import { equipImageMap } from '@/config/image.js';
-import { Input } from 'antd';
-
-export default function Equipment() {
-  const {
-    airMapTop,
-    lampMapTop,
-    maptemp3,
-    maptemp4,
-    maptemp5,
-    envir_all,
-    equip_air,
-    equip_lamp,
-    equip_water,
-    envir_curtain,
-    envir_humidification,
-  } = equipImageMap;
-  const [searchText, setSearchText] = useState();
-  const [showModal, setShowModal] = useState(false);
-  const [mapImg, setMapImg] = useState(maptemp3);
-  const [explainImg, setExplainImg] = useState(null);
-
-  const [navigatorList] = useState([
-    {
-      name: '全部设备',
-      id: 'all',
-      src: envir_all,
-      num: 24.3,
-      color: '#E5574F',
-    },
-    {
-      name: '空调',
-      id: 'air',
-      src: equip_air,
-      num: 11,
-      color: '#5E8BCF',
-    },
-    {
-      name: '照明',
-      id: 'lamp',
-      src: equip_lamp,
-      num: 10,
-      color: '#FFE823',
-    },
-    {
-      name: '水机',
-      id: 'water',
-      src: equip_water,
-      num: 33,
-      color: '#9FB7CD',
-    },
-    {
-      name: '窗帘',
-      id: 'curtain',
-      src: envir_curtain,
-      num: 50,
-      color: '#E89E32',
-    },
-    {
-      name: '加湿器',
-      id: 'humidification',
-      src: envir_humidification,
-      num: 36,
-      color: '#2D553E',
-    },
-  ]);
-  const navigatorChange = (item) => {
-    //debugger;
-    if (item.id == 'all') {
-      setExplainImg(null);
-      setMapImg(maptemp3);
-    }
-    if (item.id == 'air') {
-      setExplainImg(airMapTop);
-      setMapImg(maptemp4);
-    }
-    if (item.id == 'lamp') {
-      setExplainImg(lampMapTop);
-      setMapImg(maptemp5);
-    }
-  };
-  const specialRoomClick = () => {
-    console.log('showChart');
-    setShowModal(true);
-  };
-  return (
-    <div>
-      <PageHeader
-        title={<FormattedMessage id="menu.equipment" />}
-        action={
-          //   <SearchInput
-          //     placeholder="搜索空间"
-          //     onSearch={(s) => {
-          //       setSearchText(s || undefined);
-          //     }}
-          //     onCancel={() => {
-          //       setSearchText();
-          //     }}
-          //   />
-          <Input.Search
-            size="large"
-            allowClear
-            style={{ width: '20%' }}
-            placeholder={'搜索空间'}
-            onSearch={(s) => {
-              setSearchText(s || undefined);
-            }}
-          />
-        }
-      />
-      <div style={{ marginTop: '26px' }}>
-        <TopNavigator
-          navigatorList={navigatorList}
-          type={'equipment'}
-          navigatorChange={navigatorChange}
-        ></TopNavigator>
-        <div className={styles.mapwrap}>
-          <div className={styles.top}>
-            <div className={styles.right}>
-              <img style={{ width: '100%' }} src={explainImg}></img>
-            </div>
-          </div>
-          <div className={styles.map}>
-            <div className={styles.specialRoom} onClick={specialRoomClick}></div>
-            <img src={mapImg}></img>
-          </div>
-        </div>
-      </div>
-
-      {showModal && (
-        <DeviceModal
-          onClose={() => {
-            setShowModal(false);
-          }}
-        ></DeviceModal>
-      )}
-    </div>
-  );
-}

+ 19 - 21
src/pages/Equipment/index.less

@@ -1,27 +1,25 @@
 .label {
   color: #c4c4c4;
 }
-.mapwrap {
-  .top {
-    box-sizing: border-box;
-    height: 47px;
-    padding-top: 10px;
-    padding-right: 20px;
-    .right {
-      float: right;
-      width: 210px;
+.maptop {
+  box-sizing: border-box;
+  height: 44px;
+  padding-top: 10px;
+  .right {
+    display: flex;
+    float: right;
+    .circle {
+      display: inline-block;
+      width: 10px;
+      height: 10px;
+      margin-right: 4px;
+      margin-left: 18px;
+      background: #5e8bcf;
+      border-radius: 10px;
     }
   }
-  .map {
-    position: relative;
-    width: 100%;
-    overflow: hidden;
-  }
-  .specialRoom {
-    position: absolute;
-    top: 0;
-    right: 0;
-    width: 180px;
-    height: 170px;
-  }
+}
+.mapwrap {
+  height: calc(100% - 280px);
+  overflow: hidden;
 }

+ 214 - 0
src/pages/Equipment/index.tsx

@@ -0,0 +1,214 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import { FormattedMessage } from 'umi';
+
+// import SearchInput from '@/tenants-ui/SearchInput';
+import PageHeader from '@/components/PageHeader';
+import Map from '@/components/map';
+import TopNavigator from '../Environment/components/topNavigator';
+import TopNavRight from './components/topNavRight';
+import DeviceModal from './components/deviceModal';
+
+import styles from './index.less';
+import mapstyles from '@/assets/css/map.less';
+
+// import './map.less';
+import { equipImageMap } from '@/config/image.js';
+import { Input } from 'antd';
+import Icon from '@/tenants-ui/Icon';
+import type { navigatorItem } from '@/pages/Environment/index';
+
+//import { getMapList } from '@/services/ant-design-pro/environment';
+
+const Environment: React.FC = () => {
+  const { envir_all, equip_air, equip_lamp, envir_curtain } = equipImageMap;
+  const [searchText, setSearchText] = useState<string>('');
+  const [showModal, setShowModal] = useState<boolean>(false);
+  const [navigatorList] = useState<navigatorItem[]>([
+    {
+      name: '全部设备',
+      id: 'all',
+      src: envir_all,
+      //color: '#E5574F',
+      color: '#E89E32',
+      colorStr: '',
+    },
+    {
+      name: '空调',
+      id: 'air',
+      src: equip_air,
+      num: 11,
+      color: '#5E8BCF',
+      colorStr: '94,139,207,',
+    },
+    {
+      name: '照明',
+      id: 'lamp',
+      src: equip_lamp,
+      num: 10,
+      color: '#FFE823',
+      colorStr: '255,232,35,',
+    },
+    {
+      name: '窗帘',
+      id: 'curtain',
+      src: envir_curtain,
+      num: 50,
+      color: '#E89E32',
+      colorStr: '232, 158, 50,',
+    },
+  ]);
+  //   const [mapList, setMapList] = useState<API.MapInfo[]>([]);
+  //   useEffect(() => {
+  //     getMapList({ floorId: 'sffsdfdsfdf', floorNumber: 22 }).then(function (res) {
+  //       var data: API.MapInfo[] = res.data || [];
+  //       data.forEach((item, index) => {});
+  //       setMapList(data);
+  //     });
+  //   }, []);
+  const [selNav, setSelNav] = useState<navigatorItem>(navigatorList[0]);
+  const navigatorChange = (item: navigatorItem) => {
+    //debugger;
+    if (item.id == 'all') {
+    }
+    if (item.id == 'air') {
+    }
+    if (item.id == 'lamp') {
+    }
+    setSelNav(item);
+  };
+  const showModalClick = () => {
+    console.log('showChart');
+    setShowModal(true);
+  };
+  //当前设备的状态 all全部开启 part部分开启 close全都关闭  还有类型type 包括air lamp curtain
+  //根据设备状态返回不同的 颜色
+  const getColorOpacity = (value: string, type?: string): string => {
+    //debugger;
+    let colorStr = '';
+    if (selNav.id == 'all') {
+      //全部设备时 colorStr赋值
+      var filterRes = navigatorList.filter(function (item) {
+        return item.id == type;
+      });
+      colorStr = (filterRes[0] || {}).colorStr;
+    } else {
+      colorStr = selNav.colorStr;
+    }
+    if (value == 'part') {
+      return 'rgba(' + colorStr + '0.4)';
+    } else if (value == 'all') {
+      return 'rgba(' + colorStr + '1)';
+    } else {
+      return 'rgba(196, 196, 196, 0.6)';
+    }
+  };
+  return (
+    <>
+      <PageHeader
+        title={<FormattedMessage id="menu.equipment" />}
+        action={
+          <Input.Search
+            size="large"
+            allowClear
+            style={{ width: '230px' }}
+            placeholder={'搜索空间'}
+            onSearch={(s) => {
+              setSearchText(s || undefined);
+            }}
+          />
+        }
+      />
+      <TopNavigator
+        navigatorList={navigatorList}
+        type={'equipment'}
+        navigatorChange={navigatorChange}
+        action={<TopNavRight selNavObj={selNav} navigatorList={navigatorList}></TopNavRight>}
+      ></TopNavigator>
+      <div className={styles.maptop}>
+        {selNav.id !== 'all' && (
+          <div className={styles.right}>
+            <div>
+              <span
+                className={styles.circle}
+                style={{ backgroundColor: 'rgba(' + selNav.colorStr + '1)' }}
+              ></span>
+              <span>开启</span>
+            </div>
+            <div>
+              <span
+                className={styles.circle}
+                style={{ backgroundColor: 'rgba(' + selNav.colorStr + '0.4)' }}
+              ></span>
+              <span>部分开启</span>
+            </div>
+            <div>
+              <span
+                className={styles.circle}
+                style={{ backgroundColor: 'rgba(196, 196, 196, 0.6)' }}
+              ></span>
+              <span>关闭</span>
+            </div>
+          </div>
+        )}
+      </div>
+      <div className={styles.mapwrap}>
+        <Map
+          type={'equipment'}
+          render={(item, index) => {
+            return (
+              <div
+                key={index + 'house'}
+                className={mapstyles.house}
+                style={{
+                  left: item.left,
+                  top: item.top,
+                  width: item.width,
+                  height: item.height,
+                  backgroundColor:
+                    selNav.id == 'all'
+                      ? 'rgba(196, 196, 196, 0.2)'
+                      : getColorOpacity(item[selNav.id]),
+                }}
+              >
+                {selNav.id == 'all' && (
+                  <div className={mapstyles.allDevice}>
+                    {/* 所有设备的图标列表 */}
+                    {item.device.map((ditem, dindex: number) => {
+                      return (
+                        <div
+                          key={dindex + 'device'}
+                          className={mapstyles.icon}
+                          style={{
+                            backgroundColor: getColorOpacity(ditem.status, ditem.type),
+                          }}
+                        >
+                          <Icon className="" type={ditem.type} style={{ color: '#ffffff' }}></Icon>
+                        </div>
+                      );
+                    })}
+                  </div>
+                )}
+                <div className={mapstyles.showModal} onClick={showModalClick}>
+                  <Icon className="" type="hover"></Icon>
+                </div>
+                <div className={mapstyles.content}>
+                  <Icon className="" type={item.roomFuncType} style={{ fontSize: 20 }}></Icon>
+                  <div className={mapstyles.name}>{item.localName}</div>
+                </div>
+              </div>
+            );
+          }}
+        ></Map>
+      </div>
+      {showModal && (
+        <DeviceModal
+          onClose={() => {
+            setShowModal(false);
+          }}
+        ></DeviceModal>
+      )}
+    </>
+  );
+};
+
+export default Environment;

+ 1 - 1
src/pages/Runtime/RecordList/index.less

@@ -1 +1 @@
-@import '/src/antd-tenants.less';
+// @import '/src/antd-tenants.less';

+ 0 - 49
src/pages/Runtime/components/topNavRight/index.jsx

@@ -1,49 +0,0 @@
-// 设备管理 导航条的右侧
-import React, { useState, useEffect } from 'react';
-
-import cx from 'classnames';
-import styles from './index.less';
-
-export default ({}) => {
-  const [allList] = useState([
-    {
-      name: '运行中',
-      id: 'run',
-      num: 7,
-    },
-    {
-      name: '加班/超时运行',
-      id: 'overtime',
-      num: 60,
-    },
-    {
-      name: '不在运行时间',
-      id: 'nottime',
-      num: 33,
-    },
-  ]);
-
-  return (
-    <>
-      <div className={styles.right}>
-        <div className={styles.allList}>
-          <div className={styles.item}>
-            <div className={cx(styles.run, styles.circle)}></div>
-            <div className={styles.title}>运行中</div>
-            <div className={styles.num}>22</div>
-          </div>
-          <div className={styles.item}>
-            <div className={cx(styles.overtime, styles.circle)}></div>
-            <div className={styles.title}>加班/超时运行</div>
-            <div className={styles.num}>5</div>
-          </div>
-          <div className={styles.item}>
-            <div className={cx(styles.nottime, styles.circle)}></div>
-            <div className={styles.title}>不在运行时间</div>
-            <div className={styles.num}>15</div>
-          </div>
-        </div>
-      </div>
-    </>
-  );
-};

+ 14 - 8
src/pages/Runtime/components/topNavRight/index.less

@@ -5,7 +5,7 @@
   justify-content: center;
   float: right;
   height: 100%;
-  padding-right: 46px;
+  //   padding-right: 46px;
   white-space: nowrap;
   .title {
     color: #c4c4c4;
@@ -13,25 +13,26 @@
   }
   .allList {
     display: flex;
+    height: 100%;
     .item {
       display: flex;
       align-items: center;
       justify-content: center;
       //width: 50px;
-      margin-left: 38px;
+      margin-right: 38px;
       .circle {
         width: 18px;
         height: 18px;
         border-radius: 100%;
       }
-      .run {
-        background: rgb(197, 247, 205);
+      .normalRun {
+        background: rgba(196, 196, 196, 0.2);
       }
-      .overtime {
-        background: rgb(45, 223, 70);
+      .overtimeWork {
+        background: rgba(94, 139, 207, 0.2);
       }
-      .nottime {
-        background: #f5f5f5;
+      .overtimeBook {
+        background: rgba(0, 220, 35, 0.2);
       }
       .title {
         margin: 0 10px;
@@ -44,4 +45,9 @@
       }
     }
   }
+  .date {
+    display: flex;
+    height: 100%;
+    border-left: 1px solid rgba(196, 196, 196, 0.4);
+  }
 }

+ 48 - 0
src/pages/Runtime/components/topNavRight/index.tsx

@@ -0,0 +1,48 @@
+// 设备管理 导航条的右侧
+import React, { useState, useEffect } from 'react';
+import { DatePicker } from 'antd';
+import cx from 'classnames';
+import styles from './index.less';
+import moment from 'moment';
+
+export type TopNavRightProps = {
+  runtimeNav: any;
+};
+
+const TopNavRight: React.FC<TopNavRightProps> = ({ runtimeNav }) => {
+  const dateFormat = 'YYYY/MM/DD';
+  return (
+    <>
+      <div className={styles.right}>
+        <div className={styles.allList}>
+          <div className={styles.item}>
+            <div className={cx(styles.normalRun, styles.circle)}></div>
+            <div className={styles.title}>正常运行</div>
+            <div className={styles.num}>{runtimeNav.normalRun}</div>
+          </div>
+          <div className={styles.item}>
+            <div className={cx(styles.overtimeWork, styles.circle)}></div>
+            <div className={styles.title}>加班</div>
+            <div className={styles.num}>{runtimeNav.overtimeWork}</div>
+          </div>
+          <div className={styles.item}>
+            <div className={cx(styles.overtimeBook, styles.circle)}></div>
+            <div className={styles.title}>会议预约</div>
+            <div className={styles.num}>{runtimeNav.overtimeBook}</div>
+          </div>
+        </div>
+        <div className={styles.date}>
+          <DatePicker
+            bordered={false}
+            defaultValue={moment('2015/01/01', dateFormat)}
+            format={dateFormat}
+            allowClear={false}
+            inputReadOnly={true}
+            autoFocus={true}
+          />
+        </div>
+      </div>
+    </>
+  );
+};
+export default TopNavRight;

+ 0 - 57
src/pages/Runtime/index.jsx

@@ -1,57 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import PageHeader from '@/components/PageHeader';
-import TopNavigator from '../Environment/components/topNavigator';
-import { FormattedMessage, history } from 'umi';
-import SearchInput from '@/tenants-ui/SearchInput';
-import styles from './index.less';
-import Icon from '@/tenants-ui/Icon';
-import { equipImageMap } from '@/config/image.js';
-
-export default function Runtime(props) {
-  const { maptemp6 } = equipImageMap;
-
-  const [searchText, setSearchText] = useState();
-  const [navigatorList] = useState([]);
-  const [mapImg, setMapImg] = useState(maptemp6);
-
-  const checkRecord = () => {
-    history.push('/runtime/recordList');
-    console.log('showR');
-  };
-  return (
-    <div>
-      <PageHeader
-        title={<FormattedMessage id="menu.runtime" />}
-        action={
-          <div className={styles.headerRight}>
-            <SearchInput
-              placeholder="搜索空间"
-              onSearch={(s) => {
-                setSearchText(s || undefined);
-              }}
-              onCancel={() => {
-                setSearchText();
-              }}
-            />
-            <div className={styles.check} onClick={checkRecord}>
-              <Icon style={{ marginRight: 6 }} type="document" />
-              <span>查看加班记录</span>
-            </div>
-          </div>
-        }
-      />
-      <div style={{ marginTop: '26px' }}>
-        <TopNavigator type={'runtime'}></TopNavigator>
-        <div className={styles.mapwrap}>
-          <div className={styles.top}>
-            <div className={styles.right}></div>
-          </div>
-          <div className={styles.map}>
-            <div className={styles.specialRoom}></div>
-            <img src={mapImg}></img>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}

+ 13 - 22
src/pages/Runtime/index.less

@@ -7,27 +7,18 @@
   }
 }
 
-.mapwrap {
-  .top {
-    box-sizing: border-box;
-    height: 47px;
-    padding-top: 10px;
-    padding-right: 20px;
-    .right {
-      float: right;
-      width: 210px;
-    }
-  }
-  .map {
-    position: relative;
-    width: 100%;
-    overflow: hidden;
-  }
-  .specialRoom {
-    position: absolute;
-    top: 0;
-    right: 0;
-    width: 180px;
-    height: 170px;
+.maptop {
+  box-sizing: border-box;
+  height: 44px;
+  padding-top: 10px;
+  padding-right: 20px;
+  .right {
+    float: right;
+    width: 210px;
   }
 }
+
+.mapwrap {
+  height: calc(100% - 280px);
+  overflow: hidden;
+}

+ 135 - 0
src/pages/Runtime/index.tsx

@@ -0,0 +1,135 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import PageHeader from '@/components/PageHeader';
+import Map from '@/components/map';
+import TopNavigator from '../Environment/components/topNavigator';
+import RunNavRight from './components/topNavRight';
+import { FormattedMessage, history } from 'umi';
+import { Input } from 'antd';
+import styles from './index.less';
+import mapstyles from '@/assets/css/map.less';
+import Icon from '@/tenants-ui/Icon';
+//import { equipImageMap } from '@/config/image.js';
+//import { getMapList } from '@/services/ant-design-pro/environment';
+
+type runtimeNavType = {
+  normalRun: number;
+  overtimeWork: number;
+  overtimeBook: number;
+};
+
+const Runtime: React.FC = () => {
+  // const { maptemp6 } = equipImageMap;
+
+  const [searchText, setSearchText] = useState<string>();
+  const [runtimeNav] = useState<runtimeNavType>({
+    normalRun: 22,
+    overtimeWork: 22,
+    overtimeBook: 22,
+  });
+  //const [mapList, setMapList] = useState<API.MapInfo[]>([]);
+
+  const checkRecord = () => {
+    history.push('/runtime/recordList');
+    console.log('showR');
+  };
+  //   useEffect(() => {
+  //     getMapList({ floorId: 'sffsdfdsfdf', floorNumber: 22 }).then(function (res) {
+  //       var data: API.MapInfo[] = res.data || [];
+  //       data.forEach((item, index) => {});
+  //       setMapList(data);
+  //     });
+  //   }, []);
+  return (
+    <>
+      <PageHeader
+        title={<FormattedMessage id="menu.runtime" />}
+        action={
+          <div className={styles.headerRight}>
+            {/* <SearchInput
+              placeholder="搜索空间"
+              onSearch={(s) => {
+                setSearchText(s || undefined);
+              }}
+              onCancel={() => {
+                setSearchText();
+              }}
+            /> */}
+            <Input.Search
+              size="large"
+              allowClear
+              style={{ width: '230px' }}
+              placeholder={'搜索空间'}
+              onSearch={(s) => {
+                setSearchText(s || undefined);
+              }}
+            />
+            <div className={styles.check} onClick={checkRecord} style={{ display: 'none' }}>
+              <Icon className="" style={{ marginRight: 6 }} type="document" />
+              <span>查看加班记录</span>
+            </div>
+          </div>
+        }
+      />
+      <TopNavigator
+        type={'runtime'}
+        action={<RunNavRight runtimeNav={runtimeNav}></RunNavRight>}
+      ></TopNavigator>
+      <div className={styles.maptop}>
+        <div className={styles.right}></div>
+      </div>
+      <div className={styles.mapwrap}>
+        {/* <div className={styles.map}>
+            <div className={styles.specialRoom}></div>
+            <img src={mapImg}></img>
+          </div> */}
+        {/* <div
+              className={mapstyles.house}
+              style={{
+                width: 180,
+                height: 100,
+              }}
+            >
+              <div className={mapstyles.allTime}>08:00-23:00</div>
+              <div className={mapstyles.content}>
+                <Icon className="" type="office" style={{ fontSize: 20 }}></Icon>
+                <div className={mapstyles.name}>Jozy Zone</div>
+              </div>
+            </div> */}
+        <Map
+          type={'runtime'}
+          render={(item, index) => {
+            return (
+              <div
+                key={index + 'house'}
+                className={mapstyles.house}
+                style={{
+                  left: item.left,
+                  top: item.top,
+                  width: item.width,
+                  height: item.height,
+                  backgroundColor:
+                    item.runTimeStatus == 'normalRun'
+                      ? 'rgba(196, 196, 196, 0.2)'
+                      : item.runTimeStatus == 'overtimeWork'
+                      ? 'rgba(94, 139, 207, 0.2)'
+                      : 'rgba(0, 220, 35, 0.2)',
+                }}
+              >
+                <div className={mapstyles.allTime}>
+                  {(item.timeList || []).map(function (titem: string, tindex: number) {
+                    return <span key={tindex + 'time'}>{titem},</span>;
+                  })}
+                </div>
+                <div className={mapstyles.content}>
+                  <Icon className="" type={item.roomFuncType} style={{ fontSize: 20 }}></Icon>
+                  <div className={mapstyles.name}>{item.localName}</div>
+                </div>
+              </div>
+            );
+          }}
+        ></Map>
+      </div>
+    </>
+  );
+};
+export default Runtime;

+ 35 - 0
src/services/ant-design-pro/environment.ts

@@ -0,0 +1,35 @@
+/** 登录接口 POST /api/login/account */
+import { request } from 'umi';
+
+export async function getMapList(body: any, options?: { [key: string]: any }) {
+  return request<API.MapList>('/api/environment/map', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    ...(options || {}),
+    data: body,
+  });
+}
+export async function getBuildingList(body: any, options?: { [key: string]: any }) {
+  // debugger;
+  return request<API.BuildFloorList>('/api/environment/Building', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    ...(options || {}),
+    data: body,
+  });
+}
+export async function getFloorList(body: any, options?: { [key: string]: any }) {
+  // debugger;
+  return request<API.BuildFloorList>('/api/environment/Floor', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    ...(options || {}),
+    data: body,
+  });
+}

+ 2 - 0
src/services/ant-design-pro/index.ts

@@ -4,7 +4,9 @@
 // API 唯一标识:
 import * as api from './api';
 import * as login from './login';
+import * as environment from './environment';
 export default {
   api,
   login,
+  environment,
 };

+ 24 - 0
src/services/ant-design-pro/typings.d.ts

@@ -98,4 +98,28 @@ declare namespace API {
     description?: string;
     type?: NoticeIconItemType;
   };
+
+  type MapInfo = {
+    left?: number;
+    top?: number;
+    width?: number;
+    height?: number;
+    value?: number;
+    name?: string;
+    type?: string;
+    [key: string]: any;
+  };
+  type MapList = {
+    data?: MapInfo[];
+    success?: boolean;
+  };
+  //楼层建筑都是这种类型
+  type BuildFloorItem = {
+    name: string;
+    id: string;
+  };
+  type BuildFloorList = {
+    data?: BuildFloorItem[];
+    success?: boolean;
+  };
 }

+ 1 - 1
src/tenants-ui/Icon/index.jsx

@@ -1,7 +1,7 @@
 import { createFromIconfontCN } from '@ant-design/icons';
 
 const Icon = createFromIconfontCN({
-  scriptUrl: '//at.alicdn.com/t/font_3184967_m3fl5qsc7m.js',
+  scriptUrl: '//at.alicdn.com/t/font_3184967_s7z4njiltfc.js',
 });
 
 export default ({ type, className, style = {} }) => {