lind 3 лет назад
Родитель
Сommit
2bd1e8aaf9

+ 4 - 4
config/proxy.ts

@@ -12,11 +12,11 @@ export default {
       // target: 'http://192.168.32.44:8844/',
       // ws: 'ws://192.168.32.44:8844/',
       // 开发环境
-      // target: 'http://120.79.18.123:8844/',
-      // ws: 'ws://120.79.18.123:8844/',
+      target: 'http://120.79.18.123:8844/',
+      ws: 'ws://120.79.18.123:8844/',
       // 测试环境
-      target: 'http://120.77.179.54:8844/',
-      ws: 'ws://120.77.179.54:8844/',
+      // target: 'http://120.77.179.54:8844/',
+      // ws: 'ws://120.77.179.54:8844/',
       // target: 'http://192.168.66.5:8844/',
       // ws: 'ws://192.168.66.5:8844/',
       // ws: 'ws://demo.jetlinks.cn/jetlinks',

BIN
public/images/device/device-number.png


BIN
public/images/device/device-product.png


BIN
public/images/home/1.png


BIN
public/images/home/2.png


BIN
public/images/home/3.png


BIN
public/images/home/arrow-1.png


BIN
public/images/home/arrow-2.png


BIN
public/images/home/bottom-1.png


BIN
public/images/home/bottom-2.png


BIN
public/images/home/bottom-3.png


BIN
public/images/home/bottom-4.png


BIN
public/images/home/bottom-5.png


BIN
public/images/home/bottom-6.png


BIN
public/images/home/bottom-7.png


BIN
public/images/home/bottom-8.png


BIN
public/images/home/content.png


BIN
public/images/home/top-1.png


BIN
public/images/home/top-2.png


BIN
public/images/home/top-bg.png


+ 0 - 1
src/components/AMapComponent/PathSimplifier/index.tsx

@@ -34,7 +34,6 @@ const PathSimplifier = (props: PathSimplifierProps) => {
       }
 
       if (props.pathData) {
-        console.log(props.pathData.map((item) => ({ name: item.name || '路线', path: item.path })));
         pathSimplifierRef.current?.setData(
           props.pathData.map((item) => ({ name: item.name || '路线', path: item.path })),
         );

+ 141 - 88
src/pages/device/DashBoard/index.tsx

@@ -1,11 +1,11 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { Badge, Card } from 'antd';
+import { Card } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import './index.less';
 import Service from './service';
 import encodeQuery from '@/utils/encodeQuery';
 import { useRequest } from 'umi';
-import DashBoard from '@/components/DashBoard';
+import DashBoard, { DashBoardTopCard } from '@/components/DashBoard';
 import type { EChartsOption } from 'echarts';
 import Echarts from '@/components/DashBoard/echarts';
 import moment from 'moment';
@@ -13,44 +13,11 @@ import { AMap } from '@/components';
 import { Marker } from 'react-amap';
 import { EnvironmentOutlined } from '@ant-design/icons';
 
-interface TopCardProps {
-  isEcharts: boolean;
-  title: string;
-  total?: number | string;
-  topRender?: any;
-  bottomRender: () => React.ReactNode;
-}
-
 type RefType = {
   getValues: Function;
 };
 
 const service = new Service('device/instance');
-const TopCard = (props: TopCardProps) => {
-  return (
-    <div className={'top-card-item'}>
-      {props.isEcharts ? (
-        <div className={'top-card-top'}>
-          <div className={'top-card-top-charts'}>
-            <div>{props.title}</div>
-            <div className={'top-card-top-charts-total'}>{props.total}</div>
-            <div style={{ height: 45, width: '100%' }}>{props.topRender}</div>
-          </div>
-        </div>
-      ) : (
-        <div className={'top-card-top'}>
-          <div className={'top-card-top-left'}></div>
-          <div className={'top-card-top-right'}>
-            <div className={'top-card-title'}>{props.title}</div>
-            <div className={'top-card-total'}>{props.total}</div>
-          </div>
-        </div>
-      )}
-
-      <div className={'top-card-bottom'}>{props.bottomRender()}</div>
-    </div>
-  );
-};
 
 const DeviceBoard = () => {
   const [deviceOnline, setDeviceOnline] = useState(0);
@@ -62,6 +29,7 @@ const DeviceBoard = () => {
   const [yesterdayCount, setYesterdayCount] = useState(0);
   const [deviceOptions, setDeviceOptions] = useState<EChartsOption>({});
   const [month, setMonth] = useState(0);
+  const [day, setDay] = useState(0);
   const [point, setPoint] = useState([]);
 
   const ref = useRef<RefType>();
@@ -143,10 +111,24 @@ const DeviceBoard = () => {
           type: 'value',
           show: false,
         },
+        grid: {
+          top: '2%',
+          bottom: 0,
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow',
+          },
+        },
         series: [
           {
+            name: '在线数',
             data: y,
             type: 'bar',
+            itemStyle: {
+              color: '#2F54EB',
+            },
           },
         ],
       });
@@ -173,6 +155,18 @@ const DeviceBoard = () => {
         object: 'message',
         measurement: 'quantity',
         dimension: 'agg',
+        group: 'oneday',
+        params: {
+          time: '1d',
+          format: 'yyyy-MM-dd',
+          from: 'now-1d',
+        },
+      },
+      {
+        dashboard: 'device',
+        object: 'message',
+        measurement: 'quantity',
+        dimension: 'agg',
         group: 'thisMonth',
         params: {
           time: '1M',
@@ -184,11 +178,19 @@ const DeviceBoard = () => {
     ]);
     if (res.status === 200) {
       const thisMonth = res.result.find((item: any) => item.group === 'thisMonth').data.value;
+      const oneDay = res.result.find((item: any) => item.group === 'oneday').data.value;
+      setDay(oneDay);
       setMonth(thisMonth);
-      const today = res.result.filter((item: any) => item.group !== 'thisMonth');
+      const today = res.result.filter((item: any) => item.group === 'today');
       const x = today.map((item: any) => item.data.timeString);
       const y = today.map((item: any) => item.data.value);
       setDeviceOptions({
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow',
+          },
+        },
         xAxis: {
           type: 'category',
           boundaryGap: false,
@@ -199,11 +201,37 @@ const DeviceBoard = () => {
           type: 'value',
           show: false,
         },
+        grid: {
+          top: '2%',
+          bottom: 0,
+        },
         series: [
           {
+            name: '消息量',
             data: y,
             type: 'line',
-            areaStyle: {},
+            smooth: true,
+            color: '#685DEB',
+            areaStyle: {
+              color: {
+                type: 'linear',
+                x: 0,
+                y: 0,
+                x2: 0,
+                y2: 1,
+                colorStops: [
+                  {
+                    offset: 0,
+                    color: '#685DEB', // 100% 处的颜色
+                  },
+                  {
+                    offset: 1,
+                    color: '#FFFFFF', //   0% 处的颜色
+                  },
+                ],
+                global: false, // 缺省为 false
+              },
+            },
           },
         ],
       });
@@ -236,7 +264,6 @@ const DeviceBoard = () => {
         setOptions({
           xAxis: {
             type: 'category',
-            boundaryGap: false,
             data: x,
           },
           yAxis: {
@@ -245,8 +272,11 @@ const DeviceBoard = () => {
           series: [
             {
               data: y,
-              type: 'line',
-              areaStyle: {},
+              type: 'bar',
+              barMaxWidth: 20,
+              itemStyle: {
+                color: '#2F54EB',
+              },
             },
           ],
         });
@@ -273,67 +303,90 @@ const DeviceBoard = () => {
   return (
     <PageContainer>
       <div className={'device-dash-board'}>
-        <Card className={'top-card-items'} bodyStyle={{ display: 'flex', gap: 12 }}>
-          <TopCard
+        <DashBoardTopCard>
+          <DashBoardTopCard.Item
             title={'产品数量'}
-            total={productTotal}
-            isEcharts={false}
-            bottomRender={() => (
-              <>
-                <Badge status="success" text="已发布" />
-                <span style={{ padding: '0 4px' }}>{productPublish}</span>
-                <Badge status="error" text="未发布" />{' '}
-                <span style={{ padding: '0 4px' }}>{productUnPublish}</span>
-              </>
-            )}
-          />
-          <TopCard
+            value={productTotal}
+            footer={[
+              {
+                title: '已发布',
+                value: productPublish,
+                status: 'success',
+              },
+              {
+                title: '未发布',
+                value: productUnPublish,
+                status: 'error',
+              },
+            ]}
+            span={6}
+          >
+            <img src={require('/public/images/device/device-product.png')} />
+          </DashBoardTopCard.Item>
+          <DashBoardTopCard.Item
             title={'设备数量'}
-            total={deviceTotal}
-            isEcharts={false}
-            bottomRender={() => (
-              <>
-                <Badge status="success" text="在线" />{' '}
-                <span style={{ padding: '0 4px' }}>{deviceOnline}</span>
-                <Badge status="error" text="离线" />{' '}
-                <span style={{ padding: '0 4px' }}>{deviceOffline}</span>
-              </>
-            )}
-          />
-          <TopCard
+            value={deviceTotal}
+            footer={[
+              {
+                title: '在线',
+                value: deviceOnline,
+                status: 'success',
+              },
+              {
+                title: '离线',
+                value: deviceOffline,
+                status: 'error',
+              },
+            ]}
+            span={6}
+          >
+            <img src={require('/public/images/device/device-number.png')} />
+          </DashBoardTopCard.Item>
+          <DashBoardTopCard.Item
             title={'当前在线'}
-            total={22}
-            isEcharts={true}
-            topRender={
-              <div style={{ height: 56 }}>
-                <Echarts options={onlineOptions} />
-              </div>
-            }
-            bottomRender={() => <>昨日在线:{yesterdayCount} </>}
-          />
-          <TopCard
+            value={deviceOnline}
+            footer={[
+              {
+                title: '昨日在线',
+                value: yesterdayCount,
+              },
+            ]}
+            span={6}
+          >
+            <Echarts options={onlineOptions} />
+          </DashBoardTopCard.Item>
+          <DashBoardTopCard.Item
             title={'今日设备消息量'}
-            total={2221}
-            isEcharts={true}
-            topRender={
-              <div style={{ height: 56 }}>
-                <Echarts options={deviceOptions} />
-              </div>
-            }
-            bottomRender={() => <>当月设备消息量:{month} </>}
-          />
-        </Card>
+            value={day}
+            footer={[
+              {
+                title: '当月设备消息量',
+                value: month,
+              },
+            ]}
+            span={6}
+          >
+            <Echarts options={deviceOptions} />
+          </DashBoardTopCard.Item>
+        </DashBoardTopCard>
         <DashBoard
           title={'设备消息'}
           options={options}
-          // closeInitialParams={true}
           ref={ref}
           height={500}
           defaultTime={'week'}
           onParamsChange={getEcharts}
         />
         <Card style={{ marginTop: 10 }}>
-          <div>设备分布</div>
+          <div
+            style={{
+              fontSize: '16px',
+              fontWeight: 'bold',
+              marginBottom: 10,
+            }}
+          >
+            设备分布
+          </div>
           <div>
             <AMap
               AMapUI

+ 22 - 0
src/pages/home/components/Body.tsx

@@ -0,0 +1,22 @@
+import Title from '@/pages/home/components/Title';
+import './index.less';
+import classNames from 'classnames';
+
+interface BodyProps {
+  title: string;
+  english: string;
+  className?: string;
+  url?: string;
+}
+
+const defaultUrl = require('/public/images/home/content.png');
+export default (props: BodyProps) => {
+  return (
+    <div className={classNames('home-body', props.className)}>
+      <div className={'home-body-img'}>
+        <img src={props.url || defaultUrl} />
+      </div>
+      <Title title={props.title} english={props.english} />
+    </div>
+  );
+};

+ 55 - 25
src/pages/home/components/Guide.tsx

@@ -1,36 +1,66 @@
-import { Card, Col, Row } from 'antd';
+import './index.less';
+import { getMenuPathByCode } from '@/utils/menu';
+import { message } from 'antd';
+import useHistory from '@/hooks/route/useHistory';
+import Title from './Title';
 
-interface Props {
+const Image = {
+  1: require('/public/images/home/1.png'),
+  2: require('/public/images/home/2.png'),
+  3: require('/public/images/home/3.png'),
+};
+
+interface GuideProps {
   title: string;
-  data: any[];
-  jump?: (auth: boolean, url: string, param: string) => void;
+  data: GuideItemProps[];
+}
+
+interface GuideItemProps {
+  key: string;
+  name: string;
+  english: string;
+  url: string;
+  param: string;
+  index?: number;
+  auth: boolean;
 }
 
-const Guide = (props: Props) => {
-  const { title, data, jump } = props;
+const GuideItem = (props: GuideItemProps) => {
+  const path = getMenuPathByCode(props.url);
+  const history = useHistory();
+
+  const jumpPage = () => {
+    if (path && props.auth) {
+      history.push(`${path}${props.param}`);
+    } else {
+      message.warning('暂无权限,请联系管理员');
+    }
+  };
+
   return (
-    <Card>
-      <div style={{ marginBottom: 15 }}>
-        <h3>{title}</h3>
+    <div className={'home-guide-item arrow'} onClick={jumpPage}>
+      <div className={'item-english'}>{props.english}</div>
+      <div className={'item-title'}>{props.name}</div>
+      <div className={`item-index`}>
+        <img src={Image[props.index!]} />
       </div>
-      <Row gutter={24}>
-        {data.map((item) => (
-          <Col key={item.key} span={8}>
-            <Card
-              bordered
-              onClick={() => {
-                if (jump) {
-                  jump(item.auth, item.url, item.param);
-                }
-              }}
-            >
-              {item.name}
-            </Card>
-          </Col>
+    </div>
+  );
+};
+
+const Guide = (props: GuideProps) => {
+  return (
+    <div className={'home-guide'}>
+      <Title title={props.title} />
+      <div className={'home-guide-items'}>
+        {props.data.map((item, index) => (
+          <GuideItem {...item} index={index + 1} />
         ))}
-      </Row>
-    </Card>
+      </div>
+    </div>
   );
 };
 
+Guide.Item = GuideItem;
+
 export default Guide;

+ 31 - 23
src/pages/home/components/Statistics.tsx

@@ -1,28 +1,36 @@
-import { Card, Col, Row } from 'antd';
+import Title from '@/pages/home/components/Title';
+import React from 'react';
+import './index.less';
 
-const Statistics = () => {
+type StatisticsItem = {
+  name: string;
+  value: number;
+  img: string;
+};
+
+interface StatisticsProps {
+  extra?: React.ReactNode | string;
+  data: StatisticsItem[];
+}
+
+const defaultImage = require('/public/images/home/top-1.png');
+
+const Statistics = (props: StatisticsProps) => {
   return (
-    <Card
-      title={'设备统计'}
-      extra={
-        <a
-          onClick={() => {
-            // pageJump(!!getMenuPathByCode('device/DashBoard'), 'device/DashBoard');
-          }}
-        >
-          详情
-        </a>
-      }
-    >
-      <Row gutter={24}>
-        <Col span={12}>
-          <Card bordered>产品数量</Card>
-        </Col>
-        <Col span={12}>
-          <Card bordered>设备数量</Card>
-        </Col>
-      </Row>
-    </Card>
+    <div className={'home-statistics'}>
+      <Title title={'设备统计'} extra={props.extra} />
+      <div className={'home-statistics-body'}>
+        {props.data.map((item) => (
+          <div className={'home-guide-item'}>
+            <div className={'item-english'}>{item.name}</div>
+            <div className={'item-title'}>{item.value}</div>
+            <div className={`item-index`}>
+              <img src={item.img || defaultImage} />
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
   );
 };
 

+ 19 - 0
src/pages/home/components/Title.tsx

@@ -0,0 +1,19 @@
+import classNames from 'classnames';
+import React from 'react';
+
+interface TitleProps {
+  title: string;
+  english?: string;
+  className?: string;
+  extra?: React.ReactNode | string;
+}
+
+export default (props: TitleProps) => {
+  return (
+    <div className={classNames('home-title', props.className)}>
+      <span>{props.title}</span>
+      <div>{props.extra}</div>
+      {props.english && <div className={'home-title-english'}>{props.english}</div>}
+    </div>
+  );
+};

+ 114 - 0
src/pages/home/components/index.less

@@ -0,0 +1,114 @@
+@import '~antd/es/style/themes/default.less';
+
+@bodyPadding: 24px 16px;
+@margin: 24px;
+
+.home-guide {
+  margin-bottom: @margin;
+  padding: @bodyPadding;
+  background-color: #fff;
+
+  .home-guide-items {
+    display: flex;
+    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;
+  }
+
+  .item-title {
+    margin: 20px 0;
+    color: @text-color;
+    font-weight: 700;
+    font-size: 20px;
+  }
+
+  .item-index {
+    position: absolute;
+    right: 10%;
+    bottom: 0;
+  }
+}
+
+.home-title {
+  position: relative;
+  z-index: 2;
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 12px;
+  padding-left: 18px;
+  font-weight: bold;
+  font-size: 18px;
+
+  &::after {
+    position: absolute;
+    top: 50%;
+    left: 0;
+    width: 8px;
+    height: 8px;
+    background-color: @primary-color;
+    border: 1px solid #b4c0da;
+    transform: translateY(-50%);
+    content: ' ';
+  }
+
+  .home-title-english {
+    position: absolute;
+    top: 30px;
+    color: rgba(0, 0, 0, 0.3);
+    font-size: 12px;
+  }
+}
+
+.home-body {
+  position: relative;
+  height: 500px;
+  margin-bottom: @margin;
+  padding: @bodyPadding;
+  overflow: hidden;
+  background-color: #fff;
+
+  .home-body-img {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 1;
+    height: 100%;
+  }
+}
+
+.home-statistics {
+  position: relative;
+  padding: @bodyPadding;
+  background-color: #fff;
+
+  .home-statistics-body {
+    display: flex;
+    gap: 24px;
+  }
+}

+ 4 - 0
src/pages/home/components/index.ts

@@ -0,0 +1,4 @@
+export { default as Guide } from './Guide';
+export { default as Title } from './Title';
+export { default as Statistics } from './Statistics';
+export { default as Body } from './Body';

+ 28 - 30
src/pages/home/device/index.tsx

@@ -1,7 +1,6 @@
-import { Card, Col, message, Row } from 'antd';
+import { Col, Row } from 'antd';
 import { PermissionButton } from '@/components';
-import { getMenuPathByCode } from '@/utils/menu';
-import Guide from '../components/Guide';
+import { Body, Guide } from '../components';
 import Statistics from '../components/Statistics';
 import Steps from '../components/Steps';
 
@@ -10,40 +9,28 @@ const Device = () => {
   const devicePermission = PermissionButton.usePermission('device/Instance').permission;
   const rulePermission = PermissionButton.usePermission('rule-engine/Instance').permission;
   // // 跳转
-  const pageJump = (auth: boolean, url: string, param: string) => {
-    if (auth) {
-      // 判断是否有权限
-      const path = getMenuPathByCode(url);
-      if (path) {
-        const tab: any = window.open(`${origin}/#${path}${param}`);
-        tab!.onTabSaveSuccess = () => {
-          // if (value.status === 200) {
-          // }
-        };
-      }
-    } else {
-      message.error('暂无权限,请联系管理员');
-    }
-  };
 
   const guideList = [
     {
       key: 'product',
-      name: '1、创建产品',
+      name: '创建产品',
+      english: 'CREATE PRODUCT',
       auth: !!productPermission.add,
       url: 'device/Product',
       param: '?save=true',
     },
     {
       key: 'device',
-      name: '2、创建设备',
+      name: '创建设备',
+      english: 'CREATE DEVICE',
       auth: !!devicePermission.add,
       url: 'device/Instance',
       param: '?save=true',
     },
     {
       key: 'rule-engine',
-      name: '3、规则引擎',
+      name: '规则引擎',
+      english: 'RULE ENGINE',
       auth: !!rulePermission.add,
       url: 'rule-engine/Instance',
       param: '?save=true',
@@ -74,22 +61,33 @@ const Device = () => {
 
   return (
     <Row gutter={24}>
-      <Col span={12}>
+      <Col span={14}>
         <Guide
           title="物联网引导"
           data={guideList}
-          jump={(auth: boolean, url: string, param: string) => {
-            pageJump(auth, url, param);
-          }}
+          // jump={(auth: boolean, url: string, param: string) => {
+          //   pageJump(auth, url, param);
+          // }}
         />
       </Col>
-      <Col span={12}>
-        <Statistics />
+      <Col span={10}>
+        <Statistics
+          data={[
+            {
+              name: '产品数量',
+              value: 111,
+              img: '',
+            },
+            {
+              name: '设备数量',
+              value: 12,
+              img: '',
+            },
+          ]}
+        />
       </Col>
       <Col span={24}>
-        <Card style={{ margin: '20px 0' }} title="平台架构图">
-          <img style={{ height: 500 }} src={require('/public/images/login.png')} />
-        </Card>
+        <Body title={'平台架构图'} english={'PLATFORM ARCHITECTURE DIAGRAM'} />
       </Col>
       <Col span={24}>
         <Steps />

+ 2 - 2
src/pages/media/DashBoard/index.tsx

@@ -100,10 +100,10 @@ export default () => {
           type: 'value',
         },
         grid: {
-          left: '2%',
+          left: '4%',
           right: '2%',
           top: '2%',
-          bottom: '2%',
+          bottom: '4%',
         },
         series: [
           {