wzyyy 3 лет назад
Родитель
Сommit
0ed9485fa7

+ 2 - 2
config/proxy.ts

@@ -9,8 +9,8 @@
 export default {
   dev: {
     '/jetlinks': {
-      // target: 'http://192.168.32.8:8844/',
-      // ws: 'ws://192.168.32.8:8844/',
+      // target: 'http://192.168.32.44:8844/',
+      // ws: 'ws://192.168.32.44:8844/',
       target: 'http://120.79.18.123:8844/',
       ws: 'ws://120.79.18.123:8844/',
       // target: 'http://192.168.66.5:8844/',

+ 4 - 0
src/locales/zh-CN/pages.ts

@@ -18,6 +18,10 @@ export default {
   'pages.data.option.disabled.tips': '确认禁用?',
   'pages.data.option.enabled': '启用',
   'pages.data.option.enabled.tips': '确认启用?',
+  'pages.data.option.disable': '禁用',
+  'pages.data.option.disable.tips': '确认禁用?',
+  'pages.data.option.enable': '启用',
+  'pages.data.option.enable.tips': '确认启用?',
   'pages.data.option.noPermission': '暂无权限,请联系管理员',
   'pages.data.option.add': '新增',
   'pages.data.option.edit': '编辑',

+ 3 - 19
src/pages/device/Instance/Detail/Modbus/index.tsx

@@ -14,7 +14,7 @@ import {
 import { service } from '@/pages/link/Channel/Modbus';
 import Save from '@/pages/link/Channel/Modbus/Save';
 import { InstanceModel } from '@/pages/device/Instance';
-import AddPoint from '@/pages/link/Channel/Opcua/Access/addPoint';
+import AddPoint from '@/pages/link/Channel/Modbus/Access/addPoint';
 
 const Modbus = () => {
   const intl = useIntl();
@@ -162,10 +162,7 @@ const Modbus = () => {
       })
       .then((res) => {
         setBindList(res.result.data);
-        setOpcId(res.result?.[0]?.id);
-        // setParam({
-        //     sorts: [{ name: 'createTime', order: 'desc' }],
-        // })
+        setOpcId(res.result?.data?.[0].id);
       });
   };
 
@@ -177,12 +174,6 @@ const Modbus = () => {
     }
   }, [visible]);
 
-  // useEffect(() => {
-  //     if(opcId){
-  //         setLoading(false)
-  //     }
-  //  }, [opcId])
-
   return (
     <Card>
       <PermissionButton
@@ -204,9 +195,7 @@ const Modbus = () => {
           defaultActiveKey={opcId}
           onChange={(e) => {
             setOpcId(e);
-            setParam({
-              terms: [{ column: 'opcUaId', value: e }],
-            });
+            setParam({});
           }}
         >
           {bindList.map((item: any) => (
@@ -261,7 +250,6 @@ const Modbus = () => {
             >
               <ProTable
                 actionRef={actionRef}
-                // loading={loading}
                 params={param}
                 columns={columns}
                 rowKey="id"
@@ -286,10 +274,6 @@ const Modbus = () => {
                   </>
                 }
                 request={async (params) => {
-                  console.log(opcId);
-                  // setTimeout(() => {
-                  //     const master =
-                  // }, 10);
                   const res = await service.queryMetadataConfig(opcId, deviceId, {
                     ...params,
                     sorts: [{ name: 'createTime', order: 'desc' }],

+ 158 - 125
src/pages/device/Instance/Detail/Opcua/index.tsx

@@ -15,6 +15,8 @@ import { service } from '@/pages/link/Channel/Opcua';
 import Save from '@/pages/link/Channel/Opcua/Save';
 import { InstanceModel } from '@/pages/device/Instance';
 import AddPoint from '@/pages/link/Channel/Opcua/Access/addPoint';
+import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
+import { map } from 'rxjs/operators';
 
 const Opcua = () => {
   const intl = useIntl();
@@ -28,6 +30,10 @@ const Opcua = () => {
   const [pointVisiable, setPointVisiable] = useState<boolean>(false);
   const [current, setCurrent] = useState<any>({});
   const [deviceId, setDeviceId] = useState<any>('');
+  const [data, setData] = useState<any>([]);
+  const [subscribeTopic] = useSendWebsocketMessage();
+  const [propertyValue, setPropertyValue] = useState<any>({});
+  const wsRef = useRef<any>();
 
   const columns: ProColumns<any>[] = [
     {
@@ -48,8 +54,7 @@ const Opcua = () => {
     },
     {
       title: '值',
-      // dataIndex: '4',
-      //   render: (record: any) => <>{propertyValue[record.property]}</>,
+      render: (record: any) => <>{propertyValue[record?.property] || '-'}</>,
     },
     {
       title: '状态',
@@ -88,11 +93,13 @@ const Opcua = () => {
           style={{ padding: 0 }}
           popConfirm={{
             title: intl.formatMessage({
-              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}.tips`,
+              id: `pages.data.option.${
+                record.state?.value !== 'disable' ? 'disable' : 'enable'
+              }.tips`,
               defaultMessage: '确认禁用?',
             }),
             onConfirm: async () => {
-              if (record.state.value === 'disable') {
+              if (record.state?.value === 'disable') {
                 await service.enablePoint(record.deviceId, [record.id]);
               } else {
                 await service.stopPoint(record.deviceId, [record.id]);
@@ -109,25 +116,25 @@ const Opcua = () => {
           isPermission={permission.action}
           tooltip={{
             title: intl.formatMessage({
-              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}`,
-              defaultMessage: record.state.value !== 'disable' ? '禁用' : '启用',
+              id: `pages.data.option.${record.state?.value !== 'disable' ? 'disable' : 'enable'}`,
+              defaultMessage: record.state?.value !== 'disable' ? '禁用' : '启用',
             }),
           }}
         >
-          {record.state.value !== 'disable' ? <StopOutlined /> : <PlayCircleOutlined />}
+          {record.state?.value !== 'disable' ? <StopOutlined /> : <PlayCircleOutlined />}
         </PermissionButton>,
         <PermissionButton
           isPermission={permission.delete}
           style={{ padding: 0 }}
-          disabled={record.state.value === 'good'}
+          disabled={record.state?.value === 'enable'}
           tooltip={{
             title:
-              record.state.value === 'disable'
+              record.state?.value === 'disable'
                 ? intl.formatMessage({
                     id: 'pages.data.option.remove',
                     defaultMessage: '删除',
                   })
-                : '请先禁用该组件,再删除。',
+                : '请先禁用该点位,再删除。',
           }}
           popConfirm={{
             title: '确认删除',
@@ -164,7 +171,7 @@ const Opcua = () => {
           },
         ],
       })
-      .then((res) => {
+      .then((res: any) => {
         setBindList(res.result);
         setOpcId(res.result?.[0]?.id);
         setParam({
@@ -181,128 +188,154 @@ const Opcua = () => {
     }
   }, [visible]);
 
+  useEffect(() => {
+    const { id, productId } = InstanceModel.detail;
+    const point = data.map((item: any) => item.property);
+    const wsId = `instance-info-property-${id}-${productId}-${point.join('-')}`;
+    const topic = `/dashboard/device/${productId}/properties/realTime`;
+    wsRef.current = subscribeTopic?.(wsId, topic, {
+      deviceId: deviceId,
+      properties: data.map((item: any) => item.property),
+      history: 1,
+    })
+      ?.pipe(map((res: any) => res.payload))
+      .subscribe((payload: any) => {
+        const { value } = payload;
+        propertyValue[value.property] = value.formatValue;
+        setPropertyValue({ ...propertyValue });
+        // console.log(propertyValue)
+      });
+  }, [data]);
+
   return (
-    <Card>
-      <PermissionButton
-        onClick={() => {
-          setVisible(true);
-          setChannel({});
-        }}
-        isPermission={permission.add}
-        key="add"
-        icon={<PlusOutlined />}
-        type="dashed"
-        style={{ width: '200px', marginLeft: 20, marginBottom: 5 }}
-      >
-        新增通道
-      </PermissionButton>
+    <Card className={styles.list}>
       {bindList.length > 0 ? (
-        <Tabs
-          tabPosition={'left'}
-          defaultActiveKey={opcId}
-          onChange={(e) => {
-            setOpcId(e);
-            setParam({
-              terms: [{ column: 'opcUaId', value: e }],
-            });
-          }}
-        >
-          {bindList.map((item: any) => (
-            <Tabs.TabPane
-              key={item.id}
-              tab={
-                <div className={styles.left}>
-                  <div style={{ width: '100px', textAlign: 'left' }}>{item.name}</div>
+        <div style={{ display: 'flex' }}>
+          <div>
+            <PermissionButton
+              onClick={() => {
+                setVisible(true);
+                setChannel({});
+              }}
+              isPermission={permission.add}
+              key="add"
+              icon={<PlusOutlined />}
+              type="dashed"
+              style={{ width: '200px', margin: '16px 0 18px 20px' }}
+            >
+              新增通道
+            </PermissionButton>
+            <Tabs
+              tabPosition={'left'}
+              defaultActiveKey={opcId}
+              onChange={(e) => {
+                setOpcId(e);
+                setParam({
+                  terms: [{ column: 'opcUaId', value: e }],
+                });
+              }}
+            >
+              {bindList.map((item: any) => (
+                <Tabs.TabPane
+                  key={item.id}
+                  tab={
+                    <div className={styles.left}>
+                      <div className={styles.text}>{item.name}</div>
+                      <div>
+                        <PermissionButton
+                          isPermission={permission.update}
+                          key="edit"
+                          onClick={() => {
+                            setVisible(true);
+                            setChannel(item);
+                          }}
+                          type={'link'}
+                          style={{ padding: 0 }}
+                          tooltip={{
+                            title: intl.formatMessage({
+                              id: 'pages.data.option.edit',
+                              defaultMessage: '编辑',
+                            }),
+                          }}
+                        >
+                          <EditOutlined />
+                        </PermissionButton>
+                        <PermissionButton
+                          isPermission={permission.delete}
+                          style={{ padding: 0 }}
+                          popConfirm={{
+                            title: '确认删除',
+                            onConfirm: async () => {
+                              const resp: any = await service.remove(item.id);
+                              if (resp.status === 200) {
+                                getOpc(deviceId);
+                                message.success(
+                                  intl.formatMessage({
+                                    id: 'pages.data.option.success',
+                                    defaultMessage: '操作成功!',
+                                  }),
+                                );
+                              }
+                            },
+                          }}
+                          key="delete"
+                          type="link"
+                        >
+                          <DeleteOutlined />
+                        </PermissionButton>
+                      </div>
+                    </div>
+                  }
+                ></Tabs.TabPane>
+              ))}
+            </Tabs>
+          </div>
+          <div style={{ width: '100%' }}>
+            <ProTable
+              actionRef={actionRef}
+              params={param}
+              columns={columns}
+              rowKey="id"
+              search={false}
+              headerTitle={
+                <>
                   <PermissionButton
-                    isPermission={permission.update}
-                    key="edit"
                     onClick={() => {
-                      setVisible(true);
-                      setChannel(item);
-                    }}
-                    type={'link'}
-                    style={{ padding: 0 }}
-                    tooltip={{
-                      title: intl.formatMessage({
-                        id: 'pages.data.option.edit',
-                        defaultMessage: '编辑',
-                      }),
+                      setPointVisiable(true);
+                      setCurrent({});
                     }}
+                    isPermission={permission.add}
+                    key="add"
+                    icon={<PlusOutlined />}
+                    type="primary"
                   >
-                    <EditOutlined />
+                    {intl.formatMessage({
+                      id: 'pages.data.option.add',
+                      defaultMessage: '新增',
+                    })}
                   </PermissionButton>
-                  <PermissionButton
-                    isPermission={permission.delete}
-                    style={{ padding: 0 }}
-                    popConfirm={{
-                      title: '确认删除',
-                      onConfirm: async () => {
-                        const resp: any = await service.remove(item.id);
-                        if (resp.status === 200) {
-                          getOpc(deviceId);
-                          message.success(
-                            intl.formatMessage({
-                              id: 'pages.data.option.success',
-                              defaultMessage: '操作成功!',
-                            }),
-                          );
-                        }
-                      },
-                    }}
-                    key="delete"
-                    type="link"
-                  >
-                    <DeleteOutlined />
-                  </PermissionButton>
-                </div>
+                </>
               }
-            >
-              <ProTable
-                actionRef={actionRef}
-                params={param}
-                columns={columns}
-                rowKey="id"
-                search={false}
-                headerTitle={
-                  <>
-                    <PermissionButton
-                      onClick={() => {
-                        setPointVisiable(true);
-                        setCurrent({});
-                      }}
-                      isPermission={permission.add}
-                      key="add"
-                      icon={<PlusOutlined />}
-                      type="primary"
-                    >
-                      {intl.formatMessage({
-                        id: 'pages.data.option.add',
-                        defaultMessage: '新增',
-                      })}
-                    </PermissionButton>
-                  </>
-                }
-                request={async (params) => {
-                  const res = await service.PointList({
-                    ...params,
-                    sorts: [{ name: 'createTime', order: 'desc' }],
-                  });
-                  // setData(res.result.data);
-                  return {
-                    code: res.message,
-                    result: {
-                      data: res.result.data,
-                      pageIndex: 0,
-                      pageSize: 0,
-                      total: 0,
-                    },
-                    status: res.status,
-                  };
-                }}
-              />
-            </Tabs.TabPane>
-          ))}
-        </Tabs>
+              request={async (params) => {
+                const res = await service.PointList({
+                  ...params,
+                  sorts: [{ name: 'createTime', order: 'desc' }],
+                });
+                setData(res.result.data);
+                return {
+                  code: res.message,
+                  result: {
+                    data: res.result.data,
+                    pageIndex: 0,
+                    pageSize: 0,
+                    total: 0,
+                  },
+                  status: res.status,
+                };
+              }}
+            />
+          </div>
+        </div>
       ) : (
         <Empty />
       )}

+ 3 - 3
src/pages/link/Channel/Modbus/Access/addPoint/index.tsx

@@ -23,7 +23,7 @@ const AddPoint = (props: Props) => {
         metadataType: 'property',
         codec: 'number',
       })
-      .then((res) => {
+      .then((res: any) => {
         if (res.status === 200) {
           message.success('操作成功!');
           props.close();
@@ -33,7 +33,7 @@ const AddPoint = (props: Props) => {
   };
 
   useEffect(() => {
-    service.deviceDetail(props.deviceId).then((res) => {
+    service.deviceDetail(props.deviceId).then((res: any) => {
       if (res.result.metadata) {
         const item = JSON.parse(res.result?.metadata);
         setProperty(item.properties);
@@ -124,7 +124,7 @@ const AddPoint = (props: Props) => {
                 { required: true, message: '请输入读取起始位置' },
                 ({}) => ({
                   validator(_, value) {
-                    if (/(^[1-9]\d*$)/.test(value)) {
+                    if (/(^[0-9]\d*$)/.test(value)) {
                       return Promise.resolve();
                     }
                     return Promise.reject(new Error('请输入正整数'));

+ 20 - 25
src/pages/link/Channel/Modbus/Access/index.tsx

@@ -163,25 +163,6 @@ const Access = () => {
     },
   ];
 
-  const pointWs = () => {
-    if (productId && deviceId) {
-      const id = `instance-info-property-${deviceId}-${productId}-opc-point`;
-      const topic = `/dashboard/device/${productId}/properties/realTime`;
-      subscribeTopic!(id, topic, {
-        deviceId: deviceId,
-        properties: data.map((item: any) => item.property),
-        history: 0,
-      })
-        ?.pipe(map((res) => res.patload))
-        .subscribe((payload: any) => {
-          const { value } = payload;
-          console.log(value);
-          propertyValue[value.property] = { ...payload, ...value };
-          setPropertyValue({ ...propertyValue });
-        });
-    }
-  };
-
   const getBindList = (masterId: any) => {
     service
       .bindDevice(
@@ -191,7 +172,7 @@ const Access = () => {
           },
         }),
       )
-      .then((res) => {
+      .then((res: any) => {
         console.log(res.result);
         if (res.status === 200) {
           setDeviceId(res.result[0]?.id);
@@ -203,10 +184,24 @@ const Access = () => {
         }
       });
   };
-
-  // useEffect(() => {
-  //   pointWs();
-  // }, [deviceId, productId])
+  const pointWs = () => {
+    if (productId && deviceId) {
+      const id = `instance-info-property-${deviceId}-${productId}-opc-point`;
+      const topic = `/dashboard/device/${productId}/properties/realTime`;
+      subscribeTopic!(id, topic, {
+        deviceId: deviceId,
+        properties: data.map((item: any) => item.metadataId),
+        history: 0,
+      })
+        ?.pipe(map((res) => res.patload))
+        .subscribe((payload: any) => {
+          const { value } = payload;
+          console.log(value);
+          propertyValue[value.property] = { ...payload, ...value };
+          setPropertyValue({ ...propertyValue });
+        });
+    }
+  };
 
   useEffect(() => {
     pointWs();
@@ -258,7 +253,7 @@ const Access = () => {
                     <Popconfirm
                       title="确认解绑该设备嘛?"
                       onConfirm={() => {
-                        service.unbind([item.id], opcUaId).then((res) => {
+                        service.unbind([item.id], opcUaId).then((res: any) => {
                           if (res.status === 200) {
                             message.success('解绑成功');
                             getBindList(opcUaId);

+ 23 - 6
src/pages/link/Channel/Modbus/Save/index.tsx

@@ -6,6 +6,7 @@ import type { ISchema } from '@formily/json-schema';
 import { service } from '@/pages/link/Channel/Modbus';
 import { Modal } from '@/components';
 import { message } from 'antd';
+import { useEffect } from 'react';
 
 interface Props {
   data: any;
@@ -141,15 +142,31 @@ const Save = (props: Props) => {
           }
         });
     } else {
-      service.save(value).then((res: any) => {
-        if (res.status === 200) {
-          message.success('保存成功');
-          props.close();
-        }
-      });
+      if (props.device) {
+        service.save(value).then((res: any) => {
+          if (res.status === 200) {
+            service.bind([props.device.id], res.result.id).then((resp: any) => {
+              if (resp.status === 200) {
+                message.success('保存成功');
+                props.close();
+              }
+            });
+          }
+        });
+      } else {
+        service.save(value).then((res: any) => {
+          if (res.status === 200) {
+            message.success('保存成功');
+            props.close();
+          }
+        });
+      }
     }
   };
 
+  useEffect(() => {
+    console.log(props.data.id);
+  }, []);
   return (
     <Modal
       title={intl.formatMessage({

+ 3 - 2
src/pages/link/Channel/Opcua/Access/addPoint/index.tsx

@@ -123,6 +123,7 @@ const AddPoint = (props: Props) => {
         layout="vertical"
         initialValues={{
           ...props.data,
+          enableCalculate: props.data.enableCalculate || false,
           initialValue: props.data?.configuration?.initialValue,
           multiple: props.data?.configuration?.multiple,
         }}
@@ -145,7 +146,7 @@ const AddPoint = (props: Props) => {
         <Row gutter={[24, 24]}>
           <Col span={12}>
             <Form.Item
-              label="属性"
+              label="属性ID"
               name="property"
               required
               rules={[{ required: true, message: '属性必选' }]}
@@ -235,7 +236,7 @@ const AddPoint = (props: Props) => {
             </Col>
             <Col span={12}>
               <Form.Item label="倍数" name="multiple" required>
-                <InputNumber style={{ width: '100%' }} min={1} />
+                <InputNumber style={{ width: '100%' }} min={1} placeholder="请输入倍数" />
               </Form.Item>
             </Col>
           </Row>

+ 18 - 3
src/pages/link/Channel/Opcua/Access/index.less

@@ -1,7 +1,12 @@
 .list {
   :global {
-    .ant-tabs-tab .ant-tabs-tab-active .ant-tabs-tab-btn {
-      text-shadow: 0 0 0 currentColor;
+    .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
+      color: #1d39c4;
+      text-shadow: none;
+    }
+
+    .ant-tabs-content-holder {
+      width: 1px;
     }
   }
 }
@@ -11,7 +16,17 @@
   align-items: center;
   justify-content: space-between;
   width: 200px;
-
+  .text {
+    width: 130px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-align: left;
+    text-overflow: ellipsis;
+    word-break: break-all;
+  }
+  .text:hover {
+    width: auto;
+  }
   .icon {
     display: none;
   }

+ 151 - 150
src/pages/link/Channel/Opcua/Access/index.tsx

@@ -36,6 +36,7 @@ const Access = () => {
   const [data, setData] = useState<any>([]);
   const [subscribeTopic] = useSendWebsocketMessage();
   const [propertyValue, setPropertyValue] = useState<any>({});
+  const wsRef = useRef<any>();
 
   const columns: ProColumns<any>[] = [
     {
@@ -56,8 +57,7 @@ const Access = () => {
     },
     {
       title: '值',
-      // dataIndex: '4',
-      render: (record: any) => <>{propertyValue[record.property]}</>,
+      render: (record: any) => <>{propertyValue[record?.property] || '-'}</>,
     },
     {
       title: '状态',
@@ -96,7 +96,9 @@ const Access = () => {
           style={{ padding: 0 }}
           popConfirm={{
             title: intl.formatMessage({
-              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}.tips`,
+              id: `pages.data.option.${
+                record.state.value !== 'disable' ? 'disable' : 'enable'
+              }.tips`,
               defaultMessage: '确认禁用?',
             }),
             onConfirm: async () => {
@@ -117,7 +119,7 @@ const Access = () => {
           isPermission={permission.action}
           tooltip={{
             title: intl.formatMessage({
-              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}`,
+              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'enable'}`,
               defaultMessage: record.state.value !== 'disable' ? '禁用' : '启用',
             }),
           }}
@@ -127,7 +129,7 @@ const Access = () => {
         <PermissionButton
           isPermission={permission.delete}
           style={{ padding: 0 }}
-          disabled={record.state.value === 'good'}
+          disabled={record.state.value === 'enable'}
           tooltip={{
             title:
               record.state.value === 'disable'
@@ -135,7 +137,7 @@ const Access = () => {
                     id: 'pages.data.option.remove',
                     defaultMessage: '删除',
                   })
-                : '请先禁用该组件,再删除。',
+                : '请先禁用该点位,再删除。',
           }}
           popConfirm={{
             title: '确认删除',
@@ -162,28 +164,8 @@ const Access = () => {
     },
   ];
 
-  const pointWs = () => {
-    if (productId && deviceId) {
-      const id = `instance-info-property-${deviceId}-${productId}-opc-point`;
-      const topic = `/dashboard/device/${productId}/properties/realTime`;
-      subscribeTopic!(id, topic, {
-        deviceId: deviceId,
-        properties: data.map((item: any) => item.property),
-        history: 0,
-      })
-        ?.pipe(map((res) => res.patload))
-        .subscribe((payload: any) => {
-          const { value } = payload;
-          console.log(value);
-          propertyValue[value.property] = { ...payload, ...value };
-          setPropertyValue({ ...propertyValue });
-        });
-    }
-  };
-
   const getBindList = (params: any) => {
-    service.getBindList(params).then((res) => {
-      console.log(res.result);
+    service.getBindList(params).then((res: any) => {
       if (res.status === 200) {
         setDeviceId(res.result[0]?.deviceId);
         setProductId(res.result[0]?.productId);
@@ -195,12 +177,25 @@ const Access = () => {
     });
   };
 
-  // useEffect(() => {
-  //   pointWs();
-  // }, [deviceId, productId])
-
   useEffect(() => {
-    pointWs();
+    if (productId && deviceId) {
+      const point = data.map((item: any) => item.property);
+      const id = `instance-info-property-${deviceId}-${productId}-${point.join('-')}`;
+      const topic = `/dashboard/device/${productId}/properties/realTime`;
+      wsRef.current = subscribeTopic?.(id, topic, {
+        deviceId: deviceId,
+        properties: data.map((item: any) => item.property),
+        history: 1,
+      })
+        ?.pipe(map((res) => res.payload))
+        .subscribe((payload: any) => {
+          const { value } = payload;
+          propertyValue[value.property] = value.formatValue;
+          setPropertyValue({ ...propertyValue });
+          // console.log(propertyValue)
+        });
+    }
+    return () => wsRef.current && wsRef.current?.unsubscribe();
   }, [data]);
 
   useEffect(() => {
@@ -221,128 +216,134 @@ const Access = () => {
   return (
     <PageContainer>
       <Card className={styles.list}>
-        <PermissionButton
-          onClick={() => {
-            setDeviceVisiable(true);
-          }}
-          isPermission={permission.add}
-          key="add"
-          icon={<PlusOutlined />}
-          type="dashed"
-          style={{ width: '200px', marginLeft: 20, marginBottom: 5 }}
-        >
-          绑定设备
-        </PermissionButton>
         {bindList.length > 0 ? (
-          <Tabs
-            tabPosition={'left'}
-            defaultActiveKey={deviceId}
-            onChange={(e) => {
-              setDeviceId(e);
-              const items = bindList.find((item: any) => item.deviceId === e);
-              setProductId(items[0]?.productId);
-              setParam({
-                terms: [{ column: 'deviceId', value: e }],
-              });
-            }}
-          >
-            {bindList.map((item: any) => (
-              <Tabs.TabPane
-                key={item.deviceId}
-                tab={
-                  <div className={styles.left}>
-                    <div style={{ width: '100px', textAlign: 'left' }}>{item.name}</div>
-                    <Popconfirm
-                      title="确认解绑该设备嘛?"
-                      onConfirm={() => {
-                        service.unbind([item.deviceId], opcUaId).then((res) => {
-                          if (res.status === 200) {
-                            message.success('解绑成功');
-                            getBindList(
-                              encodeQuery({
-                                terms: {
-                                  opcUaId: opcUaId,
-                                },
-                              }),
-                            );
-                          }
-                        });
-                      }}
-                      okText="Yes"
-                      cancelText="No"
-                    >
-                      <DisconnectOutlined className={styles.icon} />
-                    </Popconfirm>
-                  </div>
-                }
+          <div style={{ display: 'flex' }}>
+            <div>
+              <PermissionButton
+                onClick={() => {
+                  setDeviceVisiable(true);
+                }}
+                isPermission={permission.add}
+                key="add"
+                icon={<PlusOutlined />}
+                type="dashed"
+                style={{ width: '200px', margin: '16px 0 18px 20px' }}
               >
-                <ProTable
-                  actionRef={actionRef}
-                  params={param}
-                  columns={columns}
-                  rowKey="id"
-                  search={false}
-                  headerTitle={
-                    <>
-                      <PermissionButton
-                        onClick={() => {
-                          setPointVisiable(true);
-                          setCurrent({});
-                        }}
-                        isPermission={permission.add}
-                        key="add"
-                        icon={<PlusOutlined />}
-                        type="primary"
-                      >
-                        {intl.formatMessage({
-                          id: 'pages.data.option.add',
-                          defaultMessage: '新增',
-                        })}
-                      </PermissionButton>
-                      <div style={{ marginLeft: 10 }}>
-                        <Input.Search
-                          placeholder="请输入属性"
-                          allowClear
-                          onSearch={(value) => {
-                            console.log(value);
-                            if (value) {
-                              setParam({
-                                terms: [
-                                  { column: 'deviceId', value: deviceId },
-                                  { column: 'property', value: `%${value}%`, termType: 'like' },
-                                ],
-                              });
-                            } else {
-                              setParam({
-                                terms: [{ column: 'deviceId', value: deviceId }],
-                              });
-                            }
+                绑定设备
+              </PermissionButton>
+              <Tabs
+                tabPosition={'left'}
+                defaultActiveKey={deviceId}
+                onChange={(e) => {
+                  console.log(e);
+                  setDeviceId(e);
+                  const items = bindList.find((item: any) => item.deviceId === e);
+                  setProductId(items?.productId);
+                  setParam({
+                    terms: [{ column: 'deviceId', value: e }],
+                  });
+                }}
+              >
+                {bindList.map((item: any) => (
+                  <Tabs.TabPane
+                    key={item.deviceId}
+                    tab={
+                      <div className={styles.left}>
+                        <div className={styles.text}>{item.name}</div>
+                        <Popconfirm
+                          title="确认解绑该设备嘛?"
+                          onConfirm={() => {
+                            service.unbind([item.deviceId], opcUaId).then((res: any) => {
+                              if (res.status === 200) {
+                                message.success('解绑成功');
+                                getBindList(
+                                  encodeQuery({
+                                    terms: {
+                                      opcUaId: opcUaId,
+                                    },
+                                  }),
+                                );
+                              }
+                            });
                           }}
-                        />
+                          okText="Yes"
+                          cancelText="No"
+                        >
+                          <DisconnectOutlined className={styles.icon} />
+                        </Popconfirm>
                       </div>
-                    </>
-                  }
-                  request={async (params) => {
-                    const res = await service.PointList({
-                      ...params,
-                      sorts: [{ name: 'createTime', order: 'desc' }],
-                    });
-                    setData(res.result.data);
-                    return {
-                      code: res.message,
-                      result: {
-                        data: res.result.data,
-                        pageIndex: 0,
-                        pageSize: 0,
-                        total: 0,
-                      },
-                      status: res.status,
-                    };
-                  }}
-                />
-              </Tabs.TabPane>
-            ))}
-          </Tabs>
+                    }
+                  ></Tabs.TabPane>
+                ))}
+              </Tabs>
+            </div>
+            <div style={{ width: '100%' }}>
+              <ProTable
+                actionRef={actionRef}
+                params={param}
+                columns={columns}
+                rowKey="id"
+                search={false}
+                headerTitle={
+                  <>
+                    <PermissionButton
+                      onClick={() => {
+                        setPointVisiable(true);
+                        setCurrent({});
+                      }}
+                      isPermission={permission.add}
+                      key="add"
+                      icon={<PlusOutlined />}
+                      type="primary"
+                    >
+                      {intl.formatMessage({
+                        id: 'pages.data.option.add',
+                        defaultMessage: '新增',
+                      })}
+                    </PermissionButton>
+                    <div style={{ marginLeft: 10 }}>
+                      <Input.Search
+                        placeholder="请输入属性"
+                        allowClear
+                        onSearch={(value) => {
+                          console.log(value);
+                          if (value) {
+                            setParam({
+                              terms: [
+                                { column: 'deviceId', value: deviceId },
+                                { column: 'property', value: `%${value}%`, termType: 'like' },
+                              ],
+                            });
+                          } else {
+                            setParam({
+                              terms: [{ column: 'deviceId', value: deviceId }],
+                            });
+                          }
+                        }}
+                      />
+                    </div>
+                  </>
+                }
+                request={async (params) => {
+                  const res = await service.PointList({
+                    ...params,
+                    sorts: [{ name: 'createTime', order: 'desc' }],
+                  });
+                  setData(res.result.data);
+                  return {
+                    code: res.message,
+                    result: {
+                      data: res.result.data,
+                      pageIndex: 0,
+                      pageSize: 0,
+                      total: 0,
+                    },
+                    status: res.status,
+                  };
+                }}
+              />
+            </div>
+          </div>
         ) : (
           <Empty />
         )}