lind 3 лет назад
Родитель
Сommit
7b835ffb20

+ 16 - 0
config/routes.ts

@@ -15,6 +15,22 @@
       },
     ],
   },
+  {
+    path: '/account/center/bind',
+    layout: false,
+    routes: [
+      {
+        path: '/account/center/bind',
+        routes: [
+          {
+            name: 'bind',
+            path: '/account/center/bind',
+            component: './account/Center/bind',
+          },
+        ],
+      },
+    ],
+  },
   // {
   //   path: '/analysis',
   //   name: 'analysis',

+ 9 - 0
src/pages/Northbound/AliCloud/Detail/index.tsx

@@ -204,6 +204,9 @@ const Detail = observer(() => {
         'x-component': 'Select',
         'x-component-props': {
           placeholder: '请选择网桥产品',
+          showSearch: true,
+          filterOption: (input: string, option: any) =>
+            option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
         },
         'x-decorator-props': {
           tooltip: '物联网平台对应的阿里云产品',
@@ -239,6 +242,9 @@ const Detail = observer(() => {
                   'x-component': 'Select',
                   'x-component-props': {
                     placeholder: '请选择阿里云产品',
+                    showSearch: true,
+                    filterOption: (input: string, option: any) =>
+                      option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
                   },
                   'x-decorator-props': {
                     gridSpan: 12,
@@ -257,6 +263,9 @@ const Detail = observer(() => {
                   },
                   'x-component-props': {
                     placeholder: '请选择平台产品',
+                    showSearch: true,
+                    filterOption: (input: string, option: any) =>
+                      option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
                   },
                   'x-reactions': ['{{useAsyncDataSource(queryProductList)}}'],
                 },

+ 5 - 5
src/pages/Northbound/AliCloud/service.ts

@@ -9,7 +9,7 @@ class Service extends BaseService<AliCloudType> {
       method: 'GET',
       params,
     }).then((resp: any) => {
-      return resp.result?.map((item: any) => ({
+      return (resp?.result || []).map((item: any) => ({
         label: item.name,
         value: item.id,
       }));
@@ -20,9 +20,9 @@ class Service extends BaseService<AliCloudType> {
       method: 'POST',
       data,
     }).then((resp: any) => {
-      return resp.result.data?.map((item: any) => ({
-        label: item.productName,
-        value: item.productKey,
+      return (resp?.result?.data || []).map((item: any) => ({
+        label: item?.productName,
+        value: item?.productKey,
       }));
     });
 
@@ -32,7 +32,7 @@ class Service extends BaseService<AliCloudType> {
       method: 'POST',
       data,
     }).then((resp: any) => {
-      return resp.result?.map((item: any) => ({
+      return (resp?.result || []).map((item: any) => ({
         label: item.name,
         value: item.id,
       }));

+ 12 - 0
src/pages/account/Center/bind/index.less

@@ -0,0 +1,12 @@
+.col {
+  display: flex;
+  justify-content: center;
+
+  .item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-around;
+    width: 300px;
+  }
+}

+ 56 - 37
src/pages/account/Center/bind/index.tsx

@@ -1,6 +1,7 @@
-import { Button, Card, Col, message, Row } from 'antd';
+import { Avatar, Button, Card, Col, message, Row } from 'antd';
 import { useEffect, useState } from 'react';
 import Service from '@/pages/account/Center/service';
+import styles from './index.less';
 
 export const service = new Service();
 
@@ -9,6 +10,14 @@ const Bind = () => {
   const [user, setUser] = useState<any>();
   const [code, setCode] = useState<string>('');
 
+  const iconMap = new Map();
+  iconMap.set('dingtalk', require('/public/images/notice/dingtalk.png'));
+  iconMap.set('wechat-webapp', require('/public/images/notice/wechat.png'));
+
+  const bGroundMap = new Map();
+  bGroundMap.set('dingtalk', require('/public/images/notice/dingtalk-background.png'));
+  bGroundMap.set('wechat-webapp', require('/public/images/notice/wechat-background.png'));
+
   const bindUserInfo = (params: string) => {
     service.bindUserInfo(params).then((res) => {
       if (res.status === 200) {
@@ -23,9 +32,8 @@ const Bind = () => {
   };
 
   useEffect(() => {
-    // window.open('http://z.jetlinks.cn')
-    // const item = `http://pro.jetlinks.cn/#/user/login?sso=true&code=4decc08bcb87f3a4fbd74976fd86cd3d&redirect=http://pro.jetlinks.cn/jetlinks`;
     const params = window.location.href.split('?')[1].split('&')[1].split('=')[1];
+    // const params = 'b584032923c78d69e6148cf0e9312723'
     setCode(params);
     bindUserInfo(params);
     getDetail();
@@ -33,42 +41,53 @@ const Bind = () => {
   return (
     <>
       <Card>
-        <Row gutter={[24, 24]}>
-          <Col span={12}>
-            <Card title="个人信息">
-              <p>登录账号:{user?.name}</p>
-              <p>姓名:{user?.name}</p>
-            </Card>
-          </Col>
-          <Col span={12}>
-            <Card title="三方账号信息">
-              <p>类型:{bindUser?.type}</p>
-              <p>组织:{bindUser?.providerName}</p>
-            </Card>
-          </Col>
-        </Row>
-        <Row gutter={[24, 24]}>
-          <Col span={24} style={{ textAlign: 'center', marginTop: 20 }}>
-            <Button
-              type="primary"
-              onClick={() => {
-                service.bind(code).then((res) => {
-                  if (res.status === 200) {
-                    message.success('绑定成功');
-                    if ((window as any).onBindSuccess) {
-                      (window as any).onBindSuccess(res);
+        <div style={{ margin: '0 auto', width: 800 }}>
+          <Row>
+            <Col span={12} className={styles.col}>
+              <Card title="个人信息">
+                <div className={styles.item}>
+                  <div style={{ height: 100 }}>
+                    <Avatar size={90} src={user?.avatar} />
+                  </div>
+                  <p>登录账号:{user?.username}</p>
+                  <p>姓名:{user?.name}</p>
+                </div>
+              </Card>
+            </Col>
+            <Col span={12} className={styles.col}>
+              <Card title="三方账号信息">
+                <div className={styles.item}>
+                  <div style={{ height: 100 }}>
+                    <img style={{ height: 80 }} src={iconMap.get(bindUser?.type)} />
+                  </div>
+                  <p>组织:{bindUser?.providerName}</p>
+                  <p>名字:{bindUser?.result.others.name}</p>
+                </div>
+              </Card>
+            </Col>
+          </Row>
+          <Row>
+            <Col span={24} style={{ textAlign: 'center', marginTop: 20 }}>
+              <Button
+                type="primary"
+                onClick={() => {
+                  // window.close()
+                  service.bind(code).then((res) => {
+                    if (res.status === 200) {
+                      message.success('绑定成功');
+                      localStorage.setItem('onBind', 'true');
                       setTimeout(() => window.close(), 300);
+                    } else {
+                      message.error('绑定失败');
                     }
-                  } else {
-                    message.error('绑定失败');
-                  }
-                });
-              }}
-            >
-              立即绑定
-            </Button>
-          </Col>
-        </Row>
+                  });
+                }}
+              >
+                立即绑定
+              </Button>
+            </Col>
+          </Row>
+        </div>
       </Card>
     </>
   );

+ 25 - 11
src/pages/account/Center/index.tsx

@@ -1,5 +1,16 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { Avatar, Button, Card, Col, Descriptions, Divider, message, Row, Upload } from 'antd';
+import {
+  Avatar,
+  Button,
+  Card,
+  Col,
+  Descriptions,
+  Divider,
+  message,
+  Popconfirm,
+  Row,
+  Upload,
+} from 'antd';
 import { useEffect, useState } from 'react';
 import styles from './index.less';
 import { UploadProps } from 'antd/lib/upload';
@@ -222,7 +233,7 @@ const Center = () => {
                             color: '#00000073',
                           }}
                         >
-                          绑定时间: 2022-01-02 09:00:00
+                          绑定时间: {moment(item.bindTime).format('YYYY-MM-DD HH:mm:ss')}
                         </div>
                       </div>
                     ) : (
@@ -233,27 +244,30 @@ const Center = () => {
                   </div>
                   <div>
                     {item.bound ? (
-                      <Button
-                        onClick={() => {
+                      <Popconfirm
+                        title="确认解除绑定嘛?"
+                        onConfirm={() => {
                           unBind(item.type, item.provider);
                         }}
                       >
-                        解除绑定
-                      </Button>
+                        <Button>解除绑定</Button>
+                      </Popconfirm>
                     ) : (
                       <Button
                         type="primary"
                         onClick={() => {
-                          const items: any = window.open(
+                          window.open(
                             `/${SystemConst.API_BASE}/sso/${item.provider}/login`,
+                            '',
+                            'width=700,height=500,left=500,top=300',
                           );
-                          //  const items:any= window.open(`/#/account/Center/bind`);
-                          items!.onBindSuccess = (value: any) => {
-                            if (value.status === 200) {
+                          // window.open(`/#/account/center/bind`,'','width=700,height=500,left=500,top=300');
+                          localStorage.setItem('onBind', 'false');
+                          window.onstorage = (e) => {
+                            if (e.newValue) {
                               getBindInfo();
                             }
                           };
-                          // history.push(getMenuPathByCode('account/Center/bind'));
                         }}
                       >
                         立即绑定

+ 6 - 0
src/pages/link/Channel/Opcua/Access/index.tsx

@@ -0,0 +1,6 @@
+import { PageContainer } from '@ant-design/pro-layout';
+
+const Access = () => {
+  return <PageContainer>Access</PageContainer>;
+};
+export default Access;

+ 256 - 0
src/pages/link/Channel/Opcua/Save/index.tsx

@@ -0,0 +1,256 @@
+import { useIntl } from 'umi';
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import { Form, FormGrid, FormItem, Input, Select } from '@formily/antd';
+import type { ISchema } from '@formily/json-schema';
+import { service } from '@/pages/link/Channel/Opcua';
+import { Modal } from '@/components';
+import { message } from 'antd';
+import { useEffect, useState } from 'react';
+
+interface Props {
+  data: Partial<OpaUa>;
+  close: () => void;
+}
+
+const Save = (props: Props) => {
+  const intl = useIntl();
+  const [policies, setPolicies] = useState<any>([]);
+  const [modes, setModes] = useState<any>([]);
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: {
+      ...props.data,
+      clientConfigs: props.data?.clientConfigs?.[0],
+    },
+  });
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Input,
+      Select,
+      FormGrid,
+    },
+  });
+
+  const schema: ISchema = {
+    type: 'object',
+    properties: {
+      layout: {
+        type: 'void',
+        'x-decorator': 'FormGrid',
+        'x-decorator-props': {
+          maxColumns: 2,
+          minColumns: 2,
+          columnGap: 24,
+        },
+        properties: {
+          name: {
+            title: '名称',
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            'x-component-props': {
+              placeholder: '请输入名称',
+            },
+            name: 'name',
+            'x-validator': [
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+              {
+                required: true,
+                message: '请输入名称',
+              },
+            ],
+          },
+          'clientConfigs.endpoint': {
+            title: '服务地址',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input',
+            'x-component-props': {
+              placeholder: '请输入服务地址',
+            },
+            'x-validator': [
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+              {
+                required: true,
+                message: '请输入服务地址',
+              },
+              {
+                pattern:
+                  '(opc.tcp|http|https|opc.http|opc.https|opc.ws|opc.wss)://([^:/]+|\\[.*])(:\\d+)?(/.*)?',
+                message: '格式错误(opc.tcp://127.0.0.1:49320)',
+              },
+            ],
+            name: 'endpoint',
+            required: true,
+          },
+          'clientConfigs.securityPolicy': {
+            title: '安全策略',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              placeholder: '请选择安全策略',
+              showArrow: true,
+              filterOption: (input: string, option: any) =>
+                option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
+            },
+            required: true,
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择安全策略',
+              },
+            ],
+            enum: policies,
+          },
+          'clientConfigs.securityMode': {
+            title: '安全模式',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              placeholder: '请选择安全模式',
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择安全模式',
+              },
+            ],
+            required: true,
+            enum: modes,
+          },
+          'clientConfigs.username': {
+            title: '用户名',
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input',
+            'x-decorator-props': {
+              gridSpan: 1,
+            },
+            'x-component-props': {
+              placeholder: '请输入用户名',
+            },
+            name: 'name',
+            'x-validator': [
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+            ],
+          },
+          'clientConfigs.password': {
+            title: '密码',
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            'x-component-props': {
+              placeholder: '请输入密码',
+            },
+            name: 'name',
+            'x-validator': [
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+            ],
+          },
+          description: {
+            title: '说明',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input.TextArea',
+            'x-component-props': {
+              rows: 5,
+              placeholder: '请输入说明',
+            },
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
+            'x-validator': [
+              {
+                max: 200,
+                message: '最多可输入200个字符',
+              },
+            ],
+          },
+        },
+      },
+    },
+  };
+
+  const save = async () => {
+    const value = await form.submit<any>();
+    console.log(value);
+    const item = {
+      name: value.name,
+      description: value.description,
+      clientConfigs: [value.clientConfigs],
+    };
+    if (props.data.id) {
+      service.modify(props.data.id, item).then((res: any) => {
+        if (res.status === 200) {
+          message.success('保存成功');
+          props.close();
+        }
+      });
+    } else {
+      service.save(item).then((res: any) => {
+        if (res.status === 200) {
+          message.success('保存成功');
+          props.close();
+        }
+      });
+    }
+  };
+
+  useEffect(() => {
+    service.policies().then((res) => setPolicies(res.result));
+    service.modes().then((res) => setModes(res.result));
+    console.log(props.data.clientConfigs?.[0]);
+  }, []);
+  return (
+    <Modal
+      title={intl.formatMessage({
+        id: `pages.data.option.${props.data.id ? 'edit' : 'add'}`,
+        defaultMessage: '编辑',
+      })}
+      maskClosable={false}
+      visible
+      onCancel={props.close}
+      onOk={save}
+      width="35vw"
+      permissionCode={'system/Relationship'}
+      permission={['add', 'edit']}
+    >
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema} />
+      </Form>
+    </Modal>
+  );
+};
+export default Save;

+ 72 - 53
src/pages/link/Channel/Opcua/index.tsx

@@ -1,25 +1,32 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
-import { Badge, Card, Col, Row } from 'antd';
+import { Badge, Card, Col, message, Row } from 'antd';
 import styles from './index.less';
 import { PermissionButton } from '@/components';
+import { history, useIntl } from 'umi';
 import {
+  ControlOutlined,
   DeleteOutlined,
   EditOutlined,
-  LinkOutlined,
   PlayCircleOutlined,
   PlusOutlined,
   StopOutlined,
 } from '@ant-design/icons';
-import { useIntl } from 'umi';
-import { useRef } from 'react';
+import { useRef, useState } from 'react';
 import SearchComponent from '@/components/SearchComponent';
+import Service from './service';
+import Save from './Save';
+import { getMenuPathByCode } from '@/utils/menu';
+
+export const service = new Service('opc/client');
 
 const Opcua = () => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
-  // const [param, setParam] = useState({});
+  const [param, setParam] = useState({});
   const { permission } = PermissionButton.usePermission('link/Channel/Opcua');
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<Partial<OpaUa>>({});
 
   const iconMap = new Map();
   iconMap.set('1', require('/public/images/channel/1.png'));
@@ -28,18 +35,19 @@ const Opcua = () => {
   iconMap.set('4', require('/public/images/channel/4.png'));
   const background = require('/public/images/channel/background.png');
 
-  const columns: ProColumns<any>[] = [
+  const columns: ProColumns<OpaUa>[] = [
     {
       title: '通道名称',
       dataIndex: 'name',
     },
     {
-      title: 'IP',
-      dataIndex: 'ip',
+      title: '服务地址',
+      // dataIndex: 'clientConfigs',
+      render: (_, record) => <>{record.clientConfigs?.[0].endpoint}</>,
     },
     {
-      title: '端口',
-      dataIndex: 'local',
+      title: '安全策略',
+      render: (_, record) => <>{record.clientConfigs?.[0].securityPolicy}</>,
     },
     {
       title: '状态',
@@ -58,8 +66,8 @@ const Opcua = () => {
           isPermission={permission.update}
           key="edit"
           onClick={() => {
-            // setVisible(true);
-            // setCurrent(record);
+            setVisible(true);
+            setCurrent(record);
           }}
           type={'link'}
           style={{ padding: 0 }}
@@ -79,48 +87,48 @@ const Opcua = () => {
           popConfirm={{
             title: intl.formatMessage({
               id: `pages.data.option.${
-                record.state.value !== 'notActive' ? 'disabled' : 'enabled'
+                record.state.value !== 'disabled' ? 'disabled' : 'enabled'
               }.tips`,
               defaultMessage: '确认禁用?',
             }),
             onConfirm: async () => {
-              // if (record.state.value !== 'notActive') {
-              //   await service.undeployDevice(record.id);
-              // } else {
-              //   await service.deployDevice(record.id);
-              // }
-              // message.success(
-              //   intl.formatMessage({
-              //     id: 'pages.data.option.success',
-              //     defaultMessage: '操作成功!',
-              //   }),
-              // );
-              // actionRef.current?.reload();
+              if (record.state.value === 'disabled') {
+                await service.enable(record.id);
+              } else {
+                await service.disable(record.id);
+              }
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
             },
           }}
           isPermission={permission.action}
           tooltip={{
             title: intl.formatMessage({
-              id: `pages.data.option.${
-                record.state.value !== 'notActive' ? 'disabled' : 'enabled'
-              }`,
-              defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
+              id: `pages.data.option.${record.state.value !== 'disabled' ? 'disabled' : 'enabled'}`,
+              defaultMessage: record.state.value !== 'disabled' ? '禁用' : '启用',
             }),
           }}
         >
-          {record.state.value !== 'notActive' ? <StopOutlined /> : <PlayCircleOutlined />}
+          {record.state.value !== 'disabled' ? <StopOutlined /> : <PlayCircleOutlined />}
         </PermissionButton>,
         <PermissionButton
           isPermission={permission.view}
           style={{ padding: 0 }}
-          key="button"
+          key="link"
           type="link"
           tooltip={{
             title: '设备接入',
           }}
-          onClick={() => {}}
+          onClick={() => {
+            history.push(`${getMenuPathByCode('link/Channel/Opcua/Access')}?id=${record.id}`);
+          }}
         >
-          <LinkOutlined />
+          <ControlOutlined />
         </PermissionButton>,
         <PermissionButton
           isPermission={permission.delete}
@@ -128,19 +136,19 @@ const Opcua = () => {
           popConfirm={{
             title: '确认删除',
             onConfirm: async () => {
-              // const resp: any = await service.remove(record.id);
-              // if (resp.status === 200) {
-              //   message.success(
-              //     intl.formatMessage({
-              //       id: 'pages.data.option.success',
-              //       defaultMessage: '操作成功!',
-              //     }),
-              //   );
-              //   actionRef.current?.reload();
-              // }
+              const resp: any = await service.remove(record.id);
+              if (resp.status === 200) {
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              }
             },
           }}
-          key="button"
+          key="delete"
           type="link"
         >
           <DeleteOutlined />
@@ -176,7 +184,7 @@ const Opcua = () => {
       <Card style={{ marginBottom: 10 }}>
         <Row gutter={[24, 24]}>
           {topCard.map((item) => (
-            <Col span={6}>
+            <Col span={6} key={item.numeber}>
               <Card>
                 <div className={styles.topCard}>
                   <div
@@ -206,22 +214,24 @@ const Opcua = () => {
         onSearch={(data) => {
           // 重置分页数据
           actionRef.current?.reset?.();
-          console.log(data);
-          // setParam(data);
+          setParam(data);
         }}
       />
-      <ProTable<UserItem>
+      <ProTable<OpaUa>
         actionRef={actionRef}
-        // params={param}
+        params={param}
         columns={columns}
+        rowKey="id"
         search={false}
         headerTitle={
           <PermissionButton
             onClick={() => {
               // setMode('add');
+              setVisible(true);
+              setCurrent({});
             }}
             isPermission={permission.add}
-            key="button"
+            key="add"
             icon={<PlusOutlined />}
             type="primary"
           >
@@ -231,10 +241,19 @@ const Opcua = () => {
             })}
           </PermissionButton>
         }
-        // request={async (params) =>
-        //   service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
-        // }
+        request={async (params) =>
+          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+        }
       />
+      {visible && (
+        <Save
+          data={current}
+          close={() => {
+            setVisible(false);
+            actionRef.current?.reload();
+          }}
+        />
+      )}
     </PageContainer>
   );
 };

+ 26 - 0
src/pages/link/Channel/Opcua/service.ts

@@ -0,0 +1,26 @@
+import BaseService from '@/utils/BaseService';
+import { request } from 'umi';
+import SystemConst from '@/utils/const';
+
+class Service extends BaseService<OpaUa> {
+  enable = (id: string) =>
+    request(`${SystemConst.API_BASE}/opc/client/${id}/_enable`, {
+      method: 'POST',
+    });
+  disable = (id: string) =>
+    request(`${SystemConst.API_BASE}/opc/client/${id}/_disable`, {
+      method: 'POST',
+    });
+  policies = (params?: any) =>
+    request(`${SystemConst.API_BASE}/opc/client/security-policies`, {
+      method: 'GET',
+      params,
+    });
+  modes = (params?: any) =>
+    request(`${SystemConst.API_BASE}/opc/client/security-modes`, {
+      method: 'GET',
+      params,
+    });
+}
+
+export default Service;

+ 7 - 1
src/pages/link/Channel/Opcua/typings.d.ts

@@ -1,4 +1,10 @@
-type Item = {
+type OpaUa = {
   id: string;
   name: string;
+  clientConfigs?: Record<string, any>[];
+  description?: string;
+  state: {
+    text: string;
+    value: string;
+  };
 };

+ 20 - 6
src/utils/menu/index.ts

@@ -36,6 +36,20 @@ const extraRouteObj = {
       { code: 'Save2', name: '测试详情' },
     ],
   },
+  'link/Channel': {
+    children: [
+      {
+        code: 'Opcua',
+        name: 'OPC UA',
+        children: [
+          {
+            code: 'Access',
+            name: '数据点绑定',
+          },
+        ],
+      },
+    ],
+  },
   demo: {
     children: [{ code: 'AMap', name: '地图' }],
   },
@@ -54,12 +68,12 @@ export const extraRouteArr = [
         name: '基本设置',
         url: '/account/center',
       },
-      {
-        code: 'account/Center/bind',
-        name: '第三方页面',
-        url: '/account/center/bind',
-        hideInMenu: true,
-      },
+      // {
+      //   code: 'account/Center/bind',
+      //   name: '第三方页面',
+      //   url: '/account/center/bind',
+      //   hideInMenu: true,
+      // },
     ],
   },
 ];

+ 2 - 1
src/utils/menu/router.ts

@@ -35,7 +35,8 @@ export enum MENUS_CODE {
   'link/Certificate' = 'link/Certificate',
   'link/Gateway' = 'link/Gateway',
   'link/Opcua' = 'link/Opcua',
-  'link/Channal/Opcua' = 'link/Channal/Opcua',
+  'link/Channel/Opcua' = 'link/Channel/Opcua',
+  'link/Channel/Opcua/Access' = 'link/Channel/Opcua/Access',
   'link/Protocol/Debug' = 'link/Protocol/Debug',
   'link/Protocol' = 'link/Protocol',
   'link/Type' = 'link/Type',