Explorar o código

fix: 修改设备诊断

sun-chaochao %!s(int64=3) %!d(string=hai) anos
pai
achega
86d22fd111

BIN=BIN
public/images/diagnose/back.png


+ 7 - 3
src/pages/device/Instance/Detail/Diagnose/Message/index.tsx

@@ -20,6 +20,7 @@ import {
 } from '@formily/antd';
 import { randomString } from '@/utils/util';
 import Log from './Log';
+import { Store } from 'jetlinks-store';
 
 interface Props {
   onChange: (type: string) => void;
@@ -64,6 +65,7 @@ const Message = (props: Props) => {
             ...payload,
           });
           setDialogList([...dialogList]);
+          Store.set('diagnose', dialogList);
         }
         const chatBox = document.getElementById('dialog');
         if (chatBox) {
@@ -124,6 +126,8 @@ const Message = (props: Props) => {
   };
   useEffect(() => {
     subscribeLog();
+    const arr = Store.get('diagnose') || [];
+    setDialogList(arr);
   }, []);
 
   const form = createForm({
@@ -373,15 +377,15 @@ const Message = (props: Props) => {
       <Col span={8}>
         <div
           style={{
-            padding: 24,
-            border: '1px solid rgba(0, 0, 0, .09)',
+            paddingLeft: 20,
+            borderLeft: '1px solid rgba(0, 0, 0, .09)',
             overflow: 'hidden',
             maxHeight: 600,
             overflowY: 'auto',
             minHeight: 400,
           }}
         >
-          <div style={{ color: 'rgba(0, 0, 0, .85)', fontWeight: 700 }}>日志</div>
+          <TitleComponent data={'日志'} />
           <div style={{ marginTop: 10 }}>
             {logList.length > 0 ? (
               logList.map((item) => <Log data={item} key={item.key} />)

+ 383 - 283
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -4,77 +4,32 @@ import { useEffect, useState } from 'react';
 import styles from './index.less';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import type { ProductItem } from '@/pages/device/Product/typings';
+import { Store } from 'jetlinks-store';
+import { observer } from '@formily/reactive-react';
+import { DiagnoseStatusModel } from './model';
 
 interface Props {
   onChange: (type: string) => void;
+  flag: boolean;
 }
 
-const Status = (props: Props) => {
-  const StatusMap = new Map();
-  StatusMap.set('error', require('/public/images/diagnose/status/error.png'));
-  StatusMap.set('success', require('/public/images/diagnose/status/success.png'));
-  StatusMap.set('warning', require('/public/images/diagnose/status/warning.png'));
-  StatusMap.set('loading', require('/public/images/diagnose/status/loading.png'));
+const StatusMap = new Map();
+StatusMap.set('error', require('/public/images/diagnose/status/error.png'));
+StatusMap.set('success', require('/public/images/diagnose/status/success.png'));
+StatusMap.set('warning', require('/public/images/diagnose/status/warning.png'));
+StatusMap.set('loading', require('/public/images/diagnose/status/loading.png'));
 
-  const statusColor = new Map();
-  statusColor.set('error', '#E50012');
-  statusColor.set('success', '#24B276');
-  statusColor.set('warning', '#FF9000');
-  statusColor.set('loading', 'rgba(0, 0, 0, .8)');
+const statusColor = new Map();
+statusColor.set('error', '#E50012');
+statusColor.set('success', '#24B276');
+statusColor.set('warning', '#FF9000');
+statusColor.set('loading', 'rgba(0, 0, 0, .8)');
 
-  const initStatus = {
-    product: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    config: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    device: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    'device-config': {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    gateway: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    network: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-  };
-
-  const initList = [
-    {
-      key: 'product',
-      name: '产品状态',
-      desc: '诊断产品状态是否已发布,未发布的状态将导致连接失败。',
-    },
-    {
-      key: 'config',
-      name: '设备接入配置',
-      desc: '诊断设备接入配置是否正确,配置错误将导致连接失败。',
-    },
-    {
-      key: 'device',
-      name: '设备状态',
-      desc: '诊断设备状态是否已启用,未启用的状态将导致连接失败。',
-    },
-  ];
-  const [list, setList] = useState<any[]>(initList);
+const Status = observer((props: Props) => {
+  const time = 1000;
 
-  const [status, setStatus] = useState<any>(initStatus);
+  const [list, setList] = useState<any[]>([]);
 
   const getDetail = (id: string) => {
     service.detail(id).then((response) => {
@@ -82,77 +37,196 @@ const Status = (props: Props) => {
     });
   };
 
-  const handleSearch = async () => {
-    props.onChange('loading');
-    setList(initList);
-    // 设备在线
+  const initList = (proItem: ProductItem, configuration: any[], deviceConfigs: any) => {
+    const datalist = [
+      {
+        key: 'product',
+        name: '产品状态',
+        data: 'product',
+        desc: '诊断产品状态是否已发布,未发布的状态将导致连接失败。',
+      },
+      {
+        key: 'config',
+        name: '设备接入配置',
+        data: 'config',
+        desc: '诊断设备接入配置是否正确,配置错误将导致连接失败。',
+      },
+      {
+        key: 'device',
+        name: '设备状态',
+        data: 'device',
+        desc: '诊断设备状态是否已启用,未启用的状态将导致连接失败。',
+      },
+    ];
+
+    const deviceConfig = {
+      key: 'device-config',
+      name: '实例信息配置',
+      data: 'deviceConfig',
+      desc: '诊断设备实例信息是否正确,配置错误将导致连接失败。',
+    };
+
+    const gateway = {
+      key: 'gateway',
+      name: '设备接入网关状态',
+      data: 'gateway',
+      desc: '诊断设备接入网关状态是否已启用,未启用的状态将导致连接失败',
+    };
+
+    const network = {
+      key: 'network',
+      name: '网络信息',
+      data: 'network',
+      desc: '诊断网络组件配置是否正确,配置错误将导致连接失败。',
+    };
+
     if (InstanceModel.detail?.state?.value === 'online') {
-      setList([
-        ...initList,
-        {
-          key: 'device-config',
-          name: '实例信息配置',
-          desc: '诊断设备实例信息是否正确,配置错误将导致连接失败。',
-        },
-        {
-          key: 'gateway',
-          name: '设备接入网关状态',
-          desc: '诊断设备接入网关状态是否已启用,未启用的状态将导致连接失败',
-        },
-        {
-          key: 'network',
-          name: '网络信息',
-          desc: '诊断网络组件配置是否正确,配置错误将导致连接失败。',
-        },
-      ]);
-      setTimeout(() => {
-        status.product = { status: 'success', text: '已发布', info: null };
-        status.config = { status: 'success', text: '正常', info: null };
-        status.device = { status: 'success', text: '已启用', info: null };
-        status['device-config'] = { status: 'success', text: '正常', info: null };
-        status.gateway = { status: 'success', text: '已启用', info: null };
-        status.network = { status: 'success', text: '网络正常', info: null };
-        setStatus({ ...status });
-        props.onChange('success');
-      }, 1000);
-    } else if (InstanceModel.detail) {
-      const datalist = [...initList];
-      const product = await service.queryProductState(InstanceModel.detail?.productId || '');
-      status.product = {
-        status: product.result?.state === 1 ? 'success' : 'error',
-        text: product.result?.state === 1 ? '已发布' : '未发布',
-        info:
-          product.result?.state === 1 ? null : (
+      setList([...datalist, deviceConfig, gateway, network]);
+      return [...datalist, deviceConfig, gateway, network];
+    } else {
+      if (proItem?.accessId && Array.isArray(configuration) && configuration.length > 0) {
+        datalist.push(deviceConfig);
+      }
+      if (proItem?.accessId) {
+        datalist.push(gateway);
+      }
+      if (proItem?.accessId && deviceConfigs?.channel === 'network') {
+        datalist.push(network);
+      }
+      setList([...datalist]);
+    }
+  };
+
+  const diagnoseProduct = (proItem: ProductItem) =>
+    new Promise((resolve) => {
+      let data: any = {};
+      if (InstanceModel.detail?.state?.value === 'online') {
+        data = { status: 'success', text: '已发布', info: null };
+      } else {
+        data = {
+          status: proItem?.state === 1 ? 'success' : 'error',
+          text: proItem?.state === 1 ? '已发布' : '未发布',
+          info:
+            proItem?.state === 1 ? null : (
+              <div className={styles.infoItem}>
+                <Badge
+                  status="default"
+                  text={
+                    <span>
+                      产品未发布,请
+                      <Popconfirm
+                        title="确认发布"
+                        onConfirm={async () => {
+                          const resp = await service.deployProduct(
+                            InstanceModel.detail?.productId || '',
+                          );
+                          if (resp.status === 200) {
+                            message.success('操作成功!');
+                            DiagnoseStatusModel.product = {
+                              status: 'success',
+                              text: '已发布',
+                              info: null,
+                            };
+                          }
+                        }}
+                      >
+                        <a>发布</a>
+                      </Popconfirm>
+                      产品
+                    </span>
+                  }
+                />
+              </div>
+            ),
+        };
+      }
+      DiagnoseStatusModel.status.product = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const diagnoseConfig = (proItem: ProductItem) =>
+    new Promise((resolve) => {
+      let data: any = {};
+      if (InstanceModel.detail?.state?.value === 'online') {
+        data = { status: 'success', text: '正常', info: null };
+      } else if (proItem?.accessId) {
+        data = {
+          status: 'warning',
+          text: '可能存在异常',
+          info: (
             <div className={styles.infoItem}>
               <Badge
                 status="default"
                 text={
                   <span>
-                    产品未发布,请
-                    <Popconfirm
-                      title="确认发布"
-                      onConfirm={async () => {
-                        const resp = await service.deployProduct(
-                          InstanceModel.detail?.productId || '',
+                    请检查
+                    <a
+                      onClick={() => {
+                        //跳转到产品设备接入配置
+                        const url = getMenuPathByParams(
+                          MENUS_CODE['device/Product/Detail'],
+                          InstanceModel.detail?.productId,
                         );
-                        if (resp.status === 200) {
-                          message.success('操作成功!');
-                          status.product = { status: 'success', text: '已发布', info: null };
-                          setStatus({ ...status });
-                        }
+                        const tab: any = window.open(`${origin}/#${url}?key=access`);
+                        tab!.onTabSaveSuccess = (value: any) => {
+                          if (value) {
+                            diagnoseConfig(proItem);
+                          }
+                        };
                       }}
                     >
-                      <a>发布</a>
-                    </Popconfirm>
-                    产品
+                      设备接入配置
+                    </a>
+                    是否正确填写
                   </span>
                 }
               />
             </div>
           ),
-      };
+        };
+      } else {
+        data = {
+          status: 'error',
+          text: '未配置',
+          info: (
+            <div className={styles.infoItem}>
+              <Badge
+                status="default"
+                text={
+                  <span>
+                    请进行
+                    <a
+                      onClick={() => {
+                        const url = getMenuPathByParams(
+                          MENUS_CODE['device/Product/Detail'],
+                          InstanceModel.detail?.productId,
+                        );
+                        const tab: any = window.open(`${origin}/#${url}?key=access`);
+                        tab!.onTabSaveSuccess = (value: any) => {
+                          if (value) {
+                            diagnoseConfig(proItem);
+                          }
+                        };
+                      }}
+                    >
+                      设备接入配置
+                    </a>
+                  </span>
+                }
+              />
+            </div>
+          ),
+        };
+      }
+      DiagnoseStatusModel.status.config = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const diagnoseDevice = () =>
+    new Promise((resolve) => {
+      let data: any = {};
       if (InstanceModel.detail?.state?.value === 'notActive') {
-        status.device = {
+        data = {
           status: 'error',
           text: '未启用',
           info: (
@@ -168,8 +242,11 @@ const Status = (props: Props) => {
                         const resp = await service.deployDevice(InstanceModel.detail?.id || '');
                         if (resp.status === 200) {
                           message.success('操作成功!');
-                          status.device = { status: 'success', text: '已启用', info: null };
-                          setStatus({ ...status });
+                          DiagnoseStatusModel.device = {
+                            status: 'success',
+                            text: '已启用',
+                            info: null,
+                          };
                           getDetail(InstanceModel.detail?.id || '');
                         }
                       }}
@@ -184,55 +261,19 @@ const Status = (props: Props) => {
           ),
         };
       } else {
-        status.device = { status: 'success', text: '已启用', info: null };
+        data = { status: 'success', text: '已启用', info: null };
       }
-      if (product.result?.accessId) {
-        const configuration = await service.queryProductConfig(
-          InstanceModel.detail?.productId || '',
-        );
-        if ((configuration?.result || []).length > 0) {
-          //实例信息
-          datalist.push({
-            key: 'device-config',
-            name: '实例信息配置',
-            desc: '诊断设备实例信息是否正确,配置错误将导致连接失败。',
-          });
-          status['device-config'] = {
-            status: 'warning',
-            text: '可能存在异常',
-            info: (
-              <div className={styles.infoItem}>
-                <Badge
-                  status="default"
-                  text={
-                    <span>
-                      请检查
-                      <a
-                        onClick={() => {
-                          //  跳转到设备实例页面
-                          const url = getMenuPathByParams(
-                            MENUS_CODE['device/Instance/Detail'],
-                            InstanceModel.detail?.id,
-                          );
-                          const tab: any = window.open(`${origin}/#${url}?key=detail`);
-                          tab!.onTabSaveSuccess = (value: any) => {
-                            if (value) {
-                              handleSearch();
-                            }
-                          };
-                        }}
-                      >
-                        设备实例信息
-                      </a>
-                      是否正确填写
-                    </span>
-                  }
-                />
-              </div>
-            ),
-          };
-        }
-        status.config = {
+      DiagnoseStatusModel.status.device = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const diagnoseDeviceConfig = (proItem: ProductItem, configuration: any) =>
+    new Promise((resolve) => {
+      let data: any = {};
+      if (InstanceModel.detail?.state?.value === 'online') {
+        data = { status: 'success', text: '正常', info: null };
+      } else if (proItem?.accessId && (configuration || []).length > 0) {
+        data = {
           status: 'warning',
           text: '可能存在异常',
           info: (
@@ -244,20 +285,20 @@ const Status = (props: Props) => {
                     请检查
                     <a
                       onClick={() => {
-                        //跳转到产品设备接入配置
+                        //  跳转到设备实例页面
                         const url = getMenuPathByParams(
-                          MENUS_CODE['device/Product/Detail'],
-                          InstanceModel.detail?.productId,
+                          MENUS_CODE['device/Instance/Detail'],
+                          InstanceModel.detail?.id,
                         );
-                        const tab: any = window.open(`${origin}/#${url}?key=access`);
+                        const tab: any = window.open(`${origin}/#${url}?key=detail`);
                         tab!.onTabSaveSuccess = (value: any) => {
                           if (value) {
-                            handleSearch();
+                            diagnoseDeviceConfig(proItem, configuration);
                           }
                         };
                       }}
                     >
-                      设备接入配置
+                      设备实例信息
                     </a>
                     是否正确填写
                   </span>
@@ -266,10 +307,20 @@ const Status = (props: Props) => {
             </div>
           ),
         };
-        const deviceConfig = await service.queryGatewayState(product.result?.accessId);
-        status.gateway = {
-          status: deviceConfig.result?.state?.value === 'enabled' ? 'success' : 'error',
-          text: deviceConfig.result?.state?.value === 'enabled' ? '已启用' : '未启用',
+      }
+      DiagnoseStatusModel.status.deviceConfig = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const diagnoseGateway = (proItem: ProductItem, deviceConfig: any) =>
+    new Promise((resolve) => {
+      let data: any = {};
+      if (InstanceModel.detail?.state?.value === 'online') {
+        data = { status: 'success', text: '已启用', info: null };
+      } else {
+        data = {
+          status: deviceConfig?.state?.value === 'enabled' ? 'success' : 'error',
+          text: deviceConfig?.state?.value === 'enabled' ? '已启用' : '未启用',
           info:
             deviceConfig.result?.state?.value === 'enabled' ? null : (
               <div className={styles.infoItem}>
@@ -281,11 +332,14 @@ const Status = (props: Props) => {
                       <Popconfirm
                         title="确认启用"
                         onConfirm={async () => {
-                          const resp = await service.startGateway(product.result?.accessId || '');
+                          const resp = await service.startGateway(proItem?.accessId || '');
                           if (resp.status === 200) {
                             message.success('操作成功!');
-                            status.gateway = { status: 'success', text: '已启用', info: null };
-                            setStatus({ ...status });
+                            DiagnoseStatusModel.gateway = {
+                              status: 'success',
+                              text: '已启用',
+                              info: null,
+                            };
                           }
                         }}
                       >
@@ -298,108 +352,145 @@ const Status = (props: Props) => {
               </div>
             ),
         };
-        datalist.push({
-          key: 'gateway',
-          name: '设备接入网关状态',
-          desc: '诊断设备接入网关状态是否已启用,未启用的状态将导致连接失败',
-        });
-        if (deviceConfig.result?.channel === 'network') {
-          const network = await service.queryNetworkState(deviceConfig.result?.channelId);
-          status.network = {
-            status: network.result?.state?.value === 'enabled' ? 'success' : 'error',
-            text: deviceConfig.result?.state?.value === 'enabled' ? '网络正常' : '网络异常',
-            info:
-              deviceConfig.result?.state?.value === 'enabled' ? null : (
-                <div>
-                  <div className={styles.infoItem}>
-                    <Badge
-                      status="default"
-                      text={
-                        <span>
-                          网络组件未启用, 请
-                          <Popconfirm
-                            title="确认启用"
-                            onConfirm={async () => {
-                              const resp = await service.startNetwork(
-                                deviceConfig.result?.channelId,
-                              );
-                              if (resp.status === 200) {
-                                message.success('操作成功!');
-                                status.gateway = { status: 'success', text: '已启用', info: null };
-                                setStatus({ ...status });
-                              }
-                            }}
-                          >
-                            <a>启用</a>
-                          </Popconfirm>
-                          网络组件
-                        </span>
-                      }
-                    />
-                  </div>
-                  <div className={styles.infoItem}>
-                    <Badge
-                      status="default"
-                      text="请检查服务器端口是否开放,如未开放,请开放后尝试重新连接"
-                    />
-                  </div>
-                  <div className={styles.infoItem}>
-                    <Badge
-                      status="default"
-                      text="请检查服务器防火策略,如有开启防火墙,请关闭防火墙或调整防火墙策略后重试"
-                    />
-                  </div>
+      }
+      DiagnoseStatusModel.status.gateway = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const diagnoseNetwork = (proItem: ProductItem, deviceConfig: any, network: any) =>
+    new Promise((resolve) => {
+      let data: any = {};
+      if (InstanceModel.detail?.state?.value === 'online') {
+        data = { status: 'success', text: '网络正常', info: null };
+      } else if (proItem?.accessId && deviceConfig?.channel === 'network') {
+        data = {
+          status: network?.state?.value === 'enabled' ? 'success' : 'error',
+          text: deviceConfig.result?.state?.value === 'enabled' ? '网络正常' : '网络异常',
+          info:
+            deviceConfig.result?.state?.value === 'enabled' ? null : (
+              <div>
+                <div className={styles.infoItem}>
+                  <Badge
+                    status="default"
+                    text={
+                      <span>
+                        网络组件未启用, 请
+                        <Popconfirm
+                          title="确认启用"
+                          onConfirm={async () => {
+                            const resp = await service.startNetwork(deviceConfig.result?.channelId);
+                            if (resp.status === 200) {
+                              message.success('操作成功!');
+                              DiagnoseStatusModel.gateway = {
+                                status: 'success',
+                                text: '已启用',
+                                info: null,
+                              };
+                            }
+                          }}
+                        >
+                          <a>启用</a>
+                        </Popconfirm>
+                        网络组件
+                      </span>
+                    }
+                  />
                 </div>
-              ),
-          };
-          datalist.push({
-            key: 'network',
-            name: '网络信息',
-            desc: '诊断网络组件配置是否正确,配置错误将导致连接失败。',
-          });
-        }
-      } else {
-        status.config = {
-          status: 'error',
-          text: '未配置',
-          info: (
-            <div className={styles.infoItem}>
-              <Badge
-                status="default"
-                text={
-                  <span>
-                    请进行
-                    <a
-                      onClick={() => {
-                        const url = getMenuPathByParams(
-                          MENUS_CODE['device/Product/Detail'],
-                          InstanceModel.detail?.productId,
-                        );
-                        const tab: any = window.open(`${origin}/#${url}?key=access`);
-                        tab!.onTabSaveSuccess = (value: any) => {
-                          if (value) {
-                            handleSearch();
-                          }
-                        };
-                      }}
-                    >
-                      设备接入配置
-                    </a>
-                  </span>
-                }
-              />
-            </div>
-          ),
+                <div className={styles.infoItem}>
+                  <Badge
+                    status="default"
+                    text="请检查服务器端口是否开放,如未开放,请开放后尝试重新连接"
+                  />
+                </div>
+                <div className={styles.infoItem}>
+                  <Badge
+                    status="default"
+                    text="请检查服务器防火策略,如有开启防火墙,请关闭防火墙或调整防火墙策略后重试"
+                  />
+                </div>
+              </div>
+            ),
         };
       }
-      setList([...datalist]);
-      setStatus({ ...status });
-      props.onChange('error');
+      DiagnoseStatusModel.status.network = data;
+      setTimeout(() => resolve(data), time);
+    });
+
+  const handleSearch = async () => {
+    props.onChange('loading');
+    DiagnoseStatusModel.status = {
+      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,
+      },
+    };
+    const proItem: any = await service.queryProductState(InstanceModel.detail?.productId || '');
+    const configuration = await service.queryProductConfig(InstanceModel.detail?.productId || '');
+    let deviceConfig: any = undefined;
+    let network: any = undefined;
+    if (proItem.result?.accessId) {
+      deviceConfig = await service.queryGatewayState(proItem.result?.accessId);
+    }
+    if (deviceConfig?.result?.channelId && deviceConfig?.channel === 'network') {
+      network = await service.queryNetworkState(deviceConfig?.channelId);
     }
+    initList(proItem.result, configuration.result, deviceConfig.result);
+    diagnoseProduct(proItem.result)
+      .then(() => diagnoseConfig(proItem.result))
+      .then(() => diagnoseDevice())
+      .then(() => diagnoseDeviceConfig(proItem?.result, configuration?.result))
+      .then(() => diagnoseGateway(proItem?.result, deviceConfig?.result))
+      .then(() => diagnoseNetwork(proItem?.result, deviceConfig.result, network?.result))
+      .then(() => {
+        let a = true;
+        Object.keys(DiagnoseStatusModel.status).forEach((key) => {
+          if (DiagnoseStatusModel.status[key].status !== 'success') {
+            a = false;
+          }
+        });
+        if (a) {
+          Store.set('diagnose-status', DiagnoseStatusModel.status);
+          props.onChange('success');
+        } else {
+          props.onChange('error');
+        }
+      });
   };
 
   useEffect(() => {
-    handleSearch();
+    if (!props.flag) {
+      handleSearch();
+    } else {
+      const dt = Store.get('diagnose-status');
+      DiagnoseStatusModel.status = dt;
+      props.onChange('success');
+    }
   }, []);
 
   return (
@@ -423,21 +514,30 @@ const Status = (props: Props) => {
                   <div className={styles.statusImg}>
                     <img
                       style={{ height: 32 }}
-                      className={status[item.key]?.status === 'loading' ? styles.loading : {}}
-                      src={StatusMap.get(status[item.key]?.status) || 'loading'}
+                      className={
+                        DiagnoseStatusModel.status[item.data]?.status === 'loading'
+                          ? styles.loading
+                          : {}
+                      }
+                      src={
+                        StatusMap.get(DiagnoseStatusModel.status[item.data]?.status) || 'loading'
+                      }
                     />
                   </div>
                   <div className={styles.statusContext}>
                     <div className={styles.statusTitle}>{item.name}</div>
                     <div className={styles.statusDesc}>{item.desc}</div>
-                    <div className={styles.info}>{status[item.key]?.info}</div>
+                    <div className={styles.info}>{DiagnoseStatusModel.status[item.data]?.info}</div>
                   </div>
                 </div>
                 <div
                   className={styles.statusRight}
-                  style={{ color: statusColor.get(status[item.key]?.status) || 'loading' }}
+                  style={{
+                    color:
+                      statusColor.get(DiagnoseStatusModel.status[item.data]?.status) || 'loading',
+                  }}
                 >
-                  {status[item.key]?.text}
+                  {DiagnoseStatusModel.status[item.data]?.text}
                 </div>
               </div>
             ))}
@@ -446,6 +546,6 @@ const Status = (props: Props) => {
       </Col>
     </Row>
   );
-};
+});
 
 export default Status;

+ 85 - 0
src/pages/device/Instance/Detail/Diagnose/Status/model.ts

@@ -0,0 +1,85 @@
+import { model } from '@formily/reactive';
+import type { ReactNode } from 'react';
+
+type StatusProps = {
+  status: 'loading' | 'error' | 'success' | 'warning';
+  text: string;
+  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;
+  };
+}>({
+  status: {
+    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,
+    },
+  },
+});

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

@@ -1,3 +1,12 @@
+.header {
+  width: 100%;
+}
+.header-message {
+  width: 100%;
+  background: url('/images/diagnose/back.png') no-repeat;
+  background-size: 100% 100%;
+}
+
 .container {
   margin-top: 20px;
 }

+ 37 - 21
src/pages/device/Instance/Detail/Diagnose/index.tsx

@@ -1,10 +1,12 @@
 import { Badge, Card, Col, Row } from 'antd';
 import type { ReactNode } from 'react';
+import { useEffect } from 'react';
 import { useState } from 'react';
 import Message from './Message';
 import Status from './Status';
 import './index.less';
 import classNames from 'classnames';
+import { Store } from 'jetlinks-store';
 
 interface ListProps {
   key: string;
@@ -40,6 +42,7 @@ const Diagnose = () => {
   const [current, setCurrent] = useState<string>('status');
   const [status, setStatus] = useState<string>('waiting');
   const [message, setMessage] = useState<string>('waiting');
+  const [active, setActive] = useState<boolean>(false);
 
   const [up, setUp] = useState<'success' | 'error' | 'waiting'>('waiting');
   const [down, setDown] = useState<'success' | 'error' | 'waiting'>('waiting');
@@ -94,7 +97,7 @@ const Diagnose = () => {
                     color={statusColor.get(up)}
                     text={
                       up === 'waiting'
-                        ? `诊断中`
+                        ? `上行消息通信诊断中`
                         : `上行消息通信${up === 'error' ? '异常' : '正常'}`
                     }
                   />
@@ -104,7 +107,7 @@ const Diagnose = () => {
                     color={statusColor.get(down)}
                     text={
                       down === 'waiting'
-                        ? `诊断中`
+                        ? `下行消息通信诊断中`
                         : `下行消息通信${down === 'error' ? '异常' : '正常'}`
                     }
                   />
@@ -116,30 +119,43 @@ const Diagnose = () => {
       ),
     },
   ];
+  useEffect(() => {
+    return () => {
+      Store.set('diagnose', []);
+      Store.set('diagnose-status', []);
+    };
+  }, []);
   return (
     <Card>
-      <Row gutter={24}>
-        {list.map((item: ListProps) => (
-          <Col
-            span={8}
-            key={item.key}
-            onClick={() => {
-              if (item.key === 'message' && status === 's-success-active') {
-                setCurrent(item.key);
-                setMessage('waiting');
-              }
-              if (item.key === 'status') {
-                setCurrent(item.key);
-              }
-            }}
-          >
-            {item.component}
-          </Col>
-        ))}
-      </Row>
+      <div className={current === 'message' ? 'header-message' : 'header'}>
+        <Row gutter={24} style={{ padding: 10 }}>
+          {list.map((item: ListProps) => (
+            <Col
+              span={8}
+              key={item.key}
+              onClick={() => {
+                if (current === item.key) {
+                  return;
+                }
+                if (item.key === 'message' && status === 's-success-active') {
+                  setCurrent(item.key);
+                  setMessage('waiting');
+                }
+                if (item.key === 'status') {
+                  setActive(true);
+                  setCurrent(item.key);
+                }
+              }}
+            >
+              {item.component}
+            </Col>
+          ))}
+        </Row>
+      </div>
       <div className="container">
         {current === 'status' ? (
           <Status
+            flag={active}
             onChange={(type: string) => {
               if (type === 'success') {
                 setStatus('s-success-active');

+ 196 - 198
src/pages/document.ejs

@@ -1,205 +1,205 @@
 <!DOCTYPE html>
 <html lang="en">
-<head>
-    <meta charset="UTF-8"/>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+  <head>
+    <meta charset="UTF-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
     <meta
-            name="keywords"
-            content="antd,umi,umijs,ant design,Scaffolding, layout, Ant Design, project, Pro, admin, jetlinks"
+      name="keywords"
+      content="antd,umi,umijs,ant design,Scaffolding, layout, Ant Design, project, Pro, admin, jetlinks"
     />
-    <meta name="description" content="Jetlinks"/>
+    <meta name="description" content="Jetlinks" />
     <meta
-            name="viewport"
-            content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
+      name="viewport"
+      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
     />
     <title>Jetlinks</title>
-    <link rel="icon" href="<%= context.config.publicPath + 'favicon.ico' %>" type="image/x-icon"/>
-</head>
-<body>
-<noscript>
-    <div class="noscript-container">
+    <link rel="icon" href="<%= context.config.publicPath + 'favicon.ico' %>" type="image/x-icon" />
+  </head>
+  <body>
+    <noscript>
+      <div class="noscript-container">
         Hi there! Please
         <div class="noscript-enableJS">
-            <a href="https://www.enablejavascript.io/en" target="_blank" rel="noopener noreferrer">
-                <b>enable Javascript</b>
-            </a>
+          <a href="https://www.enablejavascript.io/en" target="_blank" rel="noopener noreferrer">
+            <b>enable Javascript</b>
+          </a>
         </div>
         in your browser to use Ant Design, Out-of-the-box mid-stage front/design solution!
-    </div>
-</noscript>
-<div id="root">
-    <style>
-      html,
-      body,
-      #root {
-        height: 100%;
-        margin: 0;
-        padding: 0;
-      }
-
-      #root {
-        background-repeat: no-repeat;
-        background-size: 100% auto;
-      }
-
-      .noscript-container {
-        display: flex;
-        align-content: center;
-        justify-content: center;
-        margin-top: 90px;
-        font-size: 20px;
-        font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode',
-        Geneva, Verdana, sans-serif;
-      }
-
-      .noscript-enableJS {
-        padding-right: 3px;
-        padding-left: 3px;
-      }
-
-      .page-loading-warp {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        padding: 8px;
-      }
-
-      .ant-spin {
-        position: absolute;
-        display: none;
-        -webkit-box-sizing: border-box;
-        box-sizing: border-box;
-        margin: 0;
-        padding: 0;
-        color: rgba(0, 0, 0, 0.65);
-        color: #1890ff;
-        font-size: 14px;
-        font-variant: tabular-nums;
-        line-height: 1.5;
-        text-align: center;
-        list-style: none;
-        opacity: 0;
-        -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-        transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-        transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-        transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-        -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-        -webkit-font-feature-settings: 'tnum';
-        font-feature-settings: 'tnum';
-      }
-
-      .ant-spin-spinning {
-        position: static;
-        display: inline-block;
-        opacity: 1;
-      }
-
-      .ant-spin-dot {
-        position: relative;
-        display: inline-block;
-        width: 20px;
-        height: 20px;
-        font-size: 20px;
-      }
-
-      .ant-spin-dot-item {
-        position: absolute;
-        display: block;
-        width: 9px;
-        height: 9px;
-        background-color: #1890ff;
-        border-radius: 100%;
-        -webkit-transform: scale(0.75);
-        -ms-transform: scale(0.75);
-        transform: scale(0.75);
-        -webkit-transform-origin: 50% 50%;
-        -ms-transform-origin: 50% 50%;
-        transform-origin: 50% 50%;
-        opacity: 0.3;
-        -webkit-animation: antspinmove 1s infinite linear alternate;
-        animation: antSpinMove 1s infinite linear alternate;
-      }
-
-      .ant-spin-dot-item:nth-child(1) {
-        top: 0;
-        left: 0;
-      }
-
-      .ant-spin-dot-item:nth-child(2) {
-        top: 0;
-        right: 0;
-        -webkit-animation-delay: 0.4s;
-        animation-delay: 0.4s;
-      }
-
-      .ant-spin-dot-item:nth-child(3) {
-        right: 0;
-        bottom: 0;
-        -webkit-animation-delay: 0.8s;
-        animation-delay: 0.8s;
-      }
-
-      .ant-spin-dot-item:nth-child(4) {
-        bottom: 0;
-        left: 0;
-        -webkit-animation-delay: 1.2s;
-        animation-delay: 1.2s;
-      }
-
-      .ant-spin-dot-spin {
-        -webkit-transform: rotate(45deg);
-        -ms-transform: rotate(45deg);
-        transform: rotate(45deg);
-        -webkit-animation: antrotate 1.2s infinite linear;
-        animation: antRotate 1.2s infinite linear;
-      }
-
-      .ant-spin-lg .ant-spin-dot {
-        width: 32px;
-        height: 32px;
-        font-size: 32px;
-      }
-
-      .ant-spin-lg .ant-spin-dot i {
-        width: 14px;
-        height: 14px;
-      }
-
-      @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
-        .ant-spin-blur {
-          background: #fff;
-          opacity: 0.5;
-        }
-      }
-
-      @-webkit-keyframes antSpinMove {
-        to {
-          opacity: 1;
+      </div>
+    </noscript>
+    <div id="root">
+      <style>
+        html,
+        body,
+        #root {
+          height: 100%;
+          margin: 0;
+          padding: 0;
+        }
+
+        #root {
+          background-repeat: no-repeat;
+          background-size: 100% auto;
+        }
+
+        .noscript-container {
+          display: flex;
+          align-content: center;
+          justify-content: center;
+          margin-top: 90px;
+          font-size: 20px;
+          font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode',
+            Geneva, Verdana, sans-serif;
+        }
+
+        .noscript-enableJS {
+          padding-right: 3px;
+          padding-left: 3px;
         }
-      }
 
-      @keyframes antSpinMove {
-        to {
+        .page-loading-warp {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          padding: 8px;
+        }
+
+        .ant-spin {
+          position: absolute;
+          display: none;
+          -webkit-box-sizing: border-box;
+          box-sizing: border-box;
+          margin: 0;
+          padding: 0;
+          color: rgba(0, 0, 0, 0.65);
+          color: #1890ff;
+          font-size: 14px;
+          font-variant: tabular-nums;
+          line-height: 1.5;
+          text-align: center;
+          list-style: none;
+          opacity: 0;
+          -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+          transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+          transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+          transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+            -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+          -webkit-font-feature-settings: 'tnum';
+          font-feature-settings: 'tnum';
+        }
+
+        .ant-spin-spinning {
+          position: static;
+          display: inline-block;
           opacity: 1;
         }
-      }
 
-      @-webkit-keyframes antRotate {
-        to {
-          -webkit-transform: rotate(405deg);
-          transform: rotate(405deg);
+        .ant-spin-dot {
+          position: relative;
+          display: inline-block;
+          width: 20px;
+          height: 20px;
+          font-size: 20px;
+        }
+
+        .ant-spin-dot-item {
+          position: absolute;
+          display: block;
+          width: 9px;
+          height: 9px;
+          background-color: #1890ff;
+          border-radius: 100%;
+          -webkit-transform: scale(0.75);
+          -ms-transform: scale(0.75);
+          transform: scale(0.75);
+          -webkit-transform-origin: 50% 50%;
+          -ms-transform-origin: 50% 50%;
+          transform-origin: 50% 50%;
+          opacity: 0.3;
+          -webkit-animation: antspinmove 1s infinite linear alternate;
+          animation: antSpinMove 1s infinite linear alternate;
+        }
+
+        .ant-spin-dot-item:nth-child(1) {
+          top: 0;
+          left: 0;
         }
-      }
 
-      @keyframes antRotate {
-        to {
-          -webkit-transform: rotate(405deg);
-          transform: rotate(405deg);
+        .ant-spin-dot-item:nth-child(2) {
+          top: 0;
+          right: 0;
+          -webkit-animation-delay: 0.4s;
+          animation-delay: 0.4s;
         }
-      }
-    </style>
-    <div
-            style="
+
+        .ant-spin-dot-item:nth-child(3) {
+          right: 0;
+          bottom: 0;
+          -webkit-animation-delay: 0.8s;
+          animation-delay: 0.8s;
+        }
+
+        .ant-spin-dot-item:nth-child(4) {
+          bottom: 0;
+          left: 0;
+          -webkit-animation-delay: 1.2s;
+          animation-delay: 1.2s;
+        }
+
+        .ant-spin-dot-spin {
+          -webkit-transform: rotate(45deg);
+          -ms-transform: rotate(45deg);
+          transform: rotate(45deg);
+          -webkit-animation: antrotate 1.2s infinite linear;
+          animation: antRotate 1.2s infinite linear;
+        }
+
+        .ant-spin-lg .ant-spin-dot {
+          width: 32px;
+          height: 32px;
+          font-size: 32px;
+        }
+
+        .ant-spin-lg .ant-spin-dot i {
+          width: 14px;
+          height: 14px;
+        }
+
+        @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
+          .ant-spin-blur {
+            background: #fff;
+            opacity: 0.5;
+          }
+        }
+
+        @-webkit-keyframes antSpinMove {
+          to {
+            opacity: 1;
+          }
+        }
+
+        @keyframes antSpinMove {
+          to {
+            opacity: 1;
+          }
+        }
+
+        @-webkit-keyframes antRotate {
+          to {
+            -webkit-transform: rotate(405deg);
+            transform: rotate(405deg);
+          }
+        }
+
+        @keyframes antRotate {
+          to {
+            -webkit-transform: rotate(405deg);
+            transform: rotate(405deg);
+          }
+        }
+      </style>
+      <div
+        style="
           display: flex;
           flex-direction: column;
           align-items: center;
@@ -207,27 +207,25 @@
           height: 100%;
           min-height: 420px;
         "
-    >
+      >
         <!--        <img src="<%= context.config.publicPath + 'pro_icon.svg' %>" alt="logo" width="256"/>-->
         <!--        <div style="font-size: xx-large">数据加载中...</div>-->
         <div class="page-loading-warp">
-            <div class="ant-spin ant-spin-lg ant-spin-spinning">
+          <div class="ant-spin ant-spin-lg ant-spin-spinning">
             <span class="ant-spin-dot ant-spin-dot-spin"
-            ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
-                ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
-                ></span>
-            </div>
+              ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
+              ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
+            ></span>
+          </div>
         </div>
         <div style="display: flex; align-items: center; justify-content: center">
-
-            <img
-                    src="<%= context.config.publicPath + 'logo.svg' %>"
-                    width="200"
-                    style="margin-right: 8px"
-            />
-
+          <img
+            src="<%= context.config.publicPath + 'logo.svg' %>"
+            width="200"
+            style="margin-right: 8px"
+          />
         </div>
+      </div>
     </div>
-</div>
-</body>
+  </body>
 </html>

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

@@ -20,6 +20,7 @@ import { useHistory } from 'umi';
 import ReactMarkdown from 'react-markdown';
 import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { ExclamationCircleFilled } from '@ant-design/icons';
+import TitleComponent from '@/components/TitleComponent';
 
 interface Props {
   change: () => void;
@@ -466,7 +467,7 @@ const Access = (props: Props) => {
           <Row gutter={24}>
             <Col span={12}>
               <div className={styles.info}>
-                <div className={styles.title}>基本信息</div>
+                <TitleComponent data={'基本信息'} />
                 <Form name="basic" layout="vertical" form={form}>
                   <Form.Item
                     label="名称"

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

@@ -25,7 +25,7 @@ const Protocol = () => {
     if (resp.status === 200) {
       message.success('操作成功!');
     } else {
-      message.error('操作失败!');
+      message.error(resp?.message || '操作失败');
     }
     actionRef.current?.reload();
   };

+ 18 - 14
src/pages/system/Role/Detail/Permission/index.tsx

@@ -44,15 +44,23 @@ const Permission = () => {
 
   const getDataList: any = (data1: any[]) => {
     if (Array.isArray(data1) && data1.length > 0) {
-      return data1.map((item) => {
-        const check = item.check;
-        delete item.check;
-        return {
-          ...item,
-          granted: check === 1,
-          children: item?.children,
-        };
-      });
+      return data1
+        .filter(
+          (i) =>
+            i.check === 1 ||
+            (i?.buttons || []).filter((it: any) => it.granted).length > 0 ||
+            (i?.assetAccesses).filter((it: any) => it.granted).length > 0,
+        )
+        .map((item) => {
+          const check = item.check;
+          delete item.check;
+          return {
+            ...item,
+            granted:
+              check === 1 || (item?.buttons || []).filter((it: any) => it.granted).length > 0,
+            children: item?.children,
+          };
+        });
     }
     return [];
   };
@@ -68,11 +76,7 @@ const Permission = () => {
             name: values?.name,
             description: values?.description || '',
           });
-          const list = (
-            getDataList(flattenArray([...values.permission?.children]) || []) || []
-          ).filter((item: any) => {
-            return item.granted || (item?.assetAccesses).filter((i: any) => i.granted).length > 0;
-          });
+          const list = getDataList(flattenArray([...values.permission?.children]) || []) || [];
           service
             .saveGrantTree('role', params?.id, {
               menus: list || [],