sun-chaochao 3 лет назад
Родитель
Сommit
e2584a4a91

+ 11 - 9
src/pages/device/Instance/Detail/Config/index.tsx

@@ -108,16 +108,18 @@ const Config = () => {
                 <Space>
                   <a
                     onClick={async () => {
-                      const values = (await form.submit()) as any;
-                      const resp = await service.modify(id || '', {
-                        id,
-                        configuration: { ...values },
-                      });
-                      if (resp.status === 200) {
-                        InstanceModel.detail = {
-                          ...InstanceModel.detail,
+                      if (!state) {
+                        const values = (await form.submit()) as any;
+                        const resp = await service.modify(id || '', {
+                          id,
                           configuration: { ...values },
-                        };
+                        });
+                        if (resp.status === 200) {
+                          InstanceModel.detail = {
+                            ...InstanceModel.detail,
+                            configuration: { ...values },
+                          };
+                        }
                         setState(!state);
                       }
                     }}

+ 198 - 0
src/pages/device/Product/Detail/Access/AccessConfig/index.tsx

@@ -0,0 +1,198 @@
+import { useEffect, useState } from 'react';
+import { Badge, Button, Card, Col, message, Modal, Pagination, Row } from 'antd';
+import { service } from '@/pages/link/AccessConfig';
+import { productModel } from '@/pages/device/Product';
+import SearchComponent from '@/components/SearchComponent';
+import type { ProColumns } from '@jetlinks/pro-table';
+import styles from '../index.less';
+import Service from '@/pages/device/Product/service';
+
+interface Props {
+  close: () => void;
+  data?: any;
+}
+
+const AccessConfig = (props: Props) => {
+  const { close } = props;
+  const service1 = new Service('device-product');
+
+  const [dataSource, setDataSource] = useState<any>({
+    data: [],
+    pageSize: 4,
+    pageIndex: 0,
+    total: 0,
+  });
+  const [param, setParam] = useState<any>({ pageSize: 4 });
+
+  const [currrent, setCurrrent] = useState<any>({
+    id: productModel.current?.accessId,
+    name: productModel.current?.accessName,
+    protocol: productModel.current?.messageProtocol,
+    transport: productModel.current?.transportProtocol,
+    protocolDetail: {
+      name: productModel.current?.protocolName,
+    },
+  });
+
+  const handleSearch = (params: any) => {
+    setParam(params);
+    service
+      .queryList({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+      .then((resp) => {
+        setDataSource(resp.result);
+      });
+  };
+
+  const columns: ProColumns<any>[] = [
+    {
+      title: '名称',
+      dataIndex: 'name',
+    },
+  ];
+
+  useEffect(() => {
+    handleSearch(param);
+  }, []);
+
+  return (
+    <Modal
+      onCancel={() => {
+        close();
+      }}
+      visible
+      width={1200}
+      title={'设备接入配置'}
+      onOk={() => {
+        if (!!currrent) {
+          service1
+            .update({
+              ...productModel.current,
+              transportProtocol: currrent.transport,
+              protocolName: currrent.protocolDetail.name,
+              accessId: currrent.id,
+              accessName: currrent.name,
+              messageProtocol: currrent.protocol,
+            })
+            .then((resp) => {
+              if (resp.status === 200) {
+                service1.detail(productModel.current?.id || '').then((res) => {
+                  if (res.status === 200) {
+                    productModel.current = { ...res.result };
+                    message.success('操作成功!');
+                    close();
+                  }
+                });
+              }
+            });
+        } else {
+          message.success('请选择接入方式');
+        }
+      }}
+    >
+      <SearchComponent
+        field={columns}
+        pattern={'simple'}
+        onSearch={(data: any) => {
+          const dt = {
+            pageSize: 4,
+            terms: [...data.terms],
+          };
+          handleSearch(dt);
+        }}
+        onReset={() => {
+          handleSearch({ pageSize: 4 });
+        }}
+      />
+      <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
+        <Button
+          type="primary"
+          onClick={() => {
+            const tab: any = window.open(`${origin}/#/link/AccessConfig/Detail`);
+            tab!.onTabSaveSuccess = (value: any) => {
+              if (value.status === 200) {
+                handleSearch(param);
+              }
+            };
+          }}
+        >
+          新增
+        </Button>
+      </div>
+      <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
+        {dataSource.data.map((item: any) => (
+          <Col key={item.name} span={12}>
+            <Card
+              style={{
+                width: '100%',
+                border: currrent?.id === item.id ? '1px solid #1d39c4' : '',
+              }}
+              hoverable
+              onClick={() => {
+                setCurrrent(item);
+              }}
+            >
+              <div className={styles.box}>
+                <div className={styles.images}>{item.name}</div>
+                <div className={styles.content}>
+                  <div className={styles.header}>
+                    <div className={styles.top}>
+                      <div className={styles.title}>{item.name}</div>
+                      <div className={styles.status}>
+                        <Badge
+                          color={item.state.value === 'disabled' ? 'red' : 'green'}
+                          text={item.state.text}
+                          style={{ marginLeft: '20px' }}
+                        />
+                      </div>
+                    </div>
+                    <div className={styles.desc}>这里是接入方式的解释说明</div>
+                  </div>
+                  <div className={styles.container}>
+                    <div className={styles.server}>
+                      <div className={styles.title}>{item?.channelInfo?.name}</div>
+                      <p>
+                        {item.channelInfo?.addresses.map((i: any) => (
+                          <div key={i.address}>
+                            <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
+                          </div>
+                        ))}
+                      </p>
+                    </div>
+                    <div className={styles.procotol}>
+                      <div className={styles.title}>{item?.protocolDetail?.name}</div>
+                      <p style={{ color: 'rgba(0, 0, 0, .55)' }}>{item.description}</p>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </Card>
+          </Col>
+        ))}
+      </Row>
+      <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
+        <Pagination
+          showSizeChanger
+          size="small"
+          className={'pro-table-card-pagination'}
+          total={dataSource?.total || 0}
+          current={dataSource?.pageIndex + 1}
+          onChange={(page, size) => {
+            handleSearch({
+              ...param,
+              pageIndex: page - 1,
+              pageSize: size,
+            });
+          }}
+          pageSizeOptions={[4, 8, 16, 32]}
+          pageSize={dataSource?.pageSize}
+          showTotal={(num) => {
+            const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
+            const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
+            return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+          }}
+        />
+      </div>
+    </Modal>
+  );
+};
+export default AccessConfig;

+ 1 - 3
src/pages/device/Product/Detail/Access/index.less

@@ -75,14 +75,12 @@
 }
 
 .config {
-  width: 48%;
-
+  width: 100%;
   .title {
     width: 100%;
     margin-bottom: 10px;
     font-weight: 600;
   }
-
   .title::before {
     margin-right: 10px;
     background-color: #2810ff;

+ 34 - 198
src/pages/device/Product/Detail/Access/index.tsx

@@ -1,37 +1,11 @@
-import {
-  Alert,
-  Badge,
-  Button,
-  Card,
-  Col,
-  Descriptions,
-  message,
-  Pagination,
-  Row,
-  Table,
-  Tooltip,
-} from 'antd';
+import { Badge, Descriptions, Empty, Table, Tooltip } from 'antd';
 import { service } from '@/pages/link/AccessConfig';
 import styles from './index.less';
 import { useEffect, useState } from 'react';
-import Service from '@/pages/device/Product/service';
 import { productModel } from '@/pages/device/Product';
-import type { ProColumns } from '@jetlinks/pro-table';
-import SearchComponent from '@/components/SearchComponent';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
-import { useHistory } from 'umi';
+import AccessConfig from './AccessConfig';
 
 const Access = () => {
-  const service1 = new Service('device-product');
-  const [currrent, setCurrrent] = useState<any>({
-    id: productModel.current?.accessId,
-    name: productModel.current?.accessName,
-    protocol: productModel.current?.messageProtocol,
-    transport: productModel.current?.transportProtocol,
-    protocolDetail: {
-      name: productModel.current?.protocolName,
-    },
-  });
   const [visible, setVisible] = useState<boolean>(true);
   const [config, setConfig] = useState<any>();
   const [access, setAccess] = useState<any>();
@@ -47,15 +21,7 @@ const Access = () => {
   MetworkTypeMapping.set('mqtt-server-gateway', 'MQTT_SERVER');
   MetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
 
-  const [param, setParam] = useState<any>({ pageSize: 10 });
-  const history = useHistory();
-
-  const columns: ProColumns<any>[] = [
-    {
-      title: '名称',
-      dataIndex: 'name',
-    },
-  ];
+  const [configVisible, setConfigVisible] = useState<boolean>(false);
 
   const queryNetworkList = (id: string) => {
     service.getNetworkList(MetworkTypeMapping.get(id)).then((resp) => {
@@ -65,22 +31,6 @@ const Access = () => {
     });
   };
 
-  const [dataSource, setDataSource] = useState<any>({
-    data: [],
-    pageSize: 10,
-    pageIndex: 0,
-    total: 0,
-  });
-
-  const handleSearch = (params: any) => {
-    setParam(params);
-    service
-      .queryList({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
-      .then((resp) => {
-        setDataSource(resp.result);
-      });
-  };
-
   const queryProviders = () => {
     service.getProviders().then((resp) => {
       if (resp.status === 200) {
@@ -224,151 +174,42 @@ const Access = () => {
         productModel.current?.transportProtocol || '',
       );
       queryAccess(productModel.current?.accessId);
-    } else {
-      handleSearch(param);
     }
-  }, []);
+  }, [productModel.current]);
 
   return (
     <div>
       {!visible ? (
-        <div style={{ padding: '20px 0' }}>
-          <Alert message="请选择使用的设备接入网关,用以提供设备接入能力" type="info" showIcon />
-          <SearchComponent
-            field={columns}
-            pattern={'simple'}
-            onSearch={(data: any) => {
-              const dt = {
-                pageSize: 10,
-                terms: [...data.terms],
-              };
-              handleSearch(dt);
-            }}
-            onReset={() => {
-              handleSearch({ pageSize: 10 });
-            }}
-          />
-          <Button
-            type="primary"
-            onClick={() => {
-              history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}`);
-            }}
-          >
-            新增
-          </Button>
-          <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
-            {dataSource.data.map((item: any) => (
-              <Col key={item.name} span={12}>
-                <Card
-                  style={{
-                    width: '100%',
-                    border: currrent?.id === item.id ? '1px solid #1d39c4' : '',
-                  }}
-                  hoverable
+        <div style={{ padding: '100px 0' }}>
+          <Empty
+            description={
+              <span>
+                请先
+                <a
                   onClick={() => {
-                    setCurrrent(item);
+                    setConfigVisible(true);
                   }}
                 >
-                  <div className={styles.box}>
-                    <div className={styles.images}>{item.name}</div>
-                    <div className={styles.content}>
-                      <div className={styles.header}>
-                        <div className={styles.top}>
-                          <div className={styles.title}>{item.name}</div>
-                          <div className={styles.status}>
-                            <Badge
-                              color={item.state.value === 'disabled' ? 'red' : 'green'}
-                              text={item.state.text}
-                              style={{ marginLeft: '20px' }}
-                            />
-                          </div>
-                        </div>
-                        <div className={styles.desc}>这里是接入方式的解释说明</div>
-                      </div>
-                      <div className={styles.container}>
-                        <div className={styles.server}>
-                          <div className={styles.title}>{item?.channelInfo?.name}</div>
-                          <p>
-                            {item.channelInfo?.addresses.map((i: any) => (
-                              <div key={i.address}>
-                                <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
-                              </div>
-                            ))}
-                          </p>
-                        </div>
-                        <div className={styles.procotol}>
-                          <div className={styles.title}>{item?.protocolDetail?.name}</div>
-                          <p style={{ color: 'rgba(0, 0, 0, .55)' }}>{item.description}</p>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </Card>
-              </Col>
-            ))}
-          </Row>
-          <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-            <Pagination
-              showSizeChanger
-              size="small"
-              className={'pro-table-card-pagination'}
-              total={dataSource?.total || 0}
-              current={dataSource?.pageIndex + 1}
-              onChange={(page, size) => {
-                handleSearch({
-                  ...param,
-                  pageIndex: page - 1,
-                  pageSize: size,
-                });
-              }}
-              pageSizeOptions={[10, 20, 50, 100]}
-              pageSize={dataSource?.pageSize}
-              showTotal={(num) => {
-                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-              }}
-            />
-          </div>
-          <div style={{ marginTop: '20px' }}>
-            <Button
-              type="primary"
+                  选择
+                </a>
+                设备接入网关,用以提供设备接入能力
+              </span>
+            }
+          />
+        </div>
+      ) : (
+        <div className={styles.config}>
+          <div className={styles.title}>
+            配置概览
+            <a
+              style={{ marginLeft: 20 }}
               onClick={() => {
-                if (!!currrent) {
-                  service1
-                    .update({
-                      ...productModel.current,
-                      transportProtocol: currrent.transport,
-                      protocolName: currrent.protocolDetail.name,
-                      accessId: currrent.id,
-                      accessName: currrent.name,
-                      messageProtocol: currrent.protocol,
-                    })
-                    .then((resp) => {
-                      if (resp.status === 200) {
-                        service1.detail(productModel.current?.id || '').then((res) => {
-                          if (res.status === 200) {
-                            productModel.current = { ...res.result };
-                            message.success('操作成功!');
-                            setVisible(true);
-                            getDetail(res.result.messageProtocol, res.result.transportProtocol);
-                            queryAccess(res.result.accessId);
-                          }
-                        });
-                      }
-                    });
-                } else {
-                  message.success('请选择接入方式');
-                }
+                setConfigVisible(true);
               }}
             >
-              保存
-            </Button>
+              更换
+            </a>
           </div>
-        </div>
-      ) : (
-        <div className={styles.config}>
-          <div className={styles.title}>配置概览</div>
           <Descriptions column={1}>
             <Descriptions.Item label="接入方式">
               {providers.find((i) => i.id === access?.provider)?.name || ''}
@@ -405,20 +246,15 @@ const Access = () => {
               />
             </div>
           )}
-
-          <div style={{ marginTop: '20px' }}>
-            <Button
-              type="primary"
-              onClick={() => {
-                setVisible(false);
-                handleSearch({ pageSize: 10 });
-              }}
-            >
-              编辑
-            </Button>
-          </div>
         </div>
       )}
+      {configVisible && (
+        <AccessConfig
+          close={() => {
+            setConfigVisible(false);
+          }}
+        />
+      )}
     </div>
   );
 };

+ 130 - 106
src/pages/device/Product/Detail/BaseInfo/index.tsx

@@ -1,129 +1,152 @@
 import { productModel, service } from '@/pages/device/Product';
-import { Button, Descriptions } from 'antd';
-import { useState } from 'react';
-import { useParams } from 'umi';
+import { Button, Card, Descriptions, Divider, message } from 'antd';
+import type { SetStateAction } from 'react';
+import { useEffect, useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { EditOutlined } from '@ant-design/icons';
 import { getDateFormat } from '@/utils/util';
 import Save from '@/pages/device/Product/Save';
+import { Form, FormGrid, FormItem, FormLayout, Input, Password, PreviewText } from '@formily/antd';
+import type { ISchema } from '@formily/json-schema';
+import type { ConfigMetadata, ConfigProperty } from '@/pages/device/Product/typings';
+import { createSchemaField } from '@formily/react';
+import { createForm } from '@formily/core';
+
+const componentMap = {
+  string: 'Input',
+  password: 'Password',
+};
 
-// const componentMap = {
-//   string: 'Input',
-//   password: 'Password',
-// };
 const BaseInfo = () => {
   const intl = useIntl();
-  const param = useParams<{ id: string }>();
-  // const [metadata, setMetadata] = useState<ConfigMetadata[]>([]);
-  // const [state, setState] = useState<boolean>(false);
+  const [metadata, setMetadata] = useState<ConfigMetadata[]>([]);
+  const [state, setState] = useState<boolean>(false);
   const [visible, setVisible] = useState(false);
 
-  // const form = createForm({
-  //   validateFirst: true,
-  //   readPretty: state,
-  //   initialValues: productModel.current?.configuration,
-  // });
+  const form = createForm({
+    validateFirst: true,
+    readPretty: state,
+    initialValues: productModel.current?.configuration,
+  });
+
+  const id = productModel.current?.id;
+
+  useEffect(() => {
+    if (id) {
+      service.getConfigMetadata(id).then((config: { result: SetStateAction<ConfigMetadata[]> }) => {
+        setMetadata(config.result);
+      });
+    }
+  }, [productModel.current]);
 
-  // useEffect(() => {
-  //   if (param.id) {
-  //     service
-  //       .getConfigMetadata(param.id)
-  //       .then((config: { result: SetStateAction<ConfigMetadata[]> }) => {
-  //         setMetadata(config.result);
-  //       });
-  //   }
-  // }, [param.id]);
+  const SchemaField = createSchemaField({
+    components: {
+      Password,
+      FormGrid,
+      PreviewText,
+      FormItem,
+      Input,
+    },
+  });
 
-  // const SchemaField = createSchemaField({
-  //   components: {
-  //     Password,
-  //     FormGrid,
-  //     PreviewText,
-  //     FormItem,
-  //     Input,
-  //   },
-  // });
-  //
-  // const configToSchema = (data: ConfigProperty[]) => {
-  //   const config = {};
-  //   data.forEach((item) => {
-  //     config[item.property] = {
-  //       type: 'string',
-  //       title: item.name,
-  //       'x-decorator': 'FormItem',
-  //       'x-component': componentMap[item.type.type],
-  //       'x-decorator-props': {
-  //         tooltip: item.description,
-  //       },
-  //     };
-  //   });
-  //   return config;
-  // };
+  const configToSchema = (data: ConfigProperty[]) => {
+    const config = {};
+    data.forEach((item) => {
+      config[item.property] = {
+        type: 'string',
+        title: item.name,
+        'x-decorator': 'FormItem',
+        'x-component': componentMap[item.type.type],
+        'x-decorator-props': {
+          tooltip: item.description,
+        },
+      };
+    });
+    return config;
+  };
 
   const getDetailInfo = () => {
-    service.getProductDetail(param?.id).subscribe((data) => {
+    service.getProductDetail(id || '').subscribe((data) => {
       if (data) {
         productModel.current = data;
       }
     });
   };
 
-  // const renderConfigCard = () => {
-  //   return metadata && metadata.length > 0 ? (
-  //     metadata?.map((item) => {
-  //       const itemSchema: ISchema = {
-  //         type: 'object',
-  //         properties: {
-  //           grid: {
-  //             type: 'void',
-  //             'x-component': 'FormGrid',
-  //             'x-component-props': {
-  //               minColumns: [2],
-  //               maxColumns: [2],
-  //             },
-  //             properties: configToSchema(item.properties),
-  //           },
-  //         },
-  //       };
-  //
-  //       return (
-  //         <Card
-  //           key={item.name}
-  //           title={item.name}
-  //           extra={
-  //             <a onClick={() => setState(!state)}>
-  //               {state ? (
-  //                 <>
-  //                   {intl.formatMessage({
-  //                     id: 'pages.data.option.edit',
-  //                     defaultMessage: '编辑',
-  //                   })}
-  //                 </>
-  //               ) : (
-  //                 <>
-  //                   {intl.formatMessage({
-  //                     id: 'pages.device.productDetail.base.save',
-  //                     defaultMessage: '保存',
-  //                   })}
-  //                 </>
-  //               )}
-  //             </a>
-  //           }
-  //         >
-  //           <PreviewText.Placeholder value='-'>
-  //             <Form form={form}>
-  //               <FormLayout labelCol={6} wrapperCol={16}>
-  //                 <SchemaField schema={itemSchema} />
-  //               </FormLayout>
-  //             </Form>
-  //           </PreviewText.Placeholder>
-  //         </Card>
-  //       );
-  //     })
-  //   ) : (
-  //     <Empty description={'暂无配置'} />
-  //   );
-  // };
+  const renderConfigCard = () => {
+    return (
+      metadata &&
+      metadata.length > 0 &&
+      metadata?.map((item) => {
+        const itemSchema: ISchema = {
+          type: 'object',
+          properties: {
+            grid: {
+              type: 'void',
+              'x-component': 'FormGrid',
+              'x-component-props': {
+                minColumns: [2],
+                maxColumns: [2],
+              },
+              properties: configToSchema(item.properties),
+            },
+          },
+        };
+
+        return (
+          <>
+            <Divider />
+            <Card
+              key={item.name}
+              title={item.name}
+              extra={
+                <a
+                  onClick={async () => {
+                    if (!state) {
+                      const values = (await form.submit()) as any;
+                      const resp = await service.modify(id || '', {
+                        id,
+                        configuration: { ...values },
+                      });
+                      if (resp.status === 200) {
+                        message.success('操作成功!');
+                        getDetailInfo();
+                      }
+                      setState(!state);
+                    }
+                  }}
+                >
+                  {state ? (
+                    <>
+                      {intl.formatMessage({
+                        id: 'pages.data.option.edit',
+                        defaultMessage: '编辑',
+                      })}
+                    </>
+                  ) : (
+                    <>
+                      {intl.formatMessage({
+                        id: 'pages.device.productDetail.base.save',
+                        defaultMessage: '保存',
+                      })}
+                    </>
+                  )}
+                </a>
+              }
+            >
+              <PreviewText.Placeholder value="-">
+                <Form form={form}>
+                  <FormLayout labelCol={6} wrapperCol={16}>
+                    <SchemaField schema={itemSchema} />
+                  </FormLayout>
+                </Form>
+              </PreviewText.Placeholder>
+            </Card>
+          </>
+        );
+      })
+    );
+  };
 
   return (
     <>
@@ -212,6 +235,7 @@ const BaseInfo = () => {
         reload={getDetailInfo}
         visible={visible}
       />
+      {renderConfigCard()}
     </>
   );
 };

+ 93 - 58
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -18,14 +18,17 @@ import { useEffect, useState } from 'react';
 import styles from './index.less';
 import { service } from '@/pages/link/AccessConfig';
 import encodeQuery from '@/utils/encodeQuery';
-import { useHistory } from 'umi';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { useHistory, useLocation } from 'umi';
 
 interface Props {
   change: () => void;
   data: any;
-  access: any;
 }
+
+type LocationType = {
+  id?: string;
+};
+
 const Access = (props: Props) => {
   const [form] = Form.useForm();
 
@@ -34,8 +37,9 @@ const Access = (props: Props) => {
   const [current, setCurrent] = useState<number>(0);
   const [networkList, setNetworkList] = useState<any[]>([]);
   const [procotolList, setProcotolList] = useState<any[]>([]);
-  const [procotolCurrent, setProcotolCurrent] = useState<string>(props.access?.protocol || '');
-  const [networkCurrent, setNetworkCurrent] = useState<string>(props.access?.channelId || '');
+  const [access, setAccess] = useState<any>({});
+  const [procotolCurrent, setProcotolCurrent] = useState<string>('');
+  const [networkCurrent, setNetworkCurrent] = useState<string>('');
   const [config, setConfig] = useState<any>();
   const [providers, setProviders] = useState<any[]>([]);
 
@@ -57,24 +61,20 @@ const Access = (props: Props) => {
   ProcotoleMapping.set('mqtt-server-gateway', 'MQTT');
   ProcotoleMapping.set('tcp-server-gateway', 'TCP');
 
-  const queryNetworkList = (params?: any) => {
-    service
-      .getNetworkList(MetworkTypeMapping.get(props.data?.id || props.access?.provider), params)
-      .then((resp) => {
-        if (resp.status === 200) {
-          setNetworkList(resp.result);
-        }
-      });
+  const queryNetworkList = (id: string, params?: any) => {
+    service.getNetworkList(MetworkTypeMapping.get(id), params).then((resp) => {
+      if (resp.status === 200) {
+        setNetworkList(resp.result);
+      }
+    });
   };
 
-  const queryProcotolList = (params?: any) => {
-    service
-      .getProtocolList(ProcotoleMapping.get(props.data?.id || props.access?.provider), params)
-      .then((resp) => {
-        if (resp.status === 200) {
-          setProcotolList(resp.result);
-        }
-      });
+  const queryProcotolList = (id: string, params?: any) => {
+    service.getProtocolList(ProcotoleMapping.get(id), params).then((resp) => {
+      if (resp.status === 200) {
+        setProcotolList(resp.result);
+      }
+    });
   };
 
   const queryProviders = () => {
@@ -86,26 +86,39 @@ const Access = (props: Props) => {
   };
 
   useEffect(() => {
-    if (props.data) {
-      queryNetworkList();
+    if (props.data?.id) {
+      queryNetworkList(props.data?.id);
       setCurrent(0);
     }
   }, [props.data]);
 
+  const location = useLocation<LocationType>();
+
+  const params = new URLSearchParams(location.search);
+
   useEffect(() => {
-    form.setFieldsValue({
-      name: props.access?.name,
-      description: props.access?.name,
-    });
-    queryProviders();
-  }, [props.access]);
+    if (params.get('id')) {
+      service.detail(params.get('id') || '').then((resp) => {
+        setAccess(resp.result);
+        setProcotolCurrent(resp.result?.protocol);
+        setNetworkCurrent(resp.result?.channelId);
+        form.setFieldsValue({
+          name: resp.result?.name,
+          description: resp.result?.description,
+        });
+        queryProviders();
+        setCurrent(0);
+        queryNetworkList(resp.result?.provider);
+      });
+    }
+  }, []);
 
   const next = () => {
     if (current === 0) {
       if (!networkCurrent) {
         message.error('请选择网络组件!');
       } else {
-        queryProcotolList();
+        queryProcotolList(props.data?.id || access?.provider);
         setCurrent(current + 1);
       }
     }
@@ -114,10 +127,7 @@ const Access = (props: Props) => {
         message.error('请选择消息协议!');
       } else {
         service
-          .getConfigView(
-            procotolCurrent,
-            ProcotoleMapping.get(props.data?.id || props.access?.provider),
-          )
+          .getConfigView(procotolCurrent, ProcotoleMapping.get(props.data?.id || access?.provider))
           .then((resp) => {
             if (resp.status === 200) {
               setConfig(resp.result);
@@ -264,6 +274,7 @@ const Access = (props: Props) => {
                 placeholder="请输入名称"
                 onSearch={(value: string) => {
                   queryNetworkList(
+                    props.data?.id || access?.provider,
                     encodeQuery({
                       terms: {
                         name$LIKE: `%${value}%`,
@@ -276,7 +287,12 @@ const Access = (props: Props) => {
               <Button
                 type="primary"
                 onClick={() => {
-                  history.push(`${getMenuPathByCode(MENUS_CODE['link/Type/Save'])}`);
+                  const tab: any = window.open(`${origin}/#/link/Type/Save/:id`);
+                  tab!.onTabSaveSuccess = (value: any) => {
+                    if (value.status === 200) {
+                      queryNetworkList(props.data?.id || access?.provider);
+                    }
+                  };
                 }}
               >
                 新增
@@ -312,16 +328,17 @@ const Access = (props: Props) => {
               </Row>
             ) : (
               <Empty
-                image="https://gw.alipayobjects.com/zos/antfincdn/ZHrcdLPrvN/empty.svg"
-                imageStyle={{
-                  height: 60,
-                }}
                 description={
                   <span>
                     暂无数据{' '}
                     <a
                       onClick={() => {
-                        history.push(`${getMenuPathByCode(MENUS_CODE['link/Type/Save'])}`);
+                        const tab: any = window.open(`${origin}/#/link/Type/Save/:id`);
+                        tab!.onTabSaveSuccess = (value: any) => {
+                          if (value.status === 200) {
+                            queryNetworkList(props.data?.id || access?.provider);
+                          }
+                        };
                       }}
                     >
                       创建接入方式
@@ -345,6 +362,7 @@ const Access = (props: Props) => {
                 placeholder="请输入名称"
                 onSearch={(value: string) => {
                   queryProcotolList(
+                    props.data?.id || access?.provider,
                     encodeQuery({
                       terms: {
                         name$LIKE: `%${value}%`,
@@ -357,7 +375,12 @@ const Access = (props: Props) => {
               <Button
                 type="primary"
                 onClick={() => {
-                  history.push(`${getMenuPathByCode(MENUS_CODE['link/Protocol'])}`);
+                  const tab: any = window.open(`${origin}/#/link/Protocol?save=true`);
+                  tab!.onTabSaveSuccess = (value: any) => {
+                    if (value) {
+                      queryProcotolList(props.data?.id || access?.provider);
+                    }
+                  };
                 }}
               >
                 新增
@@ -385,16 +408,17 @@ const Access = (props: Props) => {
               </Row>
             ) : (
               <Empty
-                image="https://gw.alipayobjects.com/zos/antfincdn/ZHrcdLPrvN/empty.svg"
-                imageStyle={{
-                  height: 60,
-                }}
                 description={
                   <span>
                     暂无数据
                     <a
                       onClick={() => {
-                        history.push(`${getMenuPathByCode(MENUS_CODE['link/Protocol'])}`);
+                        const tab: any = window.open(`${origin}/#/link/Protocol?save=true`);
+                        tab!.onTabSaveSuccess = (value: any) => {
+                          if (value) {
+                            queryProcotolList(props.data?.id || access?.provider);
+                          }
+                        };
                       }}
                     >
                       去新增
@@ -427,9 +451,12 @@ const Access = (props: Props) => {
               <div className={styles.title}>配置概览</div>
               <Descriptions column={1}>
                 <Descriptions.Item label="接入方式">
-                  {props.data?.name || providers.find((i) => i.id === props.access?.provider)?.name}
+                  {props.data?.name || providers.find((i) => i.id === access?.provider)?.name}
+                </Descriptions.Item>
+                <Descriptions.Item>
+                  {props.data?.description ||
+                    providers.find((i) => i.id === access?.provider)?.description}
                 </Descriptions.Item>
-                <Descriptions.Item>{props.data?.description || ''}</Descriptions.Item>
                 <Descriptions.Item label="消息协议">
                   {procotolList.find((i) => i.id === procotolCurrent)?.name || ''}
                 </Descriptions.Item>
@@ -508,8 +535,8 @@ const Access = (props: Props) => {
                 try {
                   const values = await form.validateFields();
                   // 编辑还是保存
-                  if (!!props.access.id) {
-                    const params = {
+                  if (!params.get('id')) {
+                    const param = {
                       name: values.name,
                       description: values.description,
                       provider: props.data.id,
@@ -518,27 +545,35 @@ const Access = (props: Props) => {
                       channel: 'network', // 网络组件
                       channelId: networkCurrent,
                     };
-                    service.save(params).then((resp: any) => {
+                    service.save(param).then((resp: any) => {
                       if (resp.status === 200) {
                         message.success('操作成功!');
-                        history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig'])}`);
+                        history.goBack();
+                        if ((window as any).onTabSaveSuccess) {
+                          (window as any).onTabSaveSuccess(resp);
+                          setTimeout(() => window.close(), 300);
+                        }
                       }
                     });
                   } else {
-                    const params = {
-                      id: props.access?.id,
+                    const param = {
+                      id: access?.id,
                       name: values.name,
                       description: values.description,
-                      provider: props.data.id,
+                      provider: access?.provider,
                       protocol: procotolCurrent,
-                      transport: ProcotoleMapping.get(props.data.id),
+                      transport: access?.transport,
                       channel: 'network', // 网络组件
                       channelId: networkCurrent,
                     };
-                    service.update(params).then((resp: any) => {
+                    service.update(param).then((resp: any) => {
                       if (resp.status === 200) {
                         message.success('操作成功!');
-                        history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig'])}`);
+                        history.goBack();
+                        if ((window as any).onTabSaveSuccess) {
+                          (window as any).onTabSaveSuccess(resp);
+                          setTimeout(() => window.close(), 300);
+                        }
                       }
                     });
                   }

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

@@ -1,30 +1,17 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
 import { useLocation } from 'umi';
 import Access from './Access';
 import Provider from './Provider';
-import { service } from '@/pages/link/AccessConfig';
 
 type LocationType = {
   id?: string;
 };
 
 const Detail = () => {
-  const [visible, setVisible] = useState<boolean>(true);
-  const [data, setData] = useState<any>({});
-  const [config, setConfig] = useState<any>({});
-
   const location = useLocation<LocationType>();
-
-  useEffect(() => {
-    const params = new URLSearchParams(location.search);
-    if (params.get('id')) {
-      service.detail(params.get('id') || '').then((resp) => {
-        setConfig(resp.result);
-        setVisible(false);
-      });
-    }
-  }, []);
+  const [visible, setVisible] = useState<boolean>(!new URLSearchParams(location.search).get('id'));
+  const [data, setData] = useState<any>({});
 
   return (
     <PageContainer>
@@ -38,7 +25,6 @@ const Detail = () => {
       ) : (
         <Access
           data={data}
-          access={config}
           change={() => {
             setVisible(true);
           }}

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

@@ -1,6 +1,6 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { useRef } from 'react';
+import { useEffect, useRef } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { Badge, Button, message, Popconfirm, Tooltip } from 'antd';
 import { CheckCircleOutlined, DeleteOutlined, EditOutlined, StopOutlined } from '@ant-design/icons';
@@ -11,6 +11,8 @@ import { CurdModel } from '@/components/BaseCrud/model';
 import Service from '@/pages/link/Protocol/service';
 import { onFormValuesChange, registerValidateRules } from '@formily/core';
 import { Store } from 'jetlinks-store';
+import { useLocation } from 'umi';
+import SystemConst from '@/utils/const';
 
 export const service = new Service('protocol');
 const Protocol = () => {
@@ -281,6 +283,21 @@ const Protocol = () => {
     },
   };
 
+  const location = useLocation();
+
+  useEffect(() => {
+    if ((location as any).query?.save === 'true') {
+      CurdModel.add();
+    }
+    const subscription = Store.subscribe(SystemConst.BASE_UPDATE_DATA, (data) => {
+      if ((window as any).onTabSaveSuccess) {
+        (window as any).onTabSaveSuccess(data);
+        setTimeout(() => window.close(), 300);
+      }
+    });
+    return () => subscription.unsubscribe();
+  }, []);
+
   return (
     <PageContainer>
       <BaseCrud

+ 4 - 0
src/pages/link/Type/Save/index.tsx

@@ -703,6 +703,10 @@ const Save = observer(() => {
     if (response.status === 200) {
       message.success('保存成功');
       history.back();
+      if ((window as any).onTabSaveSuccess) {
+        (window as any).onTabSaveSuccess(response);
+        setTimeout(() => window.close(), 300);
+      }
     }
   };
   return (