sun-chaochao 3 лет назад
Родитель
Сommit
1b74f44519
29 измененных файлов с 1977 добавлено и 1416 удалено
  1. 0 0
      public/images/diagnose copy/back.png
  2. 0 0
      public/images/diagnose copy/message-error.png
  3. 0 0
      public/images/diagnose copy/status-error.png
  4. 0 0
      public/images/diagnose copy/status-success-active.png
  5. 0 0
      public/images/diagnose copy/status-success.png
  6. BIN
      public/images/diagnose copy/status/error.png
  7. BIN
      public/images/diagnose copy/status/loading.png
  8. BIN
      public/images/diagnose copy/status/success.png
  9. BIN
      public/images/diagnose copy/status/warning.png
  10. 0 0
      public/images/diagnose copy/waiting.png
  11. BIN
      public/images/diagnose/error.png
  12. BIN
      public/images/diagnose/loading-1.png
  13. BIN
      public/images/diagnose/loading-2.png
  14. BIN
      public/images/diagnose/success.png
  15. 3 0
      src/pages/device/Instance/Detail/Diagnose/Message/index.less
  16. 152 132
      src/pages/device/Instance/Detail/Diagnose/Message/index.tsx
  17. 0 136
      src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx
  18. 70 121
      src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx
  19. 0 7
      src/pages/device/Instance/Detail/Diagnose/Status/index.less
  20. 1195 725
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  21. 324 74
      src/pages/device/Instance/Detail/Diagnose/Status/model.ts
  22. 69 25
      src/pages/device/Instance/Detail/Diagnose/index.less
  23. 106 183
      src/pages/device/Instance/Detail/Diagnose/index.tsx
  24. 1 0
      src/pages/device/Instance/typings.d.ts
  25. 13 9
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  26. 5 0
      src/pages/link/AccessConfig/service.ts
  27. 33 3
      src/pages/rule-engine/Alarm/Config/index.tsx
  28. 3 0
      src/pages/system/Relationship/index.tsx
  29. 3 1
      src/pages/system/Role/index.tsx

public/images/diagnose/back.png → public/images/diagnose copy/back.png


public/images/diagnose/message-error.png → public/images/diagnose copy/message-error.png


public/images/diagnose/status-error.png → public/images/diagnose copy/status-error.png


public/images/diagnose/status-success-active.png → public/images/diagnose copy/status-success-active.png


public/images/diagnose/status-success.png → public/images/diagnose copy/status-success.png


BIN
public/images/diagnose copy/status/error.png


BIN
public/images/diagnose copy/status/loading.png


BIN
public/images/diagnose copy/status/success.png


BIN
public/images/diagnose copy/status/warning.png


public/images/diagnose/waiting.png → public/images/diagnose copy/waiting.png


BIN
public/images/diagnose/error.png


BIN
public/images/diagnose/loading-1.png


BIN
public/images/diagnose/loading-2.png


BIN
public/images/diagnose/success.png


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

@@ -1,3 +1,6 @@
+.message-status {
+  padding: 8px 24px;
+}
 .content {
   width: 100%;
 }

+ 152 - 132
src/pages/device/Instance/Detail/Diagnose/Message/index.tsx

@@ -1,8 +1,8 @@
 import TitleComponent from '@/components/TitleComponent';
 import './index.less';
 import Dialog from './Dialog';
-import { Button, Col, DatePicker, Empty, Input, InputNumber, Row, Select } from 'antd';
-import { useEffect, useState } from 'react';
+import { Badge, Button, Col, DatePicker, Empty, Input, InputNumber, Row, Select } from 'antd';
+import { useEffect, useMemo, useState } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
@@ -20,19 +20,13 @@ import {
 } from '@formily/antd';
 import { randomString } from '@/utils/util';
 import Log from './Log';
-import { Store } from 'jetlinks-store';
-
-interface Props {
-  onChange: (type: string) => void;
-}
+import { DiagnoseStatusModel, messageStatusMap, messageStyleMap } from '../Status/model';
+import { observer } from '@formily/reactive-react';
 
 const DatePicker1: any = DatePicker;
 
-const Message = (props: Props) => {
+const Message = observer(() => {
   const [subscribeTopic] = useSendWebsocketMessage();
-  const [dialogList, setDialogList] = useState<any[]>([]);
-  const [tempList, setTempList] = useState<any[]>([]);
-  const [logList, setLogList] = useState<any[]>([]);
   const [type, setType] = useState<'property' | 'function'>('function');
   const [input, setInput] = useState<any>({});
   const [inputs, setInputs] = useState<any[]>([]);
@@ -50,17 +44,19 @@ const Message = (props: Props) => {
       ?.pipe(map((res) => res.payload))
       .subscribe((payload: any) => {
         if (payload.type === 'log') {
-          logList.push({
-            key: randomString(),
-            ...payload,
-          });
-          setLogList([...logList]);
+          DiagnoseStatusModel.logList = [
+            ...DiagnoseStatusModel.logList,
+            {
+              key: randomString(),
+              ...payload,
+            },
+          ];
         } else {
-          tempList.push({
-            key: randomString(),
-            ...payload,
-          });
-          const flag = [...tempList]
+          DiagnoseStatusModel.allDialogList = [
+            ...DiagnoseStatusModel.logList,
+            { key: randomString(), ...payload },
+          ];
+          const flag = [...DiagnoseStatusModel.allDialogList]
             .filter(
               (i) =>
                 i.traceId === payload.traceId &&
@@ -69,21 +65,26 @@ const Message = (props: Props) => {
             .every((item) => {
               return !item.error;
             });
-          if (!flag) {
-            props.onChange(!payload.upstream ? 'down-error' : 'up-error');
+          if (!payload.upstream) {
+            DiagnoseStatusModel.message.down = {
+              text: !flag ? '下行消息通信异常' : '下行消息通信正常',
+              status: !flag ? 'error' : 'success',
+            };
           } else {
-            props.onChange(!payload.upstream ? 'down-success' : 'up-success');
+            DiagnoseStatusModel.message.up = {
+              text: !flag ? '上行消息通信异常' : '上行消息通信正常',
+              status: !flag ? 'error' : 'success',
+            };
           }
-          setTempList([...tempList]);
-          Store.set('temp', tempList);
-          const t = dialogList.find(
+          const list = [...DiagnoseStatusModel.dialogList];
+          const t = list.find(
             (item) =>
               item.traceId === payload.traceId &&
               payload.downstream === item.downstream &&
               payload.upstream === item.upstream,
           );
           if (t) {
-            dialogList.map((item) => {
+            list.map((item) => {
               if (item.key === payload.traceId) {
                 item.list.push({
                   key: randomString(),
@@ -92,7 +93,7 @@ const Message = (props: Props) => {
               }
             });
           } else {
-            dialogList.push({
+            list.push({
               key: randomString(),
               traceId: payload.traceId,
               downstream: payload.downstream,
@@ -105,8 +106,7 @@ const Message = (props: Props) => {
               ],
             });
           }
-          setDialogList([...dialogList]);
-          Store.set('diagnose', dialogList);
+          DiagnoseStatusModel.dialogList = [...list];
         }
         const chatBox = document.getElementById('dialog');
         if (chatBox) {
@@ -166,44 +166,46 @@ const Message = (props: Props) => {
     }
   };
   useEffect(() => {
-    subscribeLog();
-    const arr = Store.get('diagnose') || [];
-    setDialogList(arr);
-    const temp = Store.get('temp') || [];
-    setTempList(temp);
-  }, []);
+    if (DiagnoseStatusModel.state === 'success') {
+      subscribeLog();
+    }
+  }, [DiagnoseStatusModel.state]);
 
-  const form = createForm({
-    initialValues: {
-      data: [...inputs],
-    },
-    effects() {
-      onFieldReact('data.*.valueType.type', (field) => {
-        const value = (field as Field).value;
-        const format = field.query('..value').take() as any;
-        if (format) {
-          switch (value) {
-            case 'date':
-              format.setComponent(FDatePicker);
-              break;
-            case 'boolean':
-              format.setComponent(FSelect);
-              format.setDataSource([
-                { label: '是', value: true },
-                { label: '否', value: false },
-              ]);
-              break;
-            case 'int':
-              format.setComponent(NumberPicker);
-              break;
-            default:
-              format.setComponent(FInput);
-              break;
-          }
-        }
-      });
-    },
-  });
+  const form = useMemo(
+    () =>
+      createForm({
+        initialValues: {
+          data: [...inputs],
+        },
+        effects() {
+          onFieldReact('data.*.valueType.type', (field) => {
+            const value = (field as Field).value;
+            const format = field.query('..value').take() as any;
+            if (format) {
+              switch (value) {
+                case 'date':
+                  format.setComponent(FDatePicker);
+                  break;
+                case 'boolean':
+                  format.setComponent(FSelect);
+                  format.setDataSource([
+                    { label: '是', value: true },
+                    { label: '否', value: false },
+                  ]);
+                  break;
+                case 'int':
+                  format.setComponent(NumberPicker);
+                  break;
+                default:
+                  format.setComponent(FInput);
+                  break;
+              }
+            }
+          });
+        },
+      }),
+    [],
+  );
 
   const dataRender = () => {
     switch (type) {
@@ -352,69 +354,87 @@ const Message = (props: Props) => {
   return (
     <Row gutter={24}>
       <Col span={16}>
-        <TitleComponent data="调试" />
-        <div className="content">
-          <div className="dialog" id="dialog">
-            {dialogList.map((item) => (
-              <Dialog data={item} key={item.key} />
-            ))}
+        <div>
+          <div style={{ marginBottom: 20 }}>
+            <Row gutter={24}>
+              {Object.keys(DiagnoseStatusModel.message).map((key) => {
+                const obj = DiagnoseStatusModel.message[key];
+                return (
+                  <Col key={key} span={12}>
+                    <div style={messageStyleMap.get(obj.status)} className="message-status">
+                      <Badge status={messageStatusMap.get(obj.status)} />
+                      {obj.text}
+                    </div>
+                  </Col>
+                );
+              })}
+            </Row>
           </div>
-        </div>
-        <div className="function">
-          <Row gutter={24}>
-            <Col span={5}>
-              <Select
-                value={type}
-                placeholder="请选择"
-                style={{ width: '100%' }}
-                onChange={(value) => {
-                  setType(value);
-                  setInputs([]);
-                  setInput({});
-                }}
-              >
-                <Select.Option value="function">调用功能</Select.Option>
-                <Select.Option value="property">操作属性</Select.Option>
-              </Select>
-            </Col>
-            {dataRender()}
-            <Col span={3}>
-              <Button
-                type="primary"
-                onClick={async () => {
-                  props.onChange('waiting');
-                  if (type === 'function') {
-                    const list = (form?.values?.data || []).filter((it) => !!it.value);
-                    const obj = {};
-                    list.map((it) => {
-                      obj[it.id] = it.value;
-                    });
-                    await service.executeFunctions(InstanceModel.detail?.id || '', input.id, {
-                      ...obj,
-                    });
-                  } else {
-                    if (propertyType === 'read') {
-                      await service.readProperties(InstanceModel.detail?.id || '', [property]);
-                    } else {
-                      await service.settingProperties(InstanceModel.detail?.id || '', {
-                        [property]: propertyValue,
-                      });
-                    }
-                  }
-                }}
-              >
-                发送
-              </Button>
-            </Col>
-          </Row>
-          {inputs.length > 0 && (
-            <div className="parameter">
-              <h4>功能参数</h4>
-              <FormProvider form={form}>
-                <SchemaField schema={schema} />
-              </FormProvider>
+          <div>
+            <TitleComponent data="调试" />
+            <div className="content">
+              <div className="dialog" id="dialog">
+                {DiagnoseStatusModel.dialogList.map((item) => (
+                  <Dialog data={item} key={item.key} />
+                ))}
+              </div>
             </div>
-          )}
+            <div className="function">
+              <Row gutter={24}>
+                <Col span={5}>
+                  <Select
+                    value={type}
+                    placeholder="请选择"
+                    style={{ width: '100%' }}
+                    onChange={(value) => {
+                      setType(value);
+                      setInputs([]);
+                      setInput({});
+                    }}
+                  >
+                    <Select.Option value="function">调用功能</Select.Option>
+                    <Select.Option value="property">操作属性</Select.Option>
+                  </Select>
+                </Col>
+                {dataRender()}
+                <Col span={3}>
+                  <Button
+                    type="primary"
+                    onClick={async () => {
+                      if (type === 'function') {
+                        const list = (form?.values?.data || []).filter((it) => !!it.value);
+                        const obj = {};
+                        list.map((it) => {
+                          obj[it.id] = it.value;
+                        });
+                        await service.executeFunctions(InstanceModel.detail?.id || '', input.id, {
+                          ...obj,
+                        });
+                      } else {
+                        if (propertyType === 'read') {
+                          await service.readProperties(InstanceModel.detail?.id || '', [property]);
+                        } else {
+                          await service.settingProperties(InstanceModel.detail?.id || '', {
+                            [property]: propertyValue,
+                          });
+                        }
+                      }
+                    }}
+                  >
+                    发送
+                  </Button>
+                </Col>
+              </Row>
+              {inputs.length > 0 && (
+                <div className="parameter">
+                  <h4>功能参数</h4>
+                  <FormProvider form={form}>
+                    <SchemaField schema={schema} />
+                  </FormProvider>
+                </div>
+              )}
+            </div>
+          </div>
         </div>
       </Col>
       <Col span={8}>
@@ -430,8 +450,8 @@ const Message = (props: Props) => {
         >
           <TitleComponent data={'日志'} />
           <div style={{ marginTop: 10 }}>
-            {logList.length > 0 ? (
-              logList.map((item) => <Log data={item} key={item.key} />)
+            {DiagnoseStatusModel.logList.length > 0 ? (
+              DiagnoseStatusModel.logList.map((item) => <Log data={item} key={item.key} />)
             ) : (
               <Empty />
             )}
@@ -440,6 +460,6 @@ const Message = (props: Props) => {
       </Col>
     </Row>
   );
-};
+});
 
 export default Message;

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

@@ -1,136 +0,0 @@
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
-import { InfoCircleOutlined } 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) {
-        props.close();
-      }
-    };
-  };
-
-  return (
-    <Modal
-      title="诊断建议"
-      onCancel={() => {
-        props.close();
-      }}
-      onOk={() => {
-        props.close();
-      }}
-      width={700}
-      visible
-    >
-      <div className={styles.advice}>
-        <div className={styles.alert}>
-          <InfoCircleOutlined 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 className={styles.infoItem}>
-          <Badge status="default" text={'请检查设备是否已开机'} />
-        </div>
-      </div>
-    </Modal>
-  );
-};
-
-export default DiagnosticAdvice;

+ 70 - 121
src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx

@@ -1,99 +1,23 @@
-import { createForm } from '@formily/core';
-import { createSchemaField } from '@formily/react';
-import type { ISchema } from '@formily/json-schema';
-import { Form, FormGrid, FormItem, Input, Password, PreviewText } from '@formily/antd';
-import { Modal } from 'antd';
+import { Button, Descriptions, Modal } from 'antd';
 import styles from './index.less';
 import { InfoCircleOutlined } from '@ant-design/icons';
-
-const componentMap = {
-  string: 'Input',
-  password: 'Password',
-};
-
+import { useHistory } from 'umi';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { InstanceModel } from '@/pages/device/Instance';
 interface Props {
   close: () => void;
   data: any;
-  ok: (data: any) => void;
+  save: (data: any) => void;
 }
 
 const ManualInspection = (props: Props) => {
   const { data } = props;
 
-  const form = createForm({
-    validateFirst: true,
-    initialValues: {},
-  });
-
-  const SchemaField = createSchemaField({
-    components: {
-      FormItem,
-      Input,
-      Password,
-      FormGrid,
-      PreviewText,
-    },
-  });
-
-  const configToSchema = (list: any[]) => {
-    const config = {};
-    list.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: '',
-          placeholder: `请输入${item.name}`,
-        },
-      };
-    });
-    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(data?.data?.properties),
-        },
-      },
-    };
+  const history = useHistory<Record<string, string>>();
 
-    return (
-      <>
-        <PreviewText.Placeholder value="-">
-          <Form form={form} layout="vertical">
-            <SchemaField schema={itemSchema} />
-          </Form>
-        </PreviewText.Placeholder>
-      </>
-    );
+  const okBtn = () => {
+    props.save(data);
   };
-  const renderComponent = () => (
-    <div style={{ backgroundColor: '#f6f6f6', padding: 10 }}>
-      {(data?.data?.properties || []).map((item: any) => (
-        <div key={item?.property}>
-          <span>{item?.name}</span>:{' '}
-          <span>
-            {data?.check && data?.check[item?.property] ? data?.check[item?.property] : '--'}
-          </span>
-        </div>
-      ))}
-    </div>
-  );
 
   return (
     <Modal
@@ -101,47 +25,72 @@ const ManualInspection = (props: Props) => {
       onCancel={() => {
         props.close();
       }}
-      width={600}
-      onOk={async () => {
-        const values = (await form.submit()) as any;
-        if (!data?.check) {
-          props.ok({
-            status: 'error',
-            data: data,
-          });
-        } else {
-          let flag = true;
-          Object.keys(values).forEach((key) => {
-            if (values[key] !== data?.check[key]) {
-              flag = false;
+      width={800}
+      footer={[
+        <Button
+          key="back"
+          onClick={() => {
+            if (data.type === 'device') {
+              InstanceModel.active = 'detail';
+            } else {
+              history.push(
+                `${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], data.productId)}`,
+                {
+                  tab: 'access',
+                },
+              );
             }
-          });
-          if (flag) {
-            props.ok({
-              status: 'success',
-              data: data,
-            });
-          } else {
-            props.ok({
-              status: 'error',
-              data: data,
-            });
-          }
-        }
-      }}
+            props.close();
+          }}
+        >
+          去修改
+        </Button>,
+        <Button
+          key="submit"
+          onClick={() => {
+            okBtn();
+          }}
+        >
+          确认无误
+        </Button>,
+      ]}
       visible
     >
-      <div className={styles.alert}>
-        <InfoCircleOutlined style={{ marginRight: 10 }} />
-        {data.type === 'product'
-          ? `当前填写的数据将与产品-设备接入配置中的${data.data.name}数据进行比对`
-          : `当前填写的数据将与设备-实例信息配置中的${data.data.name}数据进行比对`}
-      </div>
-      <div style={{ marginTop: 10 }}>
-        已配置参数
-        {renderComponent()}
+      <div style={{ display: 'flex' }}>
+        <div style={{ flex: 1 }}>
+          <div className={styles.alert}>
+            <InfoCircleOutlined style={{ marginRight: 10 }} />
+            请检查配置项是否填写正确,若您确定该项无需诊断可
+            <a
+              onClick={() => {
+                okBtn();
+              }}
+            >
+              忽略
+            </a>
+          </div>
+          <div style={{ marginTop: 10 }}>
+            <Descriptions title={data?.data?.name} layout="vertical" bordered>
+              {(data?.data?.properties || []).map((item: any) => (
+                <Descriptions.Item
+                  key={item.property}
+                  label={`${item.name}${item?.description ? `(${item.description})` : ''}`}
+                >
+                  {data?.configuration[item.property] || ''}
+                </Descriptions.Item>
+              ))}
+            </Descriptions>
+          </div>
+        </div>
+        {data?.data?.description ? (
+          <div style={{ width: '50%', border: '1px solid #f0f0f0' }}>
+            <h4>诊断项说明</h4>
+            <p>{data?.data?.description}</p>
+          </div>
+        ) : (
+          ''
+        )}
       </div>
-      <div style={{ marginTop: 10 }}>{renderConfigCard()}</div>
     </Modal>
   );
 };

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

@@ -88,10 +88,3 @@
   line-height: 40px;
   background-color: #f6f6f6;
 }
-
-.advice {
-  .infoItem {
-    width: 100%;
-    margin: 10px;
-  }
-}

Разница между файлами не показана из-за своего большого размера
+ 1195 - 725
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx


+ 324 - 74
src/pages/device/Instance/Detail/Diagnose/Status/model.ts

@@ -1,92 +1,342 @@
+import type { ProductItem } from '@/pages/device/Product/typings';
 import { model } from '@formily/reactive';
 import type { ReactNode } from 'react';
 
-type StatusProps = {
-  status: 'loading' | 'error' | 'success' | 'warning';
-  text: string;
-  info: null | ReactNode;
-};
+export 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'));
 
-type ListProps = {
+export type ListProps = {
   key: string;
   name: string;
-  data: string;
-  desc: string;
+  desc?: string;
+  status: 'loading' | 'error' | 'success' | 'warning';
+  text?: string;
+  info?: ReactNode;
 };
 
+export const list = [
+  { key: 'status', text: '连接状态' },
+  { key: 'message', text: '消息通信' },
+];
+
+export const textColorMap = new Map();
+textColorMap.set('loading', 'black');
+textColorMap.set('error', 'red');
+textColorMap.set('success', 'green');
+textColorMap.set('warning', 'red');
+
+export const networkInitList: ListProps[] = [
+  // {
+  //   key: 'access',
+  //   name: '设备接入配置',
+  //   desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
+  //   status: 'loading',
+  //   text: '正在诊断中...',
+  //   info: null,
+  // },
+  {
+    key: 'network',
+    name: '网络组件',
+    desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'gateway',
+    name: '设备接入网关',
+    desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'product',
+    name: '产品状态',
+    desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'device',
+    name: '设备状态',
+    desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+];
+
+export const childInitList: ListProps[] = [
+  // {
+  //   key: 'access',
+  //   name: '设备接入配置',
+  //   desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
+  //   status: 'loading',
+  //   text: '正在诊断中...',
+  //   info: null,
+  // },
+  {
+    key: 'network',
+    name: '网络组件',
+    desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'gateway',
+    name: '设备接入网关',
+    desc: '诊断设备接入网关状态是否正常,网关配置是否正确',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'parent-device',
+    name: '网关父设备',
+    desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'product',
+    name: '产品状态',
+    desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'device',
+    name: '设备状态',
+    desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+];
+
+export const cloudInitList: ListProps[] = [
+  // {
+  //   key: 'access',
+  //   name: '设备接入配置',
+  //   desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
+  //   status: 'loading',
+  //   text: '正在诊断中...',
+  //   info: null,
+  // },
+  {
+    key: 'gateway',
+    name: '设备接入网关',
+    desc: '诊断设备接入网关状态是否正常,网关配置是否正确',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'product',
+    name: '产品状态',
+    desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'device',
+    name: '设备状态',
+    desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+];
+
+export const channelInitList: ListProps[] = [
+  // {
+  //   key: 'access',
+  //   name: '设备接入配置',
+  //   desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
+  //   status: 'loading',
+  //   text: '正在诊断中...',
+  //   info: null,
+  // },
+  {
+    key: 'gateway',
+    name: '设备接入网关',
+    desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'product',
+    name: '产品状态',
+    desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'device',
+    name: '设备状态',
+    desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+];
+
+export const mediaInitList: ListProps[] = [
+  // {
+  //   key: 'access',
+  //   name: '设备接入配置',
+  //   desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。',
+  //   status: 'loading',
+  //   text: '正在诊断中...',
+  //   info: null,
+  // },
+  {
+    key: 'gateway',
+    name: '设备接入网关',
+    desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'product',
+    name: '产品状态',
+    desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+  {
+    key: 'device',
+    name: '设备状态',
+    desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
+    status: 'loading',
+    text: '正在诊断中...',
+    info: null,
+  },
+];
+
 export const DiagnoseStatusModel = model<{
-  status: {
-    config?: StatusProps;
-    network?: StatusProps;
-    product?: StatusProps;
-    device?: StatusProps;
-    productAuth?: StatusProps;
-    deviceAuth?: StatusProps;
-    deviceAccess?: StatusProps;
-    other?: StatusProps;
-  };
   list: ListProps[];
-  model: boolean;
+  product: Partial<ProductItem>;
   gateway: any;
+  configuration: {
+    product: any[];
+    device: any[];
+  };
+  percent: number;
+  state: 'loading' | 'success' | 'error'; // 上面的状态
+  status: 'loading' | 'finish'; // 检验是否完成检验过程
+  count: number;
+  logList: any[];
+  dialogList: any[];
+  allDialogList: any[];
+  message: {
+    up: {
+      text: string;
+      status: 'loading' | 'success' | 'error';
+    };
+    down: {
+      text: string;
+      status: 'loading' | 'success' | 'error';
+    };
+  };
 }>({
-  status: {
-    config: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    network: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    product: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    device: {
-      status: 'loading',
-      text: '正在诊断中...',
-      info: null,
-    },
-    deviceAccess: {
+  list: [],
+  product: {},
+  gateway: {},
+  configuration: {
+    product: [],
+    device: [],
+  },
+  state: 'loading',
+  status: 'loading',
+  percent: 0,
+  count: 0,
+  logList: [],
+  dialogList: [],
+  allDialogList: [],
+  message: {
+    up: {
+      text: '上行消息诊断中',
       status: 'loading',
-      text: '正在诊断中...',
-      info: null,
     },
-    other: {
+    down: {
+      text: '下行消息诊断中',
       status: 'loading',
-      text: '正在诊断中...',
-      info: null,
     },
   },
-  list: [
-    {
-      key: 'config',
-      name: '设备接入配置',
-      data: 'config',
-      desc: '诊断设备接入配置是否正确,配置错误将导致连接失败',
-    },
-    {
-      key: 'network',
-      name: '网络信息',
-      data: 'network',
-      desc: '诊断网络组件配置是否正确,配置错误将导致连接失败',
-    },
-    {
-      key: 'product',
-      name: '产品状态',
-      data: 'product',
-      desc: '诊断产品状态是否已发布,未发布的状态将导致连接失败',
-    },
-    {
-      key: 'device',
-      name: '设备状态',
-      data: 'device',
-      desc: '诊断设备状态是否已启用,未启用的状态将导致连接失败',
-    },
-  ],
-  model: true,
-  gateway: {},
 });
+
+export const gatewayList = [
+  'websocket-server',
+  'http-server-gateway',
+  'udp-device-gateway',
+  'coap-server-gateway',
+  'mqtt-client-gateway',
+  'tcp-server-gateway',
+];
+
+export const headerColorMap = new Map();
+headerColorMap.set('loading', 'linear-gradient(89.95deg, #E6F5FF 0.03%, #E9EAFF 99.95%)');
+headerColorMap.set(
+  'error',
+  'linear-gradient(89.95deg, rgba(231, 173, 86, 0.1) 0.03%, rgba(247, 111, 93, 0.1) 99.95%)',
+);
+headerColorMap.set('success', 'linear-gradient(89.95deg, #E8F8F7 0.03%, #EBEFFA 99.95%)');
+
+export const headerTextMap = new Map();
+headerTextMap.set('loading', '正在诊断中');
+headerTextMap.set('error', '发现连接问题');
+headerTextMap.set('success', '连接状态正常');
+
+export const headerDescMap = new Map();
+headerDescMap.set('loading', '已诊断XX个');
+headerDescMap.set('error', '请处理连接异常');
+headerDescMap.set('success', '现在可调试消息通信');
+
+export const headerImgMap = new Map();
+headerImgMap.set('loading', require('/public/images/diagnose/loading-2.png'));
+headerImgMap.set('error', require('/public/images/diagnose/error.png'));
+headerImgMap.set('success', require('/public/images/diagnose/success.png'));
+
+export const progressMap = new Map();
+progressMap.set('loading', '#597EF7');
+progressMap.set('error', '#FAB247');
+progressMap.set('success', '#32D4A4');
+
+export const messageStyleMap = new Map();
+messageStyleMap.set('loading', {
+  background: 'linear-gradient(0deg, rgba(30, 165, 241, 0.03), rgba(30, 165, 241, 0.03)), #FFFFFF',
+  boxShadow: '-2px 0px 0px #1EA5F1',
+});
+messageStyleMap.set('error', {
+  background: 'linear-gradient(0deg, rgba(255, 77, 79, 0.03), rgba(255, 77, 79, 0.03)), #FFFFFF',
+  boxShadow: '-2px 0px 0px #FF4D4F',
+});
+messageStyleMap.set('success', {
+  background: 'linear-gradient(0deg, rgba(50, 212, 164, 0.03), rgba(50, 212, 164, 0.03)), #FFFFFF',
+  boxShadow: '-2px 0px 0px #32D4A4',
+});
+
+export const messageStatusMap = new Map();
+messageStatusMap.set('loading', 'processing');
+messageStatusMap.set('error', 'error');
+messageStatusMap.set('success', 'success');
+
+export const urlMap = new Map();
+urlMap.set('mqtt-client-gateway', 'topic');
+urlMap.set('http-server-gateway', 'url');
+urlMap.set('websocket-server', 'url');
+urlMap.set('coap-server-gateway', 'url');

+ 69 - 25
src/pages/device/Instance/Detail/Diagnose/index.less

@@ -1,40 +1,84 @@
 .diagnose {
-  .header {
+  .diagnose-header {
+    position: relative;
     width: 100%;
-  }
+    height: 150px;
+    margin-bottom: 20px;
+    padding: 15px 25px;
 
-  .header-message {
-    width: 100%;
-    background: url('/images/diagnose/back.png') no-repeat;
-    background-size: 100% 100%;
+    .diagnose-top {
+      display: flex;
+      width: 100%;
+
+      .diagnose-img {
+        width: 65px;
+        height: 65px;
+        margin-right: 20px;
+      }
+
+      .diagnose-text {
+        .diagnose-title {
+          color: #000c;
+          font-weight: 700;
+          font-size: 25px;
+        }
+
+        .diagnose-desc {
+          color: rgba(0, 0, 0, 0.65);
+          font-size: 14px;
+        }
+      }
+    }
+
+    .diagnose-progress {
+      width: 100%;
+    }
+
+    .diagnose-radio {
+      position: absolute;
+      bottom: 0;
+      display: flex;
+
+      .diagnose-radio-item {
+        width: 150px;
+        height: 35px;
+        margin-right: 8px;
+        color: #00000073;
+        line-height: 35px;
+        text-align: center;
+        background: #f2f2f2;
+        border-radius: 2px 2px 0 0;
+        cursor: pointer;
+        &.disabled {
+          cursor: not-allowed;
+        }
+      }
+    }
   }
+}
 
-  .container {
-    margin-top: 20px;
+.diagnose-loading {
+  animation: diagnose-loading 2s linear infinite;
+}
+
+@keyframes diagnose-loading {
+  0% {
+    transform: rotate(0deg);
   }
 
-  .item-box {
-    width: 100%;
-    padding: 10px;
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-    cursor: pointer;
+  25% {
+    transform: rotate(90deg);
   }
 
-  .item-title {
-    font-weight: 700;
-    font-size: 14px;
+  50% {
+    transform: rotate(180deg);
   }
 
-  .item-context {
-    height: 40px;
-    font-weight: 700;
-    font-size: 24px;
+  75% {
+    transform: rotate(270deg);
   }
 
-  .item-message {
-    color: rgba(0, 0, 0, 0.85);
-    font-weight: 400;
-    font-size: 14px;
+  100% {
+    transform: rotate(360deg);
   }
 }

+ 106 - 183
src/pages/device/Instance/Detail/Diagnose/index.tsx

@@ -1,208 +1,131 @@
-import { Badge, Card, Col, Row, Tooltip } from 'antd';
-import type { ReactNode } from 'react';
+import { Card, Progress } from 'antd';
 import { useEffect, useState } from 'react';
 import Message from './Message';
 import Status from './Status';
 import './index.less';
-import classNames from 'classnames';
-import { Store } from 'jetlinks-store';
-import { DiagnoseStatusModel } from './Status/model';
 import { useDomFullHeight } from '@/hooks';
+import { InstanceModel } from '@/pages/device/Instance';
+import { observer } from '@formily/reactive-react';
+import {
+  DiagnoseStatusModel,
+  headerColorMap,
+  headerDescMap,
+  headerImgMap,
+  headerTextMap,
+  list,
+  progressMap,
+} from './Status/model';
+import classNames from 'classnames';
 
-interface ListProps {
-  key: string;
-  tab: string;
-  component: ReactNode;
-}
-
-const bImageMap = new Map();
-bImageMap.set('m-error', require('/public/images/diagnose/message-error.png'));
-bImageMap.set('s-error', require('/public/images/diagnose/status-error.png'));
-bImageMap.set('s-success-active', require('/public/images/diagnose/status-success-active.png'));
-bImageMap.set('s-success', require('/public/images/diagnose/status-success.png'));
-bImageMap.set('waiting', require('/public/images/diagnose/waiting.png'));
-
-const statusColor = new Map();
-statusColor.set('m-error', '#E50012');
-statusColor.set('s-error', '#E50012');
-statusColor.set('error', '#E50012');
-statusColor.set('s-success-active', '#24B276');
-statusColor.set('s-success', '#24B276');
-statusColor.set('success', '#24B276');
-statusColor.set('waiting', '#FF9000');
-statusColor.set('disabled', 'rgba(0, 0, 0, .8)');
-
-const statusText = new Map();
-statusText.set('s-error', '连接失败');
-statusText.set('s-success-active', '连接成功');
-statusText.set('s-success', '连接成功');
-statusText.set('waiting', '诊断中');
-statusText.set('disabled', '诊断中');
-
-const Diagnose = () => {
+const Diagnose = observer(() => {
+  const { minHeight } = useDomFullHeight(`.diagnose`, 12);
   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 [providerType, setProviderType] = useState<
+    'network' | 'child-device' | 'media' | 'cloud' | 'channel'
+  >('network');
 
-  const [up, setUp] = useState<'success' | 'error' | 'waiting'>('waiting');
-  const [down, setDown] = useState<'success' | 'error' | 'waiting'>('waiting');
-
-  const { minHeight } = useDomFullHeight(`.diagnose`, 12);
+  const ViewMap = {
+    status: <Status providerType={providerType} />,
+    message: <Message />,
+  };
 
-  const list = [
-    {
-      key: 'status',
-      tab: '连接状态',
-      component: (
-        <div
-          style={{ backgroundImage: `url(${bImageMap.get(status)}`, backgroundSize: '100% 100%' }}
-          className="item-box"
-        >
-          <div className="item-title">连接状态</div>
-          <div style={{ color: statusColor.get(status) }} className="item-context">
-            <Badge color={statusColor.get(status)} /> {statusText.get(status)}
-          </div>
-        </div>
-      ),
-    },
-    {
-      key: 'message',
-      tab: '消息通信',
-      component: (
-        <div
-          style={
-            message !== 'disabled'
-              ? {
-                  backgroundImage: `url(${bImageMap.get(message)})`,
-                  backgroundSize: '100% 100%',
-                }
-              : {
-                  backgroundColor: 'rgba(0, 0, 0, .08)',
-                  borderLeft: '2px solid rgba(0, 0, 0, .8)',
-                  cursor: 'not-allowed',
-                }
-          }
-          className="item-box"
-        >
-          <div className="item-title">消息通信</div>
-          <div
-            className={classNames('item-context', message !== 'disabled' ? 'item-message' : '')}
-            style={{ fontWeight: 400 }}
-          >
-            {message === 'disabled' ? (
-              <Tooltip title={'设备未上线时消息通信功不能使用'}>
-                <span style={{ color: statusColor.get(message) }}>
-                  <Badge color={statusColor.get(message)} />
-                  {status === 's-error' || status === 'waiting' ? '等待设备连接' : '连接中'}
-                </span>
-              </Tooltip>
-            ) : (
-              <>
-                <div>
-                  <Badge
-                    color={statusColor.get(up)}
-                    text={
-                      up === 'waiting'
-                        ? `上行消息通信诊断中`
-                        : `上行消息通信${up === 'error' ? '异常' : '正常'}`
-                    }
-                  />
-                </div>
-                <div>
-                  <Badge
-                    color={statusColor.get(down)}
-                    text={
-                      down === 'waiting'
-                        ? `下行消息通信诊断中`
-                        : `下行消息通信${down === 'error' ? '异常' : '正常'}`
-                    }
-                  />
-                </div>
-              </>
-            )}
-          </div>
-        </div>
-      ),
-    },
-  ];
   useEffect(() => {
+    setCurrent('status');
+    const provider = InstanceModel.detail?.accessProvider;
+    if (provider === 'fixed-media' || provider === 'gb28181-2016') {
+      setProviderType('media');
+    } else if (provider === 'OneNet' || provider === 'Ctwing') {
+      setProviderType('media');
+    } else if (provider === 'modbus-tcp' || provider === 'opc-ua') {
+      setProviderType('channel');
+    } else if (provider === 'child-device') {
+      setProviderType('child-device');
+    } else {
+      setProviderType('network');
+    }
+    DiagnoseStatusModel.state = 'loading';
+
     return () => {
-      Store.set('diagnose', []);
-      Store.set('diagnose-status', []);
-      DiagnoseStatusModel.model = true;
+      DiagnoseStatusModel.list = [];
+      DiagnoseStatusModel.count = 0;
     };
   }, []);
+
+  const activeStyle = {
+    background: '#FFFFFF',
+    border: '1px solid rgba(0, 0, 0, 0.09)',
+    borderRadius: '2px 2px 0px 0px',
+    color: '#000000BF',
+  };
+
   return (
     <Card className="diagnose" style={{ minHeight }}>
-      <div className={current === 'message' ? 'header-message' : 'header'}>
-        <Row gutter={24} style={{ padding: 10, width: '100%' }}>
-          {list.map((item: ListProps) => (
-            <Col
-              span={8}
-              key={item.key}
+      <div
+        className="diagnose-header"
+        style={{
+          background: headerColorMap.get(DiagnoseStatusModel.state),
+        }}
+      >
+        <div className="diagnose-top">
+          <div className="diagnose-img">
+            {DiagnoseStatusModel.state === 'loading' ? (
+              <div style={{ height: '100%', width: '100%', position: 'relative' }}>
+                <img
+                  style={{ height: '100%', position: 'absolute', zIndex: 2 }}
+                  src={headerImgMap.get(DiagnoseStatusModel.state)}
+                />
+                <img
+                  src={require('/public/images/diagnose/loading-1.png')}
+                  className={'diagnose-loading'}
+                  style={{ height: '100%' }}
+                />
+              </div>
+            ) : (
+              <img style={{ height: '100%' }} src={headerImgMap.get(DiagnoseStatusModel.state)} />
+            )}
+          </div>
+          <div className="diagnose-text">
+            <div className="diagnose-title">{headerTextMap.get(DiagnoseStatusModel.state)}</div>
+            <div className="diagnose-desc">
+              {DiagnoseStatusModel.state !== 'loading'
+                ? headerDescMap.get(DiagnoseStatusModel.state)
+                : `已诊断${DiagnoseStatusModel.count}个`}
+            </div>
+          </div>
+        </div>
+        <div className="diagnose-progress">
+          <Progress
+            strokeColor={progressMap.get(DiagnoseStatusModel.state)}
+            showInfo={false}
+            style={{ width: '100%' }}
+            size="small"
+            percent={DiagnoseStatusModel.percent}
+          />
+        </div>
+        <div className="diagnose-radio">
+          {list.map((i) => (
+            <div
+              key={i.key}
+              className={classNames(
+                'diagnose-radio-item',
+                i.key === 'message' && DiagnoseStatusModel.state !== 'success' ? 'disabled' : '',
+              )}
+              style={current === i.key ? { ...activeStyle } : {}}
               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);
+                if (DiagnoseStatusModel.state === 'success') {
+                  setCurrent(i.key);
                 }
               }}
             >
-              {item.component}
-            </Col>
+              {i.text}
+              {current === i.key ? '(诊断中)' : ''}
+            </div>
           ))}
-        </Row>
-      </div>
-      <div className="container">
-        {current === 'status' ? (
-          <Status
-            flag={active}
-            onChange={(type: string) => {
-              if (type === 'success') {
-                setStatus('s-success-active');
-                setMessage('waiting');
-              } else if (type === 'error') {
-                setStatus('s-error');
-                setMessage('disabled');
-              } else if (type === 'loading') {
-                setStatus('waiting');
-                setMessage('disabled');
-              }
-            }}
-          />
-        ) : (
-          <Message
-            onChange={(data: string) => {
-              if (data === 'waiting') {
-                setMessage('waiting');
-                setDown('waiting');
-                setUp('waiting');
-              } else if (data === 'down-error') {
-                setMessage('m-error');
-                setDown('error');
-              } else if (data === 'down-success') {
-                setMessage('s-success-active');
-                setDown('success');
-              } else if (data === 'up-success') {
-                setMessage('s-success-active');
-                setUp('success');
-              } else if (data === 'up-error') {
-                setMessage('m-error');
-                setUp('error');
-              }
-            }}
-          />
-        )}
+        </div>
       </div>
+      <div>{ViewMap[current]}</div>
     </Card>
   );
-};
+});
 
 export default Diagnose;

+ 1 - 0
src/pages/device/Instance/typings.d.ts

@@ -43,6 +43,7 @@ export type DeviceInstance = {
   accessProvider?: string;
   accessId?: string;
   features?: any[];
+  parentId?: string;
 };
 
 type Unit = {

+ 13 - 9
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -101,13 +101,21 @@ const Access = (props: Props) => {
       if (!procotolCurrent) {
         onlyMessage('请选择消息协议!', 'error');
       } else {
-        service
-          .getConfigView(procotolCurrent, ProcotoleMapping.get(props.provider?.id))
-          .then((resp) => {
+        if (props.provider?.channel !== 'child-device') {
+          service
+            .getConfigView(procotolCurrent, ProcotoleMapping.get(props.provider?.id))
+            .then((resp) => {
+              if (resp.status === 200) {
+                setConfig(resp.result);
+              }
+            });
+        } else {
+          service.getChildConfigView(procotolCurrent).then((resp) => {
             if (resp.status === 200) {
               setConfig(resp.result);
             }
           });
+        }
         setCurrent(current + 1);
       }
     }
@@ -440,13 +448,9 @@ const Access = (props: Props) => {
                         borderColor:
                           procotolCurrent === item.id ? 'var(--ant-primary-color-active)' : '',
                       }}
-                      hoverable={!props.data.id}
+                      hoverable
                       onClick={() => {
-                        if (!props.data.id) {
-                          setProcotolCurrent(item.id);
-                        } else {
-                          onlyMessage('消息协议不可修改', 'warning');
-                        }
+                        setProcotolCurrent(item.id);
                       }}
                     >
                       <div style={{ height: '45px' }}>

+ 5 - 0
src/pages/link/AccessConfig/service.ts

@@ -37,6 +37,11 @@ class Service extends BaseService<AccessItem> {
       params,
     });
   };
+  public getChildConfigView = (id: string) => {
+    return request(`/${SystemConst.API_BASE}/protocol/${id}/transports`, {
+      method: 'GET',
+    });
+  };
   public getConfigView = (id: string, transport: string) =>
     request(`/${SystemConst.API_BASE}/protocol/${id}/transport/${transport}`, {
       method: 'GET',

+ 33 - 3
src/pages/rule-engine/Alarm/Config/index.tsx

@@ -266,7 +266,7 @@ const Config = () => {
     },
   };
 
-  const ioSchema: ISchema = {
+  const outputSchema: ISchema = {
     type: 'object',
     properties: {
       id: {
@@ -333,6 +333,36 @@ const Config = () => {
     },
   };
 
+  const inputSchema: ISchema = {
+    type: 'object',
+    properties: {
+      id: {
+        'x-component': 'Input',
+        'x-hidden': true,
+      },
+      address: {
+        title: 'kafka地址',
+        type: 'string',
+        required: true,
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {
+          placeholder: '请输入kafka地址',
+        },
+      },
+      topic: {
+        title: 'topic',
+        type: 'string',
+        required: true,
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-component-props': {
+          placeholder: '请输入topic',
+        },
+      },
+    },
+  };
+
   const handleSaveIO = async () => {
     outputForm.validate();
     inputForm.validate();
@@ -443,7 +473,7 @@ const Config = () => {
               }
             />
             <Form form={outputForm} layout="vertical">
-              <SchemaField schema={ioSchema} />
+              <SchemaField schema={outputSchema} />
             </Form>
             <Divider />
             <TitleComponent
@@ -457,7 +487,7 @@ const Config = () => {
               }
             />
             <Form form={inputForm} layout="vertical">
-              <SchemaField schema={ioSchema} />
+              <SchemaField schema={inputSchema} />
               <FormButtonGroup.Sticky>
                 <FormButtonGroup.FormItem>
                   <Button type="primary" onClick={handleSaveIO}>

+ 3 - 0
src/pages/system/Relationship/index.tsx

@@ -10,6 +10,7 @@ import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
 import Save from './Save';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
+import { message } from 'antd';
 
 export const service = new Service('relation');
 
@@ -85,6 +86,8 @@ const Relationship = () => {
                   }),
                 );
                 actionRef.current?.reload();
+              } else {
+                message.error(resp.message);
               }
             },
           }}

+ 3 - 1
src/pages/system/Role/index.tsx

@@ -197,14 +197,16 @@ const Role: React.FC = observer(() => {
       CurdModel.add();
     }
     const subscription = Store.subscribe(SystemConst.BASE_UPDATE_DATA, (data) => {
-      console.log('订阅数据');
       if ((window as any).onTabSaveSuccess) {
         (window as any).onTabSaveSuccess(data);
         setTimeout(() => window.close(), 300);
+      } else {
+        history.push(`${getMenuPathByParams(MENUS_CODE['system/Role/Detail'], data.id)}`);
       }
     });
     return () => subscription.unsubscribe();
   }, []);
+
   return (
     <PageContainer>
       <BaseCrud<RoleItem>