Selaa lähdekoodia

fix: 设备诊断

sun-chaochao 3 vuotta sitten
vanhempi
commit
2cb1422163

BIN
public/images/metadata-map.png


+ 131 - 0
src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx

@@ -0,0 +1,131 @@
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { ExclamationCircleFilled } from '@ant-design/icons';
+import { Badge, Modal } from 'antd';
+import styles from './index.less';
+
+interface Props {
+  close: () => void;
+  data: any;
+}
+
+const DiagnosticAdvice = (props: Props) => {
+  const { data } = props;
+  const nameMap = new Map();
+  nameMap.set('mqtt-client-gateway', 'topic');
+  nameMap.set('websocket-server', 'URL');
+  nameMap.set('http-server-gateway', 'URL');
+  nameMap.set('coap-server-gateway', 'URL');
+  nameMap.set('udp-device-gateway', '地址');
+  nameMap.set('tcp-server-gateway', '地址');
+
+  const jumpUrl = () => {
+    const url = getMenuPathByParams(MENUS_CODE['device/Product/Detail'], data.id);
+    const tab: any = window.open(`${origin}/#${url}?key=access`);
+    tab!.onTabSaveSuccess = (value: any) => {
+      if (value) {
+        // diagnoseConfig();
+        // 没有权限怎么展示
+      }
+    };
+  };
+
+  return (
+    <Modal
+      title="诊断建议"
+      onCancel={() => {
+        props.close();
+      }}
+      width={700}
+      visible
+    >
+      <div className={styles.advice}>
+        <div className={styles.alert}>
+          <ExclamationCircleFilled style={{ marginRight: 10 }} />
+          所有诊断均无异常但设备任未上线,请检查以下内容
+        </div>
+        {(data?.product || []).map((item: any) => (
+          <div className={styles.infoItem} key={item.name}>
+            <Badge
+              status="default"
+              text={
+                <span>
+                  产品-${item.name}规则可能有加密处理,请认真查看
+                  <a
+                    onClick={() => {
+                      jumpUrl();
+                    }}
+                  >
+                    设备接入配置
+                  </a>
+                  中【消息协议】说明
+                </span>
+              }
+            />
+          </div>
+        ))}
+        {(data?.device || []).map((item: any) => (
+          <div className={styles.infoItem} key={item.name}>
+            <Badge
+              status="default"
+              text={
+                <span>
+                  设备-${item.name}规则可能有加密处理,请认真查看
+                  <a
+                    onClick={() => {
+                      jumpUrl();
+                    }}
+                  >
+                    设备接入配置
+                  </a>
+                  中【消息协议】说明
+                </span>
+              }
+            />
+          </div>
+        ))}
+        {!!data.provider && (
+          <div>
+            {data.routes.length > 0 ? (
+              <div className={styles.infoItem}>
+                <Badge
+                  status="default"
+                  text={
+                    <span>
+                      请根据
+                      <a
+                        onClick={() => {
+                          jumpUrl();
+                        }}
+                      >
+                        设备接入配置
+                      </a>
+                      中${nameMap.get(data.provider)}
+                      信息,任意上报一条数据(无设备接入配置查看权限时:请联系管理员根据设备接入配置中$
+                      {URL}信息,任意上报一条数据)。 变量说明:${nameMap.get(data.provider)}
+                      变量根据网关详情中provider类型判断。
+                    </span>
+                  }
+                />
+              </div>
+            ) : (
+              <div className={styles.infoItem}>
+                <Badge
+                  status="default"
+                  text={
+                    <span>
+                      请联系管理员提供${nameMap.get(data.provider)}
+                      信息,并根据URL信息任意上报一条数据 变量说明:${nameMap.get(data.provider)}
+                      变量根据网关详情中provider类型判断。
+                    </span>
+                  }
+                />
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+    </Modal>
+  );
+};
+
+export default DiagnosticAdvice;

+ 116 - 0
src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx

@@ -0,0 +1,116 @@
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import { InstanceModel } from '@/pages/device/Instance';
+import type { ISchema } from '@formily/json-schema';
+import { Form, FormGrid, FormItem, Input, Password, PreviewText } from '@formily/antd';
+import { Modal } from 'antd';
+
+const componentMap = {
+  string: 'Input',
+  password: 'Password',
+};
+
+interface Props {
+  close: () => void;
+  metadata: any;
+  ok: (data: any) => void;
+}
+
+const ManualInspection = (props: Props) => {
+  const { metadata } = props;
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: InstanceModel.detail?.configuration,
+  });
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Input,
+      Password,
+      FormGrid,
+      PreviewText,
+    },
+  });
+
+  const configToSchema = (data: any[]) => {
+    const config = {};
+    data.forEach((item) => {
+      config[item.property] = {
+        type: 'string',
+        title: item.name,
+        require: true,
+        'x-decorator': 'FormItem',
+        'x-component': componentMap[item.type.type],
+        'x-decorator-props': {
+          tooltip: item.description,
+        },
+        'x-component-props': {
+          value: '',
+        },
+      };
+    });
+    return config;
+  };
+
+  const renderConfigCard = () => {
+    const itemSchema: ISchema = {
+      type: 'object',
+      properties: {
+        grid: {
+          type: 'void',
+          'x-component': 'FormGrid',
+          'x-component-props': {
+            minColumns: [1],
+            maxColumns: [1],
+          },
+          properties: configToSchema(metadata?.data?.properties),
+        },
+      },
+    };
+
+    return (
+      <>
+        <PreviewText.Placeholder value="-">
+          <Form form={form} layout="vertical">
+            <SchemaField schema={itemSchema} />
+          </Form>
+        </PreviewText.Placeholder>
+      </>
+    );
+  };
+  return (
+    <Modal
+      title="人工检查"
+      onCancel={() => {
+        props.close();
+      }}
+      onOk={async () => {
+        const values = (await form.submit()) as any;
+        let flag = true;
+        Object.keys(values).forEach((key) => {
+          if (values[key] !== metadata?.check[key]) {
+            flag = false;
+          }
+        });
+        if (flag) {
+          props.ok({
+            status: 'success',
+            data: metadata,
+          });
+        } else {
+          props.ok({
+            status: 'error',
+            data: metadata,
+          });
+        }
+      }}
+      visible
+    >
+      {renderConfigCard()}
+    </Modal>
+  );
+};
+
+export default ManualInspection;

+ 15 - 0
src/pages/device/Instance/Detail/Diagnose/Status/index.less

@@ -80,3 +80,18 @@
     transform: rotate(360deg);
   }
 }
+
+.alert {
+  height: 40px;
+  padding-left: 10px;
+  color: rgba(0, 0, 0, 0.55);
+  line-height: 40px;
+  background-color: #f6f6f6;
+}
+
+.advice {
+  .infoItem {
+    width: 100%;
+    margin: 10px;
+  }
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 639 - 345
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx


+ 14 - 45
src/pages/device/Instance/Detail/Diagnose/Status/model.ts

@@ -7,76 +7,45 @@ type StatusProps = {
   info: null | ReactNode;
 };
 
-// export const initStatus = {
-//   product: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-//   config: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-//   device: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-//   deviceConfig: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-//   gateway: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-//   network: {
-//     status: 'loading',
-//     text: '正在诊断中...',
-//     info: null,
-//   },
-// };
-
 export const DiagnoseStatusModel = model<{
   status: {
-    product: StatusProps;
-    config: StatusProps;
-    device: StatusProps;
-    deviceConfig: StatusProps;
-    gateway: StatusProps;
-    network: StatusProps;
+    config?: StatusProps;
+    network?: StatusProps;
+    product?: StatusProps;
+    device?: StatusProps;
+    productAuth?: StatusProps;
+    deviceAuth?: StatusProps;
+    deviceAccess?: StatusProps;
+    other?: StatusProps;
   };
 }>({
   status: {
-    product: {
+    config: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,
     },
-    config: {
+    network: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,
     },
-    device: {
+    product: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,
     },
-    deviceConfig: {
+    device: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,
     },
-    gateway: {
+    deviceAccess: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,
     },
-    network: {
+    other: {
       status: 'loading',
       text: '正在诊断中...',
       info: null,

+ 19 - 0
src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.less

@@ -0,0 +1,19 @@
+.map-desc {
+  padding: 16px;
+  border: 1px solid rgba(#000, 0.08);
+
+  h1 {
+    margin: 0 0 16px 0;
+    color: rgba(#000, 0.85);
+    font-size: 14px;
+  }
+
+  .text {
+    color: #666;
+    font-size: 14px;
+  }
+
+  .image {
+    margin: 24px 0;
+  }
+}

+ 69 - 71
src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx

@@ -1,7 +1,10 @@
 import React, { useContext, useEffect, useState } from 'react';
-import { Form, Input, message, Pagination, Select, Table } from 'antd';
+import { Badge, Col, Form, Input, message, Pagination, Row, Select, Table } from 'antd';
 import { service } from '@/pages/device/Instance';
 import _ from 'lodash';
+import './index.less';
+
+const defaultImage = require('/public/images/metadata-map.png');
 
 const EditableContext: any = React.createContext(null);
 
@@ -67,12 +70,13 @@ const EditableCell = ({
             (option?.children || '').toLowerCase()?.indexOf(input.toLowerCase()) >= 0
           }
         >
-          <Select.Option value={'use-origin-data'}>使用原始属性</Select.Option>
-          {list.map((item: any) => (
-            <Select.Option key={item?.id} value={item?.id}>
-              {item?.id}
-            </Select.Option>
-          ))}
+          <Select.Option value={record.metadataId}>使用原始属性</Select.Option>
+          {list.length > 0 &&
+            list.map((item: any) => (
+              <Select.Option key={item?.id} value={item?.id}>
+                {item?.name}({item?.id})
+              </Select.Option>
+            ))}
         </Select>
       </Form.Item>
     );
@@ -85,25 +89,17 @@ interface Props {
   type: 'device' | 'product';
 }
 
-const statusMap = new Map();
-statusMap.set('write', '写');
-statusMap.set('read', '读');
-statusMap.set('report', '上报');
-
 const EditableTable = (props: Props) => {
   const baseColumns = [
     {
-      title: '建模属性',
+      title: '物模型属性',
       dataIndex: 'name',
       render: (text: any, record: any) => <span>{`${record.name}(${record.id})`}</span>,
     },
     {
-      title: '映射属性',
+      title: '设备上报属性',
       dataIndex: 'metadataId',
       width: '30%',
-      render: (text: any, record: any) => (
-        <span>{`${record.metadataId}(${record.metadataName})`}</span>
-      ),
       editable: true,
     },
     {
@@ -112,23 +108,14 @@ const EditableTable = (props: Props) => {
       render: (text: any, record: any) => <span>{record.valueType?.type}</span>,
     },
     {
-      title: '读写类型',
-      dataIndex: 'expands',
-      render: (text: any, record: any) => (
+      title: '映射状态',
+      dataIndex: 'customMapping',
+      render: (text: any) => (
         <span>
-          {(record.expands?.type || [])
-            .map((i: string) => {
-              return statusMap.get(i);
-            })
-            .join(',')}
+          <Badge status={text ? 'success' : 'error'} text={text ? '已映射' : '未映射'} />
         </span>
       ),
     },
-    {
-      title: '映射状态',
-      dataIndex: 'customMapping',
-      render: (text: any) => <span>{String(text)}</span>,
-    },
   ];
   const metadata = JSON.parse(props?.data?.metadata || '{}');
   const [properties, setProperties] = useState<any[]>(metadata?.properties || []);
@@ -175,7 +162,6 @@ const EditableTable = (props: Props) => {
           };
         },
       );
-      console.log(list);
       setProperties([...list]);
       setDataSource({
         data: list.slice(
@@ -209,19 +195,14 @@ const EditableTable = (props: Props) => {
     const newData = [...dataSource.data];
     const index = newData.findIndex((item) => row.id === item.id);
     const item = newData[index];
-    // newData.splice(index, 1, { ...item, ...row });
-    // setDataSource({
-    //     ...dataSource,
-    //     data: [...newData],
-    // });
     if (item?.metadataId !== row?.metadataId) {
       const resp = await service[
         props.type === 'device' ? 'saveDeviceMetadata' : 'saveProductMetadata'
       ](props.data?.id, [
         {
           metadataType: 'property',
-          metadataId: row.metadataId === 'use-origin-data' ? row.metadataId : row.id,
-          originalId: row.metadataId === 'use-origin-data' ? row.id : '',
+          metadataId: row.metadataId === row.id ? row.metadataId : row.id,
+          originalId: row.metadataId === row.id ? row.id : '',
           others: {},
         },
       ]);
@@ -294,40 +275,57 @@ const EditableTable = (props: Props) => {
           }}
         />
       </div>
-      <Table
-        components={components}
-        rowClassName={() => 'editable-row'}
-        bordered
-        rowKey="id"
-        pagination={false}
-        dataSource={dataSource?.data || []}
-        columns={columns}
-      />
-      {dataSource.data.length > 0 && (
-        <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-          <Pagination
-            showSizeChanger
-            size="small"
-            className={'pro-table-card-pagination'}
-            total={dataSource?.total || 0}
-            current={dataSource?.pageIndex + 1}
-            onChange={(page, size) => {
-              handleSearch({
-                name: value,
-                pageIndex: page - 1,
-                pageSize: size,
-              });
-            }}
-            pageSizeOptions={[10, 20, 50, 100]}
-            pageSize={dataSource?.pageSize}
-            showTotal={(num) => {
-              const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-              const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-              return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-            }}
+      <Row gutter={24}>
+        <Col span={16}>
+          <Table
+            components={components}
+            rowClassName={() => 'editable-row'}
+            bordered
+            rowKey="id"
+            pagination={false}
+            dataSource={dataSource?.data || []}
+            columns={columns}
           />
-        </div>
-      )}
+          {dataSource.data.length > 0 && (
+            <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
+              <Pagination
+                showSizeChanger
+                size="small"
+                className={'pro-table-card-pagination'}
+                total={dataSource?.total || 0}
+                current={dataSource?.pageIndex + 1}
+                onChange={(page, size) => {
+                  handleSearch({
+                    name: value,
+                    pageIndex: page - 1,
+                    pageSize: size,
+                  });
+                }}
+                pageSizeOptions={[10, 20, 50, 100]}
+                pageSize={dataSource?.pageSize}
+                showTotal={(num) => {
+                  const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
+                  const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
+                  return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+                }}
+              />
+            </div>
+          )}
+        </Col>
+        <Col span={8}>
+          <div className="map-desc">
+            <h1>功能说明</h1>
+            <div className="text">
+              该功能用于将<b>物模型属性</b>与<b>设备上报属性</b>
+              进行映射,当物模型属性与设备上报属性不一致时,可在当前页面直接修改映射关系,系统将以
+              <b>映射后</b>的<b>物模型属性</b>进行数据处理
+            </div>
+            <div className="image">
+              <img src={defaultImage} style={{ width: '100%' }} />
+            </div>
+          </div>
+        </Col>
+      </Row>
     </div>
   );
 };

+ 10 - 0
src/pages/device/Instance/service.ts

@@ -174,6 +174,11 @@ class Service extends BaseService<DeviceInstance> {
     request(`/${SystemConst.API_BASE}/device/product/${id}/config-metadata`, {
       method: 'GET',
     });
+  // 设备配置
+  public queryDeviceConfig = (id: string) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${id}/config-metadata`, {
+      method: 'GET',
+    });
   // 设备接入网关状态
   public queryGatewayState = (id: string) =>
     request(`/${SystemConst.API_BASE}/gateway/device/${id}/detail`, {
@@ -238,6 +243,11 @@ class Service extends BaseService<DeviceInstance> {
     request(`/${SystemConst.API_BASE}/device/metadata/mapping/product/${productId}`, {
       method: 'GET',
     });
+  //
+  public queryProcotolDetail = (type: string) =>
+    request(`/${SystemConst.API_BASE}/protocol/${type}/transport/MQTT`, {
+      method: 'GET',
+    });
 }
 
 export default Service;

+ 30 - 54
src/pages/device/Product/Detail/Access/index.tsx

@@ -24,8 +24,6 @@ const Access = () => {
   const [visible, setVisible] = useState<boolean>(true);
   const [config, setConfig] = useState<any>();
   const [access, setAccess] = useState<any>();
-  const [providers, setProviders] = useState<any[]>([]);
-  const [networkList, setNetworkList] = useState<any[]>([]);
   const { permission } = usePermissions('link/AccessConfig');
 
   const MetworkTypeMapping = new Map();
@@ -43,31 +41,29 @@ const Access = () => {
 
   const [metadata, setMetadata] = useState<ConfigMetadata[]>([]);
 
-  const queryNetworkList = (id: string) => {
-    service.getNetworkList(MetworkTypeMapping.get(id)).then((resp) => {
-      if (resp.status === 200) {
-        setNetworkList(resp.result);
-      }
-    });
+  const queryAccessDetail = (id: string) => {
+    service
+      .queryGatewayDetail({
+        terms: [
+          {
+            column: 'id',
+            value: id,
+          },
+        ],
+      })
+      .then((resp) => {
+        setAccess(resp.result.data[0]);
+      });
   };
 
-  const queryProviders = () => {
-    service.getProviders().then((resp) => {
+  const getConfigDetail = (messageProtocol: string, transportProtocol: string) => {
+    service.getConfigView(messageProtocol, transportProtocol).then((resp) => {
       if (resp.status === 200) {
-        setProviders(resp.result);
+        setConfig(resp.result);
       }
     });
   };
 
-  const queryAccess = (id: string) => {
-    service.queryList({ pageSize: 1000 }).then((resp) => {
-      const dt = resp.result?.data.find((i: any) => i.id === id);
-      setAccess(dt);
-      if (dt) {
-        queryNetworkList(dt?.provider);
-      }
-    });
-  };
   const columnsMQTT: any[] = [
     {
       title: '分组',
@@ -192,14 +188,6 @@ const Access = () => {
     },
   ];
 
-  const getDetail = (messageProtocol: string, transportProtocol: string) => {
-    service.getConfigView(messageProtocol, transportProtocol).then((resp) => {
-      if (resp.status === 200) {
-        setConfig(resp.result);
-      }
-    });
-  };
-
   const id = productModel.current?.id;
 
   useEffect(() => {
@@ -210,14 +198,13 @@ const Access = () => {
           setMetadata(resp.result);
         });
     }
-    queryProviders();
     setVisible(!!productModel.current?.accessId);
     if (productModel.current?.accessId) {
-      getDetail(
+      queryAccessDetail(productModel.current?.accessId);
+      getConfigDetail(
         productModel.current?.messageProtocol || '',
         productModel.current?.transportProtocol || '',
       );
-      queryAccess(productModel.current?.accessId);
     }
   }, [productModel.current]);
 
@@ -393,16 +380,8 @@ const Access = () => {
                     </span>
                   }
                 />
-                <div className={styles.context}>
-                  {providers.find((i) => i.id === access?.provider)?.name || '--'}
-                </div>
-                <div className={styles.context}>
-                  {providers.find((i) => i.id === access?.provider)?.description && (
-                    <span>
-                      {providers.find((i) => i.id === access?.provider)?.description || '--'}
-                    </span>
-                  )}
-                </div>
+                <div className={styles.context}>{access?.name || '--'}</div>
+                <div className={styles.context}>{access?.description || '--'}</div>
               </div>
 
               <div className={styles.item}>
@@ -414,21 +393,18 @@ const Access = () => {
                   </div>
                 )}
               </div>
-
               <div className={styles.item}>
                 <TitleComponent data={'连接信息'} />
-                {(networkList.find((i) => i.id === access?.channelId)?.addresses || []).length > 0
-                  ? (networkList.find((i) => i.id === access?.channelId)?.addresses || []).map(
-                      (item: any) => (
-                        <div key={item.address}>
-                          <Badge
-                            color={item.health === -1 ? 'red' : 'green'}
-                            text={item.address}
-                            style={{ marginLeft: '20px' }}
-                          />
-                        </div>
-                      ),
-                    )
+                {(access?.channelInfo?.addresses || []).length > 0
+                  ? (access?.channelInfo?.addresses || []).map((item: any) => (
+                      <div key={item.address}>
+                        <Badge
+                          color={item.health === -1 ? 'red' : 'green'}
+                          text={item.address}
+                          style={{ marginLeft: '20px' }}
+                        />
+                      </div>
+                    ))
                   : '暂无连接信息'}
               </div>
 

+ 1 - 0
src/pages/device/components/Metadata/Import/index.tsx

@@ -36,6 +36,7 @@ const Import = (props: Props) => {
   const loadData = () => async (field: Field) => {
     field.loading = true;
     const product = (await service.queryNoPagingPost({
+      paging: false,
       terms: [{ column: 'id$not', value: param.id }],
     })) as any;
     field.dataSource = product.result.map((item: any) => ({

+ 3 - 1
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -510,7 +510,9 @@ const Access = (props: Props) => {
                   <Button
                     type="primary"
                     disabled={
-                      !props.data?.id ? getButtonPermission('link/AccessConfig', ['update']) : false
+                      !!props.data.id
+                        ? getButtonPermission('link/AccessConfig', ['update'])
+                        : getButtonPermission('link/AccessConfig', ['add'])
                     }
                     onClick={async () => {
                       try {

+ 3 - 1
src/pages/link/AccessConfig/Detail/Media/index.tsx

@@ -480,7 +480,9 @@ const Media = (props: Props) => {
               <Button
                 type="primary"
                 disabled={
-                  !!params.get('id') ? getButtonPermission('link/AccessConfig', ['update']) : false
+                  !!params.get('id')
+                    ? getButtonPermission('link/AccessConfig', ['update'])
+                    : getButtonPermission('link/AccessConfig', ['add'])
                 }
                 onClick={async () => {
                   const values = await form.validateFields();

+ 1 - 1
src/pages/link/AccessConfig/index.tsx

@@ -53,7 +53,7 @@ const AccessConfig = () => {
   const handleSearch = (params: any) => {
     setParam(params);
     service
-      .queryList({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+      .queryGatewayDetail({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
       .then((resp) => {
         if (resp.status === 200) {
           setDataSource(resp.result);

+ 1 - 1
src/pages/link/AccessConfig/service.ts

@@ -4,7 +4,7 @@ import SystemConst from '@/utils/const';
 import type { AccessItem } from './typings';
 
 class Service extends BaseService<AccessItem> {
-  public queryList = (data: any) =>
+  public queryGatewayDetail = (data: any) =>
     request(`/${SystemConst.API_BASE}/gateway/device/detail/_query`, {
       method: 'POST',
       data,