Jelajahi Sumber

fix(compiler): 数据大屏中国地图修改样式,新增ota管理功能

shylock 1 bulan lalu
induk
melakukan
f927a9496e

+ 8 - 1
config/routes.ts

@@ -132,7 +132,7 @@
     layout: false,
     component: './DataBoard',
   },
-  // 故障管理
+  // 版本管理
   {
     path: '/deviceFaults',
     name: '版本管理',
@@ -194,6 +194,13 @@
       },
     ],
   },
+  // ota升级
+  {
+    path: '/otaUpdate',
+    name: 'ota管理',
+    icon: 'Appstore',
+    component: './OtaManagement/index',
+  },
   // 房间列表
   {
     path: '/roomList',

+ 3 - 7
src/pages/DataBoard/index.tsx

@@ -505,13 +505,9 @@ const DataBoard: React.FC = () => {
             </div>
           </div>
           {/* 地图 */}
-          {areaDeviceList && areaDeviceList.length ? (
-            <Suspense fallback={<div>loading</div>}>
-              <MapComponent userData={userData} deviceData={deviceData} areaList={areaDeviceList} />
-            </Suspense>
-          ) : (
-            <datav.Loading style={{ position: 'absolute' }}>Loading...</datav.Loading>
-          )}
+          <Suspense fallback={<div>loading</div>}>
+            <MapComponent userData={userData} deviceData={deviceData} />
+          </Suspense>
         </div>
         {/* 中间内容-----右侧 */}
         {/* 中间内容-----右侧--地区设备数量统计 */}

+ 94 - 332
src/pages/DataBoard/mapComponent.tsx

@@ -1,13 +1,12 @@
 import * as echarts from 'echarts';
 import styles from './mapComponent.less';
 import React, { useEffect, useRef } from 'react';
-import { queryMapJson } from '@/services/dataBoardService';
+import { queryMapJson, queryProvinceData } from '@/services/dataBoardService';
 import { message } from 'antd';
 
 interface propsData {
   userData: number;
   deviceData: number;
-  areaList: any;
 }
 
 /**
@@ -16,352 +15,115 @@ interface propsData {
  */
 const MapComponent: React.FC<propsData> = (props) => {
   const chartRef: any = useRef();
-  const { userData, deviceData, areaList } = props;
-  const geoCoordMap = {
-    海门: [121.15, 31.89],
-    鄂尔多斯: [109.781327, 39.608266],
-    招远: [120.38, 37.35],
-    舟山: [122.207216, 29.985295],
-    齐齐哈尔: [123.97, 47.33],
-    盐城: [120.13, 33.38],
-    赤峰: [118.87, 42.28],
-    青岛: [120.33, 36.07],
-    乳山: [121.52, 36.89],
-    金昌: [102.188043, 38.520089],
-    泉州: [118.58, 24.93],
-    莱西: [120.53, 36.86],
-    日照: [119.46, 35.42],
-    胶南: [119.97, 35.88],
-    南通: [121.05, 32.08],
-    拉萨: [91.11, 29.97],
-    云浮: [112.02, 22.93],
-    梅州: [116.1, 24.55],
-    文登: [122.05, 37.2],
-    上海: [121.48, 31.22],
-    攀枝花: [101.718637, 26.582347],
-    威海: [122.1, 37.5],
-    承德: [117.93, 40.97],
-    厦门: [118.1, 24.46],
-    汕尾: [115.375279, 22.786211],
-    潮州: [116.63, 23.68],
-    丹东: [124.37, 40.13],
-    太仓: [121.1, 31.45],
-    曲靖: [103.79, 25.51],
-    烟台: [121.39, 37.52],
-    福州: [119.3, 26.08],
-    瓦房店: [121.979603, 39.627114],
-    即墨: [120.45, 36.38],
-    抚顺: [123.97, 41.97],
-    玉溪: [102.52, 24.35],
-    张家口: [114.87, 40.82],
-    阳泉: [113.57, 37.85],
-    莱州: [119.942327, 37.177017],
-    湖州: [120.1, 30.86],
-    汕头: [116.69, 23.39],
-    昆山: [120.95, 31.39],
-    宁波: [121.56, 29.86],
-    湛江: [110.359377, 21.270708],
-    揭阳: [116.35, 23.55],
-    荣成: [122.41, 37.16],
-    连云港: [119.16, 34.59],
-    葫芦岛: [120.836932, 40.711052],
-    常熟: [120.74, 31.64],
-    东莞: [113.75, 23.04],
-    河源: [114.68, 23.73],
-    淮安: [119.15, 33.5],
-    泰州: [119.9, 32.49],
-    南宁: [108.33, 22.84],
-    营口: [122.18, 40.65],
-    惠州: [114.4, 23.09],
-    江阴: [120.26, 31.91],
-    蓬莱: [120.75, 37.8],
-    韶关: [113.62, 24.84],
-    嘉峪关: [98.289152, 39.77313],
-    广州: [113.23, 23.16],
-    延安: [109.47, 36.6],
-    太原: [112.53, 37.87],
-    清远: [113.01, 23.7],
-    中山: [113.38, 22.52],
-    昆明: [102.73, 25.04],
-    寿光: [118.73, 36.86],
-    盘锦: [122.070714, 41.119997],
-    长治: [113.08, 36.18],
-    深圳: [114.07, 22.62],
-    珠海: [113.52, 22.3],
-    宿迁: [118.3, 33.96],
-    咸阳: [108.72, 34.36],
-    铜川: [109.11, 35.09],
-    平度: [119.97, 36.77],
-    佛山: [113.11, 23.05],
-    海口: [110.35, 20.02],
-    江门: [113.06, 22.61],
-    章丘: [117.53, 36.72],
-    肇庆: [112.44, 23.05],
-    大连: [121.62, 38.92],
-    临汾: [111.5, 36.08],
-    吴江: [120.63, 31.16],
-    石嘴山: [106.39, 39.04],
-    沈阳: [123.38, 41.8],
-    苏州: [120.62, 31.32],
-    茂名: [110.88, 21.68],
-    嘉兴: [120.76, 30.77],
-    长春: [125.35, 43.88],
-    胶州: [120.03336, 36.264622],
-    银川: [106.27, 38.47],
-    张家港: [120.555821, 31.875428],
-    三门峡: [111.19, 34.76],
-    锦州: [121.15, 41.13],
-    南昌: [115.89, 28.68],
-    柳州: [109.4, 24.33],
-    三亚: [109.511909, 18.252847],
-    自贡: [104.778442, 29.33903],
-    吉林: [126.57, 43.87],
-    阳江: [111.95, 21.85],
-    泸州: [105.39, 28.91],
-    西宁: [101.74, 36.56],
-    宜宾: [104.56, 29.77],
-    呼和浩特: [111.65, 40.82],
-    成都: [104.06, 30.67],
-    大同: [113.3, 40.12],
-    镇江: [119.44, 32.2],
-    桂林: [110.28, 25.29],
-    张家界: [110.479191, 29.117096],
-    宜兴: [119.82, 31.36],
-    北海: [109.12, 21.49],
-    西安: [108.95, 34.27],
-    金坛: [119.56, 31.74],
-    东营: [118.49, 37.46],
-    牡丹江: [129.58, 44.6],
-    遵义: [106.9, 27.7],
-    绍兴: [120.58, 30.01],
-    扬州: [119.42, 32.39],
-    常州: [119.95, 31.79],
-    潍坊: [119.1, 36.62],
-    重庆: [106.54, 29.59],
-    台州: [121.420757, 28.656386],
-    南京: [118.78, 32.04],
-    滨州: [118.03, 37.36],
-    贵阳: [106.71, 26.57],
-    无锡: [120.29, 31.59],
-    本溪: [123.73, 41.3],
-    克拉玛依: [84.77, 45.59],
-    渭南: [109.5, 34.52],
-    马鞍山: [118.48, 31.56],
-    宝鸡: [107.15, 34.38],
-    焦作: [113.21, 35.24],
-    句容: [119.16, 31.95],
-    北京: [116.46, 39.92],
-    徐州: [117.2, 34.26],
-    衡水: [115.72, 37.72],
-    包头: [110, 40.58],
-    绵阳: [104.73, 31.48],
-    乌鲁木齐: [87.68, 43.77],
-    枣庄: [117.57, 34.86],
-    杭州: [120.19, 30.26],
-    淄博: [118.05, 36.78],
-    鞍山: [122.85, 41.12],
-    溧阳: [119.48, 31.43],
-    库尔勒: [86.06, 41.68],
-    安阳: [114.35, 36.1],
-    开封: [114.35, 34.79],
-    济南: [117, 36.65],
-    德阳: [104.37, 31.13],
-    温州: [120.65, 28.01],
-    九江: [115.97, 29.71],
-    邯郸: [114.47, 36.6],
-    临安: [119.72, 30.23],
-    兰州: [103.73, 36.03],
-    沧州: [116.83, 38.33],
-    临沂: [118.35, 35.05],
-    南充: [106.110698, 30.837793],
-    天津: [117.2, 39.13],
-    富阳: [119.95, 30.07],
-    泰安: [117.13, 36.18],
-    诸暨: [120.23, 29.71],
-    郑州: [113.65, 34.76],
-    哈尔滨: [126.63, 45.75],
-    聊城: [115.97, 36.45],
-    芜湖: [118.38, 31.33],
-    唐山: [118.02, 39.63],
-    平顶山: [113.29, 33.75],
-    邢台: [114.48, 37.05],
-    德州: [116.29, 37.45],
-    济宁: [116.59, 35.38],
-    荆州: [112.239741, 30.335165],
-    宜昌: [111.3, 30.7],
-    义乌: [120.06, 29.32],
-    丽水: [119.92, 28.45],
-    洛阳: [112.44, 34.7],
-    秦皇岛: [119.57, 39.95],
-    株洲: [113.16, 27.83],
-    石家庄: [114.48, 38.03],
-    莱芜: [117.67, 36.19],
-    常德: [111.69, 29.05],
-    保定: [115.48, 38.85],
-    湘潭: [112.91, 27.87],
-    金华: [119.64, 29.12],
-    岳阳: [113.09, 29.37],
-    长沙: [113, 28.21],
-    衢州: [118.88, 28.97],
-    廊坊: [116.7, 39.53],
-    菏泽: [115.480656, 35.23375],
-    合肥: [117.27, 31.86],
-    武汉: [114.31, 30.52],
-    大庆: [125.03, 46.58],
-  };
-
-  const newAreaList = areaList.map((item: any) => {
-    const nameWithoutCity = item.name.replace(/市$/, '');
-    return {
-      ...item,
-      name: nameWithoutCity,
-    };
-  });
-
-  // 处理数据结构
-  const convertData = (item: string | any[]) => {
-    const res = [];
-    for (let i = 0; i < item.length; i++) {
-      const geoCoord = geoCoordMap[item[i].name];
-      if (geoCoord) {
-        res.push({
-          name: item[i].name,
-          value: geoCoord.concat(item[i].value),
-        });
-      }
-    }
-    return res;
-  };
+  const { userData, deviceData } = props;
 
-  useEffect(() => {
+  const renderMapData = (areaList: any) => {
     // 初始化统计图对象
     const myChart = echarts.init(chartRef.current);
-
-    const options: any = {
-      tooltip: {},
-      geo: {
-        map: 'china',
-        show: true,
-        roam: false,
-        zoom: 1.2,
-        label: {
-          show: false,
-        },
-        itemStyle: {
-          normal: {
-            areaColor: '#091632',
-            borderColor: '#1773c3',
-            shadowColor: '#1773c3',
-            shadowBlur: 20,
-          },
-        },
-      },
-      series: [
-        {
-          type: 'map',
-          map: 'china',
-          zoom: 1.2,
-          geoIndex: 1,
-          aspectScale: 0.75, //长宽比
-          showLegendSymbol: true, // 存在legend时显示
-          label: {
+    let options: any;
+    queryMapJson().then((res) => {
+      if (res && res.code === 0) {
+        // 获取地图数据
+        const uploadedDataURL = res.data.map_json;
+        echarts.registerMap('myMap', uploadedDataURL);
+        options = {
+          tooltip: {
             show: false,
           },
-          roam: false,
-          itemStyle: {
-            normal: {
-              areaColor: '#031525',
-              borderColor: '#3B5077',
-              borderWidth: 1,
-            },
-            emphasis: {
-              areaColor: '#0f2c70',
-            },
-          },
-          data: [],
-        },
-        {
-          name: '城市',
-          type: 'scatter',
-          coordinateSystem: 'geo',
-          data: convertData(newAreaList),
-          symbolSize: function (val: number[]) {
-            return val[2] / 20;
-          },
-          label: {
-            normal: {
-              formatter: '{b}',
-              position: 'right',
-              show: false,
-            },
-            emphasis: {
-              show: true,
+          geo: {
+            map: 'myMap',
+            zoom: 1.1,
+            show: true,
+            roam: false,
+            label: {
+              emphasis: {
+                show: false,
+              },
             },
-          },
-          itemStyle: {
-            normal: {
-              color: '#ddb926',
+            itemStyle: {
+              normal: {
+                areaColor: '#091632',
+                borderColor: '#1773c3',
+                shadowColor: '#1773c3',
+                shadowBlur: 20,
+              },
             },
           },
-        },
-        {
-          name: '城市',
-          type: 'effectScatter',
-          coordinateSystem: 'geo',
-          rippleEffect: {
-            brushType: 'fill',
-          },
-          itemStyle: {
-            normal: {
-              color: '#f4e925',
-              shadowBlur: 10,
-              shadowColor: '#333',
+          visualMap: {
+            show: false,
+            max: 1,
+            inRange: {
+              color: ['#ceb10b'],
             },
+            seriesIndex: 0,
           },
-          // 取前三个显示城市名称
-          data: convertData(
-            newAreaList
-              .sort(function (a: { value: number }, b: { value: number }) {
-                return b.value - a.value;
-              })
-              .slice(0, 6),
-          ),
-          symbolSize: function (val: number[]) {
-            return val[2] / 20;
-          },
-          showEffectOn: 'render',
-          label: {
-            normal: {
-              formatter: '{b}',
-              position: 'right',
-              show: true,
-              color: '#f4e925',
+          series: [
+            {
+              type: 'map',
+              map: 'myMap',
+              zoom: 1.1,
+              geoIndex: 1,
+              aspectScale: 0.75, //长宽比
+              showLegendSymbol: true, // 存在legend时显示
+              label: {
+                normal: {
+                  show: true,
+                  textStyle: {
+                    color: '#d3d3d3',
+                  },
+                },
+                emphasis: {
+                  show: true,
+                  textStyle: {
+                    color: '#ff0000',
+                  },
+                },
+              },
+              roam: false,
+              itemStyle: {
+                normal: {
+                  areaColor: '#031525',
+                  borderColor: '#3B5077',
+                  borderWidth: 1,
+                },
+                emphasis: {
+                  areaColor: '#0f2c70',
+                },
+              },
+              data: areaList,
             },
-          },
-        },
-      ],
-    };
-
-    queryMapJson()
-      .then((res) => {
-        if (res && res.code === 0) {
-          const data = JSON.parse(res.data.map_json);
-          if (data) {
-            echarts.registerMap('china', data);
-          }
-          myChart.setOption(options);
-        } else {
-          message.error(res?.message);
-        }
-      })
-      .catch((e) => {
-        message.error(e?.message);
-      });
+          ],
+        };
 
+        myChart.setOption(options);
+      } else {
+        message.error(res?.message);
+      }
+    });
     // 组件卸载
     return () => {
       myChart.clear();
     };
+  };
+
+  // 获取数据
+  const getAreaDataList = () => {
+    queryProvinceData().then((res) => {
+      if (res && res.code === 0) {
+        const areaList = res.data.map((item: any) => {
+          const nameWithoutCity = item.name.replace(/[省|市]$/, '');
+          return {
+            ...item,
+            name: nameWithoutCity,
+          };
+        });
+        renderMapData(areaList);
+      }
+    });
+  };
+
+  useEffect(() => {
+    getAreaDataList();
   }, []);
 
   return (

+ 238 - 0
src/pages/OtaManagement/edit.tsx

@@ -0,0 +1,238 @@
+import React, { useEffect, useState } from 'react';
+import { Col, Form, Input, message, Modal, Row, TreeSelect, Upload } from 'antd';
+import { InboxOutlined } from '@ant-design/icons';
+import type { UploadProps } from 'antd';
+import { createOta, editOta, queryDeviceList } from '@/services/ota';
+const { Dragger } = Upload;
+
+interface editPros {
+  visible: boolean;
+  editCallback: () => void;
+  params: any;
+}
+
+/**
+ * ota管理 - 编辑
+ * @param props
+ * @constructor
+ */
+const Edit: React.FC<editPros> = (props) => {
+  const { params, visible, editCallback } = props;
+  const [form] = Form.useForm();
+  const [deviceList, setDeviceList] = useState([]);
+  const [fileData, setFileData]: any = useState(
+    params && params.url
+      ? [
+          {
+            url: params.url,
+            md5: params.md5,
+            size: params.file_size,
+            name: params.file_name,
+          },
+        ]
+      : [],
+  );
+
+  /**
+   *  获取设备类型列表
+   */
+  const getDeviceList = () => {
+    queryDeviceList({ q: 'tree' }).then((res) => {
+      if (res && res.code === 0) {
+        setDeviceList(res.data.list);
+      }
+    });
+  };
+
+  useEffect(() => {
+    getDeviceList();
+  }, []);
+
+  // 提交
+  const onOk = () => {
+    form.validateFields().then((values) => {
+      if (values) {
+        const data = { ...values };
+        data.url = fileData[0].url;
+        data.md5 = fileData[0].md5;
+        data.file_size = fileData[0].size;
+        data.file_name = fileData[0].name;
+        if (params) {
+          // 编辑
+          data.record_id = params.record_id;
+          editOta(data)
+            .then((res) => {
+              if (res && res.code === 0) {
+                message.success('编辑成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        } else {
+          //  新增
+          createOta(data)
+            .then((res) => {
+              if (res && res.code === 0) {
+                message.success('新增成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        }
+      }
+    });
+  };
+
+  // 取消
+  const onCancel = () => {
+    editCallback();
+  };
+
+  // 上级菜单 树选择
+  const toTreeSelect = (data: any) => {
+    if (!data) {
+      return [];
+    }
+    const newData = [];
+    for (let i = 0; i < data.length; i += 1) {
+      const item = {
+        ...data[i],
+        title: data[i].name,
+        value: data[i].record_id,
+      };
+      if (item.children && item.children.length > 0) {
+        item.children = toTreeSelect(item.children);
+        item.disabled = true;
+      }
+      newData.push(item);
+    }
+    return newData;
+  };
+
+  const uploadProps: UploadProps = {
+    name: 'file',
+    action: '/web/v1/files',
+    maxCount: 1,
+    headers: {
+      authorization: 'authorization-text',
+    },
+    defaultFileList: fileData,
+    onChange(info) {
+      const { status, response } = info.file;
+      if (status === 'done') {
+        //判断上传状态
+        if (response.code === 0) {
+          message.success(`上传成功!`);
+          setFileData([response.data]);
+          form.setFieldValue('remote_map', response.data.name);
+        } else {
+          message.error(response.resultMsg);
+        }
+      } else if (status === 'error') {
+        message.error(`${info.file.name} 文件上传失败`);
+      }
+    },
+  };
+
+  const normFile = (e: any) => {
+    if (Array.isArray(e)) {
+      return e;
+    }
+    return e && e.fileList;
+  };
+
+  const formItemLayout = {
+    labelCol: {
+      span: 4,
+    },
+    wrapperCol: {
+      span: 19,
+    },
+  };
+  return (
+    <Modal
+      title={`${params ? '编辑' : '新增'}`}
+      open={visible}
+      onOk={onOk}
+      onCancel={onCancel}
+      width={800}
+    >
+      <Form form={form}>
+        <Row>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="version"
+              label="版本号"
+              rules={[{ required: true, message: '请输入版本号' }]}
+              initialValue={params?.version || ''}
+            >
+              <Input placeholder="请输入版本号" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="device_type_id"
+              label="设备类型"
+              rules={[{ required: true, message: '请选择设备类型' }]}
+              initialValue={params?.device_type_id || undefined}
+            >
+              <TreeSelect
+                allowClear
+                showSearch
+                treeDefaultExpandAll
+                treeNodeFilterProp="title"
+                style={{ width: '100%' }}
+                dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+                treeData={toTreeSelect(deviceList)}
+                placeholder="请选择"
+              />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="content"
+              label="更新内容"
+              rules={[{ required: true, message: '请输入更新内容' }]}
+              initialValue={params?.content || ''}
+            >
+              <Input placeholder="请输入更新内容" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="url"
+              getValueFromEvent={normFile}
+              label="文件地址"
+              rules={[{ required: true, message: '请选择文件' }]}
+              initialValue={params?.url || ''}
+            >
+              <Dragger {...uploadProps}>
+                <p className="ant-upload-drag-icon">
+                  <InboxOutlined />
+                </p>
+                <p className="ant-upload-text">单击或拖动文件到此区域进行上传</p>
+              </Dragger>
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default Edit;

+ 175 - 0
src/pages/OtaManagement/index.tsx

@@ -0,0 +1,175 @@
+import React, { useEffect, useState } from 'react';
+import { PageContainer } from '@ant-design/pro-components';
+import { Button, Card, message, Modal, Space, Table } from 'antd';
+import styles from '@/pages/Welcome.less';
+import type { ColumnsType } from 'antd/es/table';
+import { delOta, queryOtaList } from '@/services/ota';
+import { PlusCircleOutlined } from '@ant-design/icons';
+import Edit from './edit';
+
+interface DataType {
+  name: string;
+  record_id: string;
+  content: string;
+  device_type: string;
+  version: string;
+}
+
+const OtaUpdate: React.FC = () => {
+  const [loading, setLoading] = useState(false);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList] = useState([]);
+  const [editData, setEditData] = useState<object | null>({});
+  const [visible, setVisible] = useState(false);
+
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+    };
+    queryOtaList(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    setLoading(true);
+    getList();
+  }, []);
+
+  // 新增
+  const onAdd = () => {
+    setEditData(null);
+    setVisible(true);
+  };
+
+  // 分页切换
+  const onChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+    };
+    queryOtaList(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  // 编辑
+  const toEdit = (data: React.SetStateAction<object | null>) => {
+    setEditData(data);
+    setVisible(true);
+  };
+
+  // 编辑回调
+  const editCallback = () => {
+    setLoading(true);
+    setVisible(false);
+    getList();
+  };
+
+  // 删除
+  const toDel = (record: DataType) => {
+    Modal.confirm({
+      title: '删除',
+      content: `确认删除:[${record.content}]`,
+      onOk: () => {
+        delOta(record.record_id).then((res) => {
+          if (res && res.code === 0) {
+            message.success('删除成功');
+            getList();
+          } else {
+            message.error('删除失败');
+          }
+        });
+      },
+    });
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '更新内容',
+      dataIndex: 'content',
+      key: 'content',
+    },
+    {
+      title: '设备类型',
+      dataIndex: 'device_type',
+      key: 'device_type',
+    },
+    {
+      title: '版本号',
+      dataIndex: 'version',
+      key: 'version',
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_, record) => (
+        <Space size="middle">
+          <a
+            onClick={() => {
+              toEdit(record);
+            }}
+          >
+            编辑
+          </a>
+          <a
+            style={{ color: 'red' }}
+            onClick={() => {
+              toDel(record);
+            }}
+          >
+            删除
+          </a>
+        </Space>
+      ),
+    },
+  ];
+  const paginationProps = {
+    showSizeChanger: true,
+    showQuickJumper: true,
+    showTotal: (total: number) => {
+      return <span> 共 {total}条 </span>;
+    },
+    ...pagination,
+  };
+
+  return (
+    <PageContainer>
+      <Card className={styles.container}>
+        <Button htmlType="button" type="primary" style={{ margin: '20px 0' }} onClick={onAdd}>
+          <PlusCircleOutlined />
+          新增ota
+        </Button>
+        <Table
+          columns={columns}
+          dataSource={dataList}
+          rowKey={(record) => record.record_id}
+          pagination={paginationProps}
+          loading={loading}
+          onChange={onChange}
+        />
+        {visible && <Edit visible={visible} editCallback={editCallback} params={editData} />}
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default OtaUpdate;

+ 7 - 0
src/services/dataBoardService.ts

@@ -59,3 +59,10 @@ export async function queryHomeInfo() {
 export async function queryMapJson() {
   return request(`/web/v1/map_jsons/user`);
 }
+
+/**
+ * 获取有数据的区域
+ */
+export async function queryProvinceData() {
+  return request(`/web/v1/statistic/province/chart`);
+}

+ 50 - 0
src/services/ota.ts

@@ -0,0 +1,50 @@
+import { request } from '@@/plugin-request/request';
+import { stringify } from 'qs';
+
+/**
+ * 查询ota升级列表
+ * @param param
+ */
+export async function queryOtaList(param: object) {
+  return request(`/web/v1/ota?${stringify(param)}`);
+}
+
+/**
+ * 删除ota升级
+ * @param record_id
+ */
+export async function delOta(record_id: any) {
+  return request(`/web/v1/ota/${record_id}`, {
+    method: 'DELETE',
+  });
+}
+
+/**
+ * 编辑ota
+ * @param params
+ */
+export async function editOta(params: any) {
+  return request(`/web/v1/ota/${params.record_id}`, {
+    method: 'PUT',
+    data: params,
+  });
+}
+
+/**
+ *  新增ota
+ * @param params
+ */
+export async function createOta(params: object) {
+  return request(`/web/v1/ota`, {
+    method: 'POST',
+    data: params,
+  });
+}
+
+/**
+ * 查询设备列表
+ * @param param
+ */
+export async function queryDeviceList(param: object) {
+  return request(`/web/v1/device_types?${stringify(param)}`);
+}