lind пре 3 година
родитељ
комит
53eb940ca3

+ 12 - 1
src/pages/Log/index.tsx

@@ -1,10 +1,21 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import Access from '@/pages/Log/Access';
 import System from '@/pages/Log/System';
+import useLocation from '@/hooks/route/useLocation';
 
 const Log = () => {
   const [tab, setTab] = useState<string>('access');
+
+  const location = useLocation();
+
+  useEffect(() => {
+    const { state } = location;
+    if (state?.key) {
+      setTab(state?.key);
+    }
+  }, [location]);
+
   const list = [
     {
       key: 'access',

+ 4 - 0
src/pages/device/Instance/index.tsx

@@ -90,6 +90,10 @@ const Instance = () => {
           type: 'or',
         },
       ]);
+      if (location.state && location.state?.save) {
+        setVisible(true);
+        setCurrent({});
+      }
     }
   }, [location]);
 

+ 5 - 3
src/pages/device/Product/index.tsx

@@ -12,7 +12,7 @@ import {
 import Service from '@/pages/device/Product/service';
 import { observer } from '@formily/react';
 import { model } from '@formily/reactive';
-import { useHistory, useLocation } from 'umi';
+import { useHistory } from 'umi';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
@@ -25,6 +25,7 @@ import { downloadObject } from '@/utils/util';
 import { service as categoryService } from '@/pages/device/Category';
 import { service as deptService } from '@/pages/system/Department';
 import { omit } from 'lodash';
+import useLocation from '@/hooks/route/useLocation';
 
 export const service = new Service('device-product');
 export const statusMap = {
@@ -81,11 +82,12 @@ const Product = observer(() => {
   const location = useLocation();
 
   useEffect(() => {
-    if ((location as any).query?.save === 'true') {
+    const { state } = location;
+    if (state && state.save) {
       setCurrent(undefined);
       setVisible(true);
     }
-  }, []);
+  }, [location]);
 
   const deleteItem = async (id: string) => {
     const response: any = await service.remove(id);

+ 8 - 5
src/pages/home/components/Guide.tsx

@@ -20,7 +20,7 @@ interface GuideItemProps {
   name: string;
   english: string;
   url: string;
-  param: string;
+  param?: Record<string, any>;
   index?: number;
   auth: boolean;
 }
@@ -31,14 +31,14 @@ const GuideItem = (props: GuideItemProps) => {
 
   const jumpPage = () => {
     if (path && props.auth) {
-      history.push(`${path}${props.param}`);
+      history.push(`${path}`, props.param);
     } else {
       message.warning('暂无权限,请联系管理员');
     }
   };
 
   return (
-    <div className={'home-guide-item arrow'} onClick={jumpPage}>
+    <div className={'home-guide-item step-bar arrow-2'} onClick={jumpPage}>
       <div className={'item-english'}>{props.english}</div>
       <div className={'item-title'}>{props.name}</div>
       <div className={`item-index`}>
@@ -52,9 +52,12 @@ const Guide = (props: GuideProps) => {
   return (
     <div className={'home-guide'}>
       <Title title={props.title} />
-      <div className={'home-guide-items'}>
+      <div
+        className={'home-guide-items'}
+        style={{ gridTemplateColumns: `repeat(${props.data ? props.data.length : 1}, 1fr)` }}
+      >
         {props.data.map((item, index) => (
-          <GuideItem {...item} index={index + 1} />
+          <GuideItem {...item} index={index + 1} key={item.key} />
         ))}
       </div>
     </div>

+ 24 - 31
src/pages/home/components/Pie.tsx

@@ -1,44 +1,37 @@
-import * as echarts from 'echarts';
-import { useEffect, useRef } from 'react';
+import Echarts from '@/components/DashBoard/echarts';
+import { useEffect, useState } from 'react';
+import type { EChartsOption } from 'echarts';
 
 interface Props {
   value: number;
 }
 
 const Pie = (props: Props) => {
-  const myChart: any = useRef(null);
+  const [options, setOptions] = useState<EChartsOption>({});
 
   useEffect(() => {
-    const dom = document.getElementById('charts');
-    if (dom) {
-      const option = {
-        series: [
-          {
-            type: 'pie',
-            radius: [20, 40],
-            top: 0,
-            height: 70,
-            left: 'center',
-            width: 70,
-            itemStyle: {
-              borderColor: '#fff',
-              borderWidth: 1,
-            },
-            label: {
-              show: false,
-            },
-            labelLine: {
-              show: false,
-            },
-            data: [props.value, 100 - props.value],
+    setOptions({
+      color: ['#2F54EB', '#979AFF'],
+      series: [
+        {
+          type: 'pie',
+          radius: ['100%', '50%'],
+          center: ['50%', '50%'],
+          width: 80,
+          label: {
+            show: false,
           },
-        ],
-      };
-      myChart.current = myChart.current || echarts.init(dom);
-      myChart.current.setOption(option);
-    }
+          data: [props.value, 100 - props.value],
+        },
+      ],
+    });
   }, [props.value]);
-  return <div id="charts" style={{ width: '100%', height: 80 }}></div>;
+
+  return (
+    <div style={{ width: '100%', height: 80 }}>
+      <Echarts options={options} />
+    </div>
+  );
 };
 
 export default Pie;

+ 40 - 62
src/pages/home/components/Steps.tsx

@@ -1,67 +1,45 @@
-import { RightOutlined } from '@ant-design/icons';
-import { Card, Col, Row } from 'antd';
+import './index.less';
+import Title from '@/pages/home/components/Title';
+import React from 'react';
 
-const Steps = () => {
+interface StepItemProps {
+  title: string | React.ReactNode;
+  content: string | React.ReactNode;
+  onClick: () => void;
+  url?: string;
+}
+
+interface StepsProps {
+  title: string | React.ReactNode;
+  data: StepItemProps[];
+}
+
+const ItemDefaultImg = require('/public/images/home/bottom-1.png');
+const StepsItem = (props: StepItemProps) => {
+  return (
+    <div className={'step-item step-bar arrow-1'}>
+      <div className={'step-item-title'}>
+        <div className={'step-item-img'}>
+          <img src={props.url || ItemDefaultImg} />
+        </div>
+        <span>{props.title}</span>
+      </div>
+      <div className={'step-item-content'}>{props.content}</div>
+    </div>
+  );
+};
+
+const Steps = (props: StepsProps) => {
   return (
-    <Card title={'设备接入推荐步骤'}>
-      <Row gutter={24}>
-        <Col span={4}>
-          <Card
-            bordered
-            title="创建产品"
-            onClick={() => {
-              // pageJump(!!devicePermission.add, 'device/Instance')
-            }}
-          >
-            产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。
-          </Card>
-        </Col>
-        <Col span={1}>
-          <RightOutlined />
-        </Col>
-        <Col span={4}>
-          <Card bordered title="配置产品接入方式" onClick={() => {}}>
-            通过产品对同一类型的所有设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。
-          </Card>
-        </Col>
-        <Col span={1}>
-          <RightOutlined />
-        </Col>
-        <Col span={4}>
-          <Card
-            bordered
-            title="添加测试设备"
-            onClick={() => {
-              // pageJump(!!devicePermission.add, 'device/Instance')
-            }}
-          >
-            添加单个设备,用于验证产品模型是否配置正确。
-          </Card>
-        </Col>
-        <Col span={1}>
-          <RightOutlined />
-        </Col>
-        <Col span={4}>
-          <Card bordered title="功能调试" onClick={() => {}}>
-            对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。
-          </Card>
-        </Col>
-        <Col span={1}>
-          <RightOutlined />
-        </Col>
-        <Col span={4}>
-          <Card
-            bordered
-            title="批量添加设备"
-            onClick={() => {
-              // pageJump(!!devicePermission.add, 'device/Instance')
-            }}
-          >
-            批量添加同一产品下的设备
-          </Card>
-        </Col>
-      </Row>
-    </Card>
+    <div className={'home-step'}>
+      <Title title={props.title} />
+      <div
+        className={'home-step-items'}
+        style={{ gridTemplateColumns: `repeat(${props.data ? props.data.length : 1}, 1fr)` }}
+      >
+        {props.data && props.data.map((item) => <StepsItem {...item} />)}
+      </div>
+    </div>
   );
 };
 

+ 1 - 1
src/pages/home/components/Title.tsx

@@ -2,7 +2,7 @@ import classNames from 'classnames';
 import React from 'react';
 
 interface TitleProps {
-  title: string;
+  title: string | React.ReactNode;
   english?: string;
   className?: string;
   extra?: React.ReactNode | string;

+ 91 - 25
src/pages/home/components/index.less

@@ -3,40 +3,30 @@
 @bodyPadding: 24px 16px;
 @margin: 24px;
 
+.home-base {
+  position: relative;
+  padding: @bodyPadding;
+  background-color: #fff;
+}
+
 .home-guide {
   margin-bottom: @margin;
   padding: @bodyPadding;
   background-color: #fff;
 
   .home-guide-items {
-    display: flex;
-    gap: 56px;
+    display: grid;
+    grid-column-gap: 56px;
   }
 }
 
 .home-guide-item {
   position: relative;
-  flex-grow: 1;
   padding: 16px;
   background: linear-gradient(135.62deg, #f6f7fd 22.27%, rgba(255, 255, 255, 0.86) 91.82%);
   border-radius: 2px;
   box-shadow: 0 4px 18px #efefef;
 
-  &.arrow {
-    &:not(:last-child) {
-      &::after {
-        position: absolute;
-        top: 50%;
-        right: -60px;
-        width: 60px;
-        height: 40px;
-        background: url('/images/home/arrow-2.png') no-repeat center;
-        transform: translateY(-50%);
-        content: ' ';
-      }
-    }
-  }
-
   .item-english {
     color: #4f4f4f;
   }
@@ -86,12 +76,11 @@
 }
 
 .home-body {
-  position: relative;
+  .home-base;
+
   height: 500px;
   margin-bottom: @margin;
-  padding: @bodyPadding;
   overflow: hidden;
-  background-color: #fff;
 
   .home-body-img {
     position: absolute;
@@ -103,12 +92,89 @@
 }
 
 .home-statistics {
-  position: relative;
-  padding: @bodyPadding;
-  background-color: #fff;
+  .home-base;
 
   .home-statistics-body {
-    display: flex;
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
     gap: 24px;
   }
 }
+
+.step-item-after {
+  position: absolute;
+  top: 50%;
+  right: -60px;
+  width: 60px;
+  height: 40px;
+  transform: translateY(-50%);
+  content: ' ';
+}
+
+.home-step {
+  .home-base;
+
+  .home-step-items {
+    display: grid;
+    grid-column-gap: 66px;
+
+    .step-item {
+      display: flex;
+      flex-direction: column;
+
+      .step-item-title {
+        position: relative;
+        padding: 16px 24px;
+        color: #333;
+        font-weight: bold;
+        font-size: 20px;
+        background-color: #f8f9fd;
+
+        .step-item-img {
+          position: absolute;
+          top: 0;
+          right: 0;
+          z-index: 1;
+        }
+
+        > span {
+          position: relative;
+          z-index: 2;
+        }
+      }
+
+      .step-item-content {
+        flex-grow: 1;
+        height: auto;
+        padding: 24px;
+        border-right: 1px solid #e5edf4;
+        border-bottom: 1px solid #e5edf4;
+        border-left: 1px solid #e5edf4;
+      }
+    }
+  }
+}
+
+.step-bar {
+  position: relative;
+
+  &.arrow-1 {
+    &:not(:last-child) {
+      &::after {
+        .step-item-after;
+
+        background: url('/images/home/arrow-1.png') no-repeat center;
+      }
+    }
+  }
+
+  &.arrow-2 {
+    &:not(:last-child) {
+      &::after {
+        .step-item-after;
+
+        background: url('/images/home/arrow-2.png') no-repeat center;
+      }
+    }
+  }
+}

+ 34 - 24
src/pages/home/comprehensive/index.tsx

@@ -5,7 +5,7 @@ import { Col, message, Row } from 'antd';
 import Body from '../components/Body';
 import Guide from '../components/Guide';
 import Statistics from '../components/Statistics';
-import Steps from '../components/Steps';
+// import Steps from '../components/Steps';
 import { service } from '..';
 import { useEffect, useState } from 'react';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
@@ -17,6 +17,9 @@ const Comprehensive = () => {
   const productPermission = PermissionButton.usePermission('device/Product').permission;
   const devicePermission = PermissionButton.usePermission('device/Instance').permission;
   const rulePermission = PermissionButton.usePermission('rule-engine/Instance').permission;
+  const accessPermission = getMenuPathByCode(MENUS_CODE['link/AccessConfig']);
+  const logPermission = getMenuPathByCode(MENUS_CODE['Log']);
+  const linkPermission = getMenuPathByCode(MENUS_CODE['link/DashBoard']);
 
   const [productCount, setProductCount] = useState<number>(0);
   const [deviceCount, setDeviceCount] = useState<number>(0);
@@ -37,7 +40,6 @@ const Comprehensive = () => {
     }
   };
 
-  // websocket
   useEffect(() => {
     getProductCount();
     getDeviceCount();
@@ -80,7 +82,6 @@ const Comprehensive = () => {
 
   const history = useHistory();
   // // 跳转
-
   const guideList = [
     {
       key: 'product',
@@ -88,7 +89,9 @@ const Comprehensive = () => {
       english: 'CREATE PRODUCT',
       auth: !!productPermission.add,
       url: 'device/Product',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
     {
       key: 'device',
@@ -96,7 +99,9 @@ const Comprehensive = () => {
       english: 'CREATE DEVICE',
       auth: !!devicePermission.add,
       url: 'device/Instance',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
     {
       key: 'rule-engine',
@@ -104,34 +109,39 @@ const Comprehensive = () => {
       english: 'RULE ENGINE',
       auth: !!rulePermission.add,
       url: 'rule-engine/Instance',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
   ];
 
   const guideOpsList = [
     {
-      key: 'product',
+      key: 'access',
       name: '设备接入配置',
-      english: 'CREATE PRODUCT',
-      auth: !!productPermission.add,
-      url: 'device/Product',
-      param: '?save=true',
+      english: 'DEVICE ACCESS CONFIGURATION',
+      auth: !!accessPermission,
+      url: 'link/AccessConfig',
     },
     {
-      key: 'device',
+      key: 'logger',
       name: '日志排查',
-      english: 'CREATE DEVICE',
-      auth: !!devicePermission.add,
-      url: 'device/Instance',
-      param: '?save=true',
+      english: 'LOG SCREEN',
+      auth: !!logPermission,
+      url: 'Log',
+      param: {
+        key: 'system',
+      },
     },
     {
-      key: 'rule-engine',
+      key: 'realtime',
       name: '实时监控',
-      english: 'RULE ENGINE',
-      auth: !!rulePermission.add,
-      url: 'rule-engine/Instance',
-      param: '?save=true',
+      english: 'REAL-TIME MONITORING',
+      auth: !!linkPermission,
+      url: 'link/DashBoard',
+      param: {
+        save: true,
+      },
     },
   ];
 
@@ -195,7 +205,7 @@ const Comprehensive = () => {
             <div style={{ fontSize: 14, fontWeight: 400 }}>
               <a
                 onClick={() => {
-                  const url = getMenuPathByCode(MENUS_CODE['device/DashBoard']);
+                  const url = getMenuPathByCode(MENUS_CODE['link/DashBoard']);
                   if (!!url) {
                     history.push(`${url}`);
                   } else {
@@ -212,12 +222,12 @@ const Comprehensive = () => {
       <Col span={24}>
         <Body title={'平台架构图'} english={'PLATFORM ARCHITECTURE DIAGRAM'} />
       </Col>
-      <Col span={24}>
+      {/* <Col span={24}>
         <Steps />
       </Col>
       <Col span={24}>
         <Steps />
-      </Col>
+      </Col> */}
     </Row>
   );
 };

+ 50 - 27
src/pages/home/device/index.tsx

@@ -1,4 +1,4 @@
-import { Col, message, Row } from 'antd';
+import { Col, message, Row, Tooltip } from 'antd';
 import { PermissionButton } from '@/components';
 import { Body, Guide } from '../components';
 import Statistics from '../components/Statistics';
@@ -7,6 +7,7 @@ import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { useHistory } from '@/hooks';
 import { service } from '..';
 import { useEffect, useState } from 'react';
+import { QuestionCircleOutlined } from '@ant-design/icons';
 
 const Device = () => {
   const productPermission = PermissionButton.usePermission('device/Product').permission;
@@ -45,7 +46,9 @@ const Device = () => {
       english: 'CREATE PRODUCT',
       auth: !!productPermission.add,
       url: 'device/Product',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
     {
       key: 'device',
@@ -53,7 +56,9 @@ const Device = () => {
       english: 'CREATE DEVICE',
       auth: !!devicePermission.add,
       url: 'device/Instance',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
     {
       key: 'rule-engine',
@@ -61,32 +66,12 @@ const Device = () => {
       english: 'RULE ENGINE',
       auth: !!rulePermission.add,
       url: 'rule-engine/Instance',
-      param: '?save=true',
+      param: {
+        save: true,
+      },
     },
   ];
 
-  // const statisticsList = [{
-  //   key: 'product',
-  //   name: '1、创建产品',
-  //   auth: !!productPermission.add,
-  //   url: 'device/Product',
-  //   param: "?save=true"
-  // }, {
-  //   key: 'device',
-  //   name: '2、创建设备',
-  //   auth: !!devicePermission.add,
-  //   url: 'device/Instance',
-  //   param: "?save=true"
-  // },
-  // {
-  //   key: 'rule-engine',
-  //   name: '3、规则引擎',
-  //   auth: !!rulePermission.add,
-  //   url: 'rule-engine/Instance',
-  //   param: "?save=true"
-  // }
-  // ];
-
   return (
     <Row gutter={24}>
       <Col span={14}>
@@ -135,7 +120,45 @@ const Device = () => {
         <Body title={'平台架构图'} english={'PLATFORM ARCHITECTURE DIAGRAM'} />
       </Col>
       <Col span={24}>
-        <Steps />
+        <Steps
+          title={
+            <span>
+              设备接入推荐步骤
+              <Tooltip title={'不同的设备因为通信协议的不用,存在接入步骤的差异'}>
+                <QuestionCircleOutlined style={{ paddingLeft: 12 }} />
+              </Tooltip>
+            </span>
+          }
+          data={[
+            {
+              title: '创建产品',
+              content:
+                '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
+              onClick: () => {},
+            },
+            {
+              title: '配置产品接入方式',
+              content:
+                '通过产品对同一类型的所有设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
+              onClick: () => {},
+            },
+            {
+              title: '添加测试设备',
+              content: '添加单个设备,用于验证产品模型是否配置正确。',
+              onClick: () => {},
+            },
+            {
+              title: '功能调试',
+              content: '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
+              onClick: () => {},
+            },
+            {
+              title: '批量添加设备',
+              content: '批量添加同一产品下的设备',
+              onClick: () => {},
+            },
+          ]}
+        />
       </Col>
     </Row>
   );

+ 12 - 1
src/pages/rule-engine/Instance/index.tsx

@@ -1,7 +1,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import Service from '@/pages/rule-engine/Instance/serivce';
 import type { InstanceItem } from '@/pages/rule-engine/Instance/typings';
-import { useRef, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import {
   DeleteOutlined,
@@ -19,6 +19,7 @@ import RuleInstanceCard from '@/components/ProTableCard/CardItems/ruleInstance';
 import Save from '@/pages/rule-engine/Instance/Save';
 import SystemConst from '@/utils/const';
 import { StatusColorEnum } from '@/components/BadgeStatus';
+import useLocation from '@/hooks/route/useLocation';
 
 export const service = new Service('rule-engine/instance');
 
@@ -30,6 +31,16 @@ const Instance = () => {
   const [searchParams, setSearchParams] = useState<any>({});
   const { permission } = PermissionButton.usePermission('rule-engine/Instance');
 
+  const location = useLocation();
+
+  useEffect(() => {
+    const { state } = location;
+    if (state && state.save) {
+      setCurrent({});
+      setVisible(true);
+    }
+  }, [location]);
+
   const tools = (record: InstanceItem) => [
     <PermissionButton
       isPermission={permission.update}