Browse Source

feat(merge): merge sc

fix: 4.28修改bug
Lind 4 năm trước cách đây
mục cha
commit
c772cce909

+ 3 - 1
src/components/ProTableCard/CardItems/protocol.tsx

@@ -32,7 +32,9 @@ export default (props: ProcotolCardProps) => {
         </div>
         </div>
         <div className={'card-item-body'}>
         <div className={'card-item-body'}>
           <div className={'card-item-header'}>
           <div className={'card-item-header'}>
-            <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+            <Tooltip title={props.name}>
+              <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+            </Tooltip>
           </div>
           </div>
           <Row gutter={24}>
           <Row gutter={24}>
             <Col span={12}>
             <Col span={12}>

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

@@ -143,7 +143,7 @@ const Config = () => {
           )}
           )}
         </Space>
         </Space>
       </div>
       </div>
-      <div style={{ paddingLeft: 10 }}>
+      <div>
         {(metadata || []).map((i) => (
         {(metadata || []).map((i) => (
           <Descriptions size="small" column={3} key={i.name} bordered title={<h4>{i.name}</h4>}>
           <Descriptions size="small" column={3} key={i.name} bordered title={<h4>{i.name}</h4>}>
             {(i?.properties || []).map((item: any) => (
             {(i?.properties || []).map((item: any) => (

+ 23 - 5
src/pages/device/Instance/Detail/Diagnose/Message/index.tsx

@@ -31,6 +31,7 @@ const DatePicker1: any = DatePicker;
 const Message = (props: Props) => {
 const Message = (props: Props) => {
   const [subscribeTopic] = useSendWebsocketMessage();
   const [subscribeTopic] = useSendWebsocketMessage();
   const [dialogList, setDialogList] = useState<any[]>([]);
   const [dialogList, setDialogList] = useState<any[]>([]);
+  const [tempList, setTempList] = useState<any[]>([]);
   const [logList, setLogList] = useState<any[]>([]);
   const [logList, setLogList] = useState<any[]>([]);
   const [type, setType] = useState<'property' | 'function'>('function');
   const [type, setType] = useState<'property' | 'function'>('function');
   const [input, setInput] = useState<any>({});
   const [input, setInput] = useState<any>({});
@@ -48,11 +49,6 @@ const Message = (props: Props) => {
     subscribeTopic!(id, topic, {})
     subscribeTopic!(id, topic, {})
       ?.pipe(map((res) => res.payload))
       ?.pipe(map((res) => res.payload))
       .subscribe((payload: any) => {
       .subscribe((payload: any) => {
-        if (payload.error) {
-          props.onChange(!payload.upstream ? 'down-error' : 'up-error');
-        } else {
-          props.onChange(!payload.upstream ? 'down-success' : 'up-success');
-        }
         if (payload.type === 'log') {
         if (payload.type === 'log') {
           logList.push({
           logList.push({
             key: randomString(),
             key: randomString(),
@@ -60,6 +56,26 @@ const Message = (props: Props) => {
           });
           });
           setLogList([...logList]);
           setLogList([...logList]);
         } else {
         } else {
+          tempList.push({
+            key: randomString(),
+            ...payload,
+          });
+          const flag = [...tempList]
+            .filter(
+              (i) =>
+                i.traceId === payload.traceId &&
+                (payload.downstream === i.downstream || payload.upstream === i.upstream),
+            )
+            .every((item) => {
+              return !item.error;
+            });
+          if (!flag) {
+            props.onChange(!payload.upstream ? 'down-error' : 'up-error');
+          } else {
+            props.onChange(!payload.upstream ? 'down-success' : 'up-success');
+          }
+          setTempList([...tempList]);
+          Store.set('temp', tempList);
           const t = dialogList.find(
           const t = dialogList.find(
             (item) =>
             (item) =>
               item.traceId === payload.traceId &&
               item.traceId === payload.traceId &&
@@ -153,6 +169,8 @@ const Message = (props: Props) => {
     subscribeLog();
     subscribeLog();
     const arr = Store.get('diagnose') || [];
     const arr = Store.get('diagnose') || [];
     setDialogList(arr);
     setDialogList(arr);
+    const temp = Store.get('temp') || [];
+    setTempList(temp);
   }, []);
   }, []);
 
 
   const form = createForm({
   const form = createForm({

+ 15 - 10
src/pages/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx

@@ -23,8 +23,7 @@ const DiagnosticAdvice = (props: Props) => {
     const tab: any = window.open(`${origin}/#${url}?key=access`);
     const tab: any = window.open(`${origin}/#${url}?key=access`);
     tab!.onTabSaveSuccess = (value: any) => {
     tab!.onTabSaveSuccess = (value: any) => {
       if (value) {
       if (value) {
-        // diagnoseConfig();
-        // 没有权限怎么展示
+        props.close();
       }
       }
     };
     };
   };
   };
@@ -35,6 +34,9 @@ const DiagnosticAdvice = (props: Props) => {
       onCancel={() => {
       onCancel={() => {
         props.close();
         props.close();
       }}
       }}
+      onOk={() => {
+        props.close();
+      }}
       width={700}
       width={700}
       visible
       visible
     >
     >
@@ -49,7 +51,7 @@ const DiagnosticAdvice = (props: Props) => {
               status="default"
               status="default"
               text={
               text={
                 <span>
                 <span>
-                  产品-${item.name}规则可能有加密处理,请认真查看
+                  产品-{item.name}规则可能有加密处理,请认真查看
                   <a
                   <a
                     onClick={() => {
                     onClick={() => {
                       jumpUrl();
                       jumpUrl();
@@ -69,7 +71,7 @@ const DiagnosticAdvice = (props: Props) => {
               status="default"
               status="default"
               text={
               text={
                 <span>
                 <span>
-                  设备-${item.name}规则可能有加密处理,请认真查看
+                  设备-{item.name}规则可能有加密处理,请认真查看
                   <a
                   <a
                     onClick={() => {
                     onClick={() => {
                       jumpUrl();
                       jumpUrl();
@@ -83,7 +85,7 @@ const DiagnosticAdvice = (props: Props) => {
             />
             />
           </div>
           </div>
         ))}
         ))}
-        {!!data.provider && (
+        {!!data?.provider && (
           <div>
           <div>
             {data.routes.length > 0 ? (
             {data.routes.length > 0 ? (
               <div className={styles.infoItem}>
               <div className={styles.infoItem}>
@@ -99,9 +101,9 @@ const DiagnosticAdvice = (props: Props) => {
                       >
                       >
                         设备接入配置
                         设备接入配置
                       </a>
                       </a>
-                      中${nameMap.get(data.provider)}
-                      信息,任意上报一条数据(无设备接入配置查看权限时:请联系管理员根据设备接入配置中$
-                      {URL}信息,任意上报一条数据)。 变量说明:${nameMap.get(data.provider)}
+                      中{nameMap.get(data.provider)}
+                      信息,任意上报一条数据(无设备接入配置查看权限时:请联系管理员根据设备接入配置中
+                      {URL}信息,任意上报一条数据)。 变量说明:{nameMap.get(data.provider)}
                       变量根据网关详情中provider类型判断。
                       变量根据网关详情中provider类型判断。
                     </span>
                     </span>
                   }
                   }
@@ -113,8 +115,8 @@ const DiagnosticAdvice = (props: Props) => {
                   status="default"
                   status="default"
                   text={
                   text={
                     <span>
                     <span>
-                      请联系管理员提供${nameMap.get(data.provider)}
-                      信息,并根据URL信息任意上报一条数据 变量说明:${nameMap.get(data.provider)}
+                      请联系管理员提供{nameMap.get(data.provider)}
+                      信息,并根据URL信息任意上报一条数据 变量说明:{nameMap.get(data.provider)}
                       变量根据网关详情中provider类型判断。
                       变量根据网关详情中provider类型判断。
                     </span>
                     </span>
                   }
                   }
@@ -123,6 +125,9 @@ const DiagnosticAdvice = (props: Props) => {
             )}
             )}
           </div>
           </div>
         )}
         )}
+        <div className={styles.infoItem}>
+          <Badge status="default" text={'请检查设备是否已开机'} />
+        </div>
       </div>
       </div>
     </Modal>
     </Modal>
   );
   );

+ 1 - 2
src/pages/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx

@@ -86,8 +86,7 @@ const ManualInspection = (props: Props) => {
     <div style={{ backgroundColor: '#f6f6f6', padding: 10 }}>
     <div style={{ backgroundColor: '#f6f6f6', padding: 10 }}>
       {(data?.data?.properties || []).map((item: any) => (
       {(data?.data?.properties || []).map((item: any) => (
         <div key={item.property}>
         <div key={item.property}>
-          <span>{item.name}</span>:{' '}
-          <span>{item.type.type !== 'password' ? data?.check[item.property] : '******'}</span>
+          <span>{item.name}</span>: <span>{data?.check[item.property]}</span>
         </div>
         </div>
       ))}
       ))}
     </div>
     </div>

+ 60 - 52
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -80,6 +80,11 @@ const Status = observer((props: Props) => {
   const [diagnoseData, setDiagnoseData] = useState<any>({});
   const [diagnoseData, setDiagnoseData] = useState<any>({});
   const [artificiaData, setArtificiaData] = useState<any>({});
   const [artificiaData, setArtificiaData] = useState<any>({});
 
 
+  const [productTemp, setProductTemp] = useState<any[]>([]);
+  const [deviceTemp, setDeviceTemp] = useState<any[]>([]);
+  const [gatewayTemp, setGatewayTemp] = useState<any>({});
+  const [productItem, setProductItem] = useState<any>({});
+
   const getDetail = (id: string) => {
   const getDetail = (id: string) => {
     service.detail(id).then((response) => {
     service.detail(id).then((response) => {
       InstanceModel.detail = response?.result;
       InstanceModel.detail = response?.result;
@@ -91,6 +96,7 @@ const Status = observer((props: Props) => {
       let data: any = {};
       let data: any = {};
       if (InstanceModel.detail.state?.value === 'online' || !!InstanceModel.detail?.protocol) {
       if (InstanceModel.detail.state?.value === 'online' || !!InstanceModel.detail?.protocol) {
         data = { status: 'success', text: '正常', info: null };
         data = { status: 'success', text: '正常', info: null };
+        DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
       } else {
       } else {
         data = {
         data = {
           status: 'warning',
           status: 'warning',
@@ -142,6 +148,7 @@ const Status = observer((props: Props) => {
       if (InstanceModel.detail.state?.value === 'online') {
       if (InstanceModel.detail.state?.value === 'online') {
         data = { status: 'success', text: '正常', info: null };
         data = { status: 'success', text: '正常', info: null };
         DiagnoseStatusModel.status.network = data;
         DiagnoseStatusModel.status.network = data;
+        DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
         setTimeout(
         setTimeout(
           () =>
           () =>
             resolve({
             resolve({
@@ -154,6 +161,7 @@ const Status = observer((props: Props) => {
       } else {
       } else {
         service.queryProductState(InstanceModel.detail?.productId || '').then((resp) => {
         service.queryProductState(InstanceModel.detail?.productId || '').then((resp) => {
           if (resp.status === 200) {
           if (resp.status === 200) {
+            setProductItem(resp.result);
             if (resp.result.accessId) {
             if (resp.result.accessId) {
               service.queryGatewayState(resp.result.accessId).then((response: any) => {
               service.queryGatewayState(resp.result.accessId).then((response: any) => {
                 if (response.status === 200) {
                 if (response.status === 200) {
@@ -242,6 +250,7 @@ const Status = observer((props: Props) => {
                     };
                     };
                   }
                   }
                   DiagnoseStatusModel.status.network = data;
                   DiagnoseStatusModel.status.network = data;
+                  DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
                   setTimeout(
                   setTimeout(
                     () =>
                     () =>
                       resolve({
                       resolve({
@@ -330,6 +339,7 @@ const Status = observer((props: Props) => {
                                 text: '正常',
                                 text: '正常',
                                 info: null,
                                 info: null,
                               };
                               };
+                              DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
                             }
                             }
                           }}
                           }}
                         >
                         >
@@ -384,6 +394,7 @@ const Status = observer((props: Props) => {
                               info: null,
                               info: null,
                             };
                             };
                             getDetail(InstanceModel.detail?.id || '');
                             getDetail(InstanceModel.detail?.id || '');
+                            DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
                           }
                           }
                         }}
                         }}
                       >
                       >
@@ -414,6 +425,7 @@ const Status = observer((props: Props) => {
       } else {
       } else {
         service.queryProductConfig(proItem.id).then((resp) => {
         service.queryProductConfig(proItem.id).then((resp) => {
           if (resp.status === 200) {
           if (resp.status === 200) {
+            setProductTemp(resp?.result);
             if (resp.result.length > 0) {
             if (resp.result.length > 0) {
               resp.result.map((item: any, index: number) => {
               resp.result.map((item: any, index: number) => {
                 let data: any = {};
                 let data: any = {};
@@ -475,6 +487,7 @@ const Status = observer((props: Props) => {
       } else {
       } else {
         service.queryDeviceConfig(InstanceModel.detail?.id || '').then((resp) => {
         service.queryDeviceConfig(InstanceModel.detail?.id || '').then((resp) => {
           if (resp.status === 200) {
           if (resp.status === 200) {
+            setDeviceTemp(resp.result);
             if (resp.result.length > 0) {
             if (resp.result.length > 0) {
               resp.result.map((item: any, index: number) => {
               resp.result.map((item: any, index: number) => {
                 let data: any = {};
                 let data: any = {};
@@ -557,6 +570,7 @@ const Status = observer((props: Props) => {
                               text: '正常',
                               text: '正常',
                               info: null,
                               info: null,
                             };
                             };
+                            DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
                           }
                           }
                         }}
                         }}
                       >
                       >
@@ -607,68 +621,26 @@ const Status = observer((props: Props) => {
 
 
     let product: any = null;
     let product: any = null;
     let gateway: any = null;
     let gateway: any = null;
-    let productauth: any = null;
-    let deviceauth: any = null;
     diagnoseConfig()
     diagnoseConfig()
       .then(() => diagnoseNetwork())
       .then(() => diagnoseNetwork())
       .then((resp: any) => {
       .then((resp: any) => {
         product = resp?.product;
         product = resp?.product;
         gateway = resp?.gatewayDetail;
         gateway = resp?.gatewayDetail;
+        setGatewayTemp(resp?.gatewayDetail);
         diagnoseProduct(product)
         diagnoseProduct(product)
           .then(() => diagnoseDevice())
           .then(() => diagnoseDevice())
           .then(() => diagnoseProductAuthConfig(product))
           .then(() => diagnoseProductAuthConfig(product))
-          .then((res) => {
-            productauth = res;
-            diagnoseDeviceAuthConfig().then((dt) => {
-              deviceauth = dt;
+          .then(() => {
+            diagnoseDeviceAuthConfig().then(() => {
               diagnoseDeviceAccess(gateway).then(() => {
               diagnoseDeviceAccess(gateway).then(() => {
-                if (InstanceModel.detail.state?.value === 'online') {
-                  const a = Object.keys(DiagnoseStatusModel.status).find((item: any) => {
-                    return item.status !== 'success';
-                  });
-                  if (!!a) {
-                    Store.set('diagnose-status', {
-                      list: DiagnoseStatusModel.list,
-                      status: DiagnoseStatusModel.status,
-                    });
-                    props.onChange('success');
-                  } else {
-                    props.onChange('error');
-                  }
+                if (InstanceModel.detail.state?.value !== 'online') {
+                  props.onChange('error');
                 } else {
                 } else {
-                  const data = { ...DiagnoseStatusModel.status };
-                  const flag = Object.keys(data).find((item: any) => {
-                    return item.status !== 'success';
+                  Store.set('diagnose-status', {
+                    list: DiagnoseStatusModel.list,
+                    status: DiagnoseStatusModel.status,
                   });
                   });
-                  if (!flag) {
-                    // 展示诊断建议
-                    if (
-                      gateway.provider !== 'mqtt-server-gateway' &&
-                      gatewayList.includes(gateway.provider)
-                    ) {
-                      service
-                        .queryProcotolDetail(gateway.provider, gateway.transport)
-                        .then((resp1) => {
-                          setDiagnoseData({
-                            product: productauth,
-                            device: deviceauth,
-                            id: product.id,
-                            provider: gateway.provider,
-                            routes: resp1.result?.routes || [],
-                          });
-                          setDiagnoseVisible(true);
-                        });
-                    } else {
-                      setDiagnoseData({
-                        product: productauth,
-                        device: deviceauth,
-                        id: product.id,
-                        provider: '',
-                        routes: [],
-                      });
-                      setDiagnoseVisible(true);
-                    }
-                  }
+                  props.onChange('success');
                 }
                 }
               });
               });
             });
             });
@@ -689,6 +661,40 @@ const Status = observer((props: Props) => {
     }
     }
   }, [devicePermission]);
   }, [devicePermission]);
 
 
+  useEffect(() => {
+    const data = { ...DiagnoseStatusModel.status };
+    const flag = Object.keys(data).every((item: any) => {
+      return data[item]?.status === 'success';
+    });
+    if (flag && InstanceModel.detail.state?.value !== 'online') {
+      // 展示诊断建议
+      if (
+        gatewayTemp.provider !== 'mqtt-server-gateway' &&
+        gatewayList.includes(gatewayTemp.provider)
+      ) {
+        service.queryProcotolDetail(gatewayTemp.provider, gatewayTemp.transport).then((resp1) => {
+          setDiagnoseData({
+            product: productTemp,
+            device: deviceTemp,
+            id: productItem.id,
+            provider: gatewayTemp.provider,
+            routes: resp1.result?.routes || [],
+          });
+          setDiagnoseVisible(true);
+        });
+      } else {
+        setDiagnoseData({
+          product: productTemp,
+          device: deviceTemp,
+          id: productItem.i,
+          provider: gatewayTemp?.provider,
+          routes: [],
+        });
+        setDiagnoseVisible(true);
+      }
+    }
+  }, [DiagnoseStatusModel.status]);
+
   return (
   return (
     <Row gutter={24}>
     <Row gutter={24}>
       <Col span={16}>
       <Col span={16}>
@@ -762,6 +768,7 @@ const Status = observer((props: Props) => {
                 text: '正常',
                 text: '正常',
                 info: null,
                 info: null,
               };
               };
+              DiagnoseStatusModel.status = { ...DiagnoseStatusModel.status };
             } else {
             } else {
               DiagnoseStatusModel.status[params.data.key] = {
               DiagnoseStatusModel.status[params.data.key] = {
                 status: 'error',
                 status: 'error',
@@ -783,7 +790,8 @@ const Status = observer((props: Props) => {
                               const tab: any = window.open(`${origin}/#${url}?key=access`);
                               const tab: any = window.open(`${origin}/#${url}?key=access`);
                               tab!.onTabSaveSuccess = (value: any) => {
                               tab!.onTabSaveSuccess = (value: any) => {
                                 if (value) {
                                 if (value) {
-                                  diagnoseConfig();
+                                  getDetail(InstanceModel.detail?.id || '');
+                                  handleSearch();
                                 }
                                 }
                               };
                               };
                             }}
                             }}

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

@@ -1,4 +1,4 @@
-import { Badge, Card, Col, Row } from 'antd';
+import { Badge, Card, Col, Row, Tooltip } from 'antd';
 import type { ReactNode } from 'react';
 import type { ReactNode } from 'react';
 import { useEffect, useState } from 'react';
 import { useEffect, useState } from 'react';
 import Message from './Message';
 import Message from './Message';
@@ -87,9 +87,12 @@ const Diagnose = () => {
             style={{ fontWeight: 400 }}
             style={{ fontWeight: 400 }}
           >
           >
             {message === 'disabled' ? (
             {message === 'disabled' ? (
-              <span style={{ color: statusColor.get(message) }}>
-                <Badge color={statusColor.get(message)} /> 连接中
-              </span>
+              <Tooltip title={'设备未上线时消息通信功不能使用'}>
+                <span style={{ color: statusColor.get(message) }}>
+                  <Badge color={statusColor.get(message)} />
+                  {status === 's-error' || status === 'waiting' ? '等待设备连接' : '连接中'}
+                </span>
+              </Tooltip>
             ) : (
             ) : (
               <>
               <>
                 <div>
                 <div>

+ 1 - 1
src/pages/device/Instance/Detail/Reation/Edit.tsx

@@ -63,7 +63,7 @@ const Edit = (props: Props) => {
         'x-decorator': 'FormItem',
         'x-decorator': 'FormItem',
         'x-component': 'Select',
         'x-component': 'Select',
         'x-component-props': {
         'x-component-props': {
-          placeholder: '请选择关联方',
+          placeholder: `请选择${item.relationName}`,
           showSearch: true,
           showSearch: true,
           showArrow: true,
           showArrow: true,
           mode: 'multiple',
           mode: 'multiple',

+ 53 - 26
src/pages/device/Instance/Detail/Running/Property/Indicators.tsx

@@ -10,13 +10,11 @@ import {
   NumberPicker,
   NumberPicker,
   Select,
   Select,
 } from '@formily/antd';
 } from '@formily/antd';
-import { createForm } from '@formily/core';
+import { createForm, onFieldReact } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { createSchemaField } from '@formily/react';
-
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import { useEffect, useState } from 'react';
 import { useEffect, useState } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { InstanceModel, service } from '@/pages/device/Instance';
-
 interface Props {
 interface Props {
   data: Partial<PropertyMetadata>;
   data: Partial<PropertyMetadata>;
   onCancel: () => void;
   onCancel: () => void;
@@ -42,6 +40,31 @@ const Indicators = (props: Props) => {
     initialValues: {
     initialValues: {
       metrics: metrics,
       metrics: metrics,
     },
     },
+    effects: () => {
+      onFieldReact('metrics.*.*', (field, form1) => {
+        const type = data.valueType?.type;
+        form1.setFieldState('metrics.*.space.value.*', (state) => {
+          state.componentType = componentMap[type || ''] || 'Input';
+          if (type === 'date') {
+            state.componentProps = {
+              showTime: true,
+            };
+          } else if (type === 'boolean') {
+            state.componentType = 'Select';
+            state.dataSource = [
+              {
+                label: data.valueType?.trueText,
+                value: String(data.valueType?.trueValue),
+              },
+              {
+                label: data.valueType?.falseText,
+                value: String(data.valueType?.falseValue),
+              },
+            ];
+          }
+        });
+      });
+    },
   });
   });
 
 
   const SchemaField = createSchemaField({
   const SchemaField = createSchemaField({
@@ -106,19 +129,6 @@ const Indicators = (props: Props) => {
                       dependencies: ['..range', data.valueType?.type],
                       dependencies: ['..range', data.valueType?.type],
                       fulfill: {
                       fulfill: {
                         state: {
                         state: {
-                          dataSource:
-                            data.valueType?.type === 'boolean'
-                              ? [
-                                  {
-                                    label: data.valueType?.trueText,
-                                    value: data.valueType?.trueValue,
-                                  },
-                                  {
-                                    label: data.valueType?.falseText,
-                                    value: data.valueType?.falseValue,
-                                  },
-                                ]
-                              : [],
                           decoratorProps: {
                           decoratorProps: {
                             gridSpan: '{{!!$deps[0]?5:$deps[1]==="boolean"?12:10}}',
                             gridSpan: '{{!!$deps[0]?5:$deps[1]==="boolean"?12:10}}',
                           },
                           },
@@ -163,17 +173,32 @@ const Indicators = (props: Props) => {
     },
     },
   };
   };
 
 
-  console.log(data);
-  console.log(InstanceModel.detail);
-
   useEffect(() => {
   useEffect(() => {
     if (InstanceModel.detail.id && data.id) {
     if (InstanceModel.detail.id && data.id) {
       service.queryMetric(InstanceModel.detail.id || '', data.id || '').then((resp) => {
       service.queryMetric(InstanceModel.detail.id || '', data.id || '').then((resp) => {
         if (resp.status === 200) {
         if (resp.status === 200) {
           if ((resp?.result || []).length > 0) {
           if ((resp?.result || []).length > 0) {
-            setMetrics(resp.result);
+            const list = resp.result.map((item: any) => {
+              return {
+                ...item,
+                value: item.value.split(','),
+              };
+            });
+            setMetrics(list);
           } else {
           } else {
-            setMetrics(data.expands?.metrics || []);
+            const type = data.valueType?.type;
+            if (type === 'boolean') {
+              const list = data.expands?.metrics.map((item: any) => {
+                const value = (item?.value || {}).map((i: any) => String(i)) || {};
+                return {
+                  ...item,
+                  value,
+                };
+              });
+              setMetrics(list || []);
+            } else {
+              setMetrics(data.expands?.metrics || []);
+            }
           }
           }
         }
         }
       });
       });
@@ -188,11 +213,13 @@ const Indicators = (props: Props) => {
       width={600}
       width={600}
       onOk={async () => {
       onOk={async () => {
         const params = (await form.submit()) as any;
         const params = (await form.submit()) as any;
-        const resp = await service.saveMetric(
-          InstanceModel.detail.id || '',
-          data.id || '',
-          params.metrics,
-        );
+        const list = (params?.metrics || []).map((item: any) => {
+          return {
+            ...item,
+            value: item.value.join(','),
+          };
+        });
+        const resp = await service.saveMetric(InstanceModel.detail.id || '', data.id || '', list);
         if (resp.status === 200) {
         if (resp.status === 200) {
           message.success('操作成功!');
           message.success('操作成功!');
           props.onCancel();
           props.onCancel();

+ 54 - 30
src/pages/device/Instance/Detail/index.tsx

@@ -263,36 +263,60 @@ const InstanceDetail = observer(() => {
           <Divider type="vertical" />
           <Divider type="vertical" />
           <Space>
           <Space>
             {deviceStatus.get(InstanceModel.detail?.state?.value)}
             {deviceStatus.get(InstanceModel.detail?.state?.value)}
-            <PermissionButton
-              type={'link'}
-              key={'state'}
-              popConfirm={{
-                title:
-                  InstanceModel.detail?.state?.value !== 'notActive'
-                    ? '确认断开连接'
-                    : '确认启用设备',
-                onConfirm: async () => {
-                  if (InstanceModel.detail?.state?.value !== 'notActive') {
-                    await service.undeployDevice(params.id);
-                  } else {
-                    await service.deployDevice(params.id);
-                  }
-                  message.success(
-                    intl.formatMessage({
-                      id: 'pages.data.option.success',
-                      defaultMessage: '操作成功!',
-                    }),
-                  );
-                  getDetail(params.id);
-                },
-              }}
-              isPermission={permission.action}
-              tooltip={{
-                title: InstanceModel.detail?.state?.value !== 'notActive' ? '断开连接' : '启用设备',
-              }}
-            >
-              {InstanceModel.detail?.state?.value !== 'notActive' ? '断开连接' : '启用设备'}
-            </PermissionButton>
+            {InstanceModel.detail?.state?.value === 'notActive' && (
+              <PermissionButton
+                type={'link'}
+                key={'state'}
+                popConfirm={{
+                  title: '确认启用设备',
+                  onConfirm: async () => {
+                    const resp = await service.deployDevice(params.id);
+                    if (resp.status === 200) {
+                      message.success(
+                        intl.formatMessage({
+                          id: 'pages.data.option.success',
+                          defaultMessage: '操作成功!',
+                        }),
+                      );
+                      getDetail(params.id);
+                    }
+                  },
+                }}
+                isPermission={permission.action}
+                tooltip={{
+                  title: '启用设备',
+                }}
+              >
+                启用设备
+              </PermissionButton>
+            )}
+            {InstanceModel.detail?.state?.value === 'online' && (
+              <PermissionButton
+                type={'link'}
+                key={'state'}
+                popConfirm={{
+                  title: '确认断开连接',
+                  onConfirm: async () => {
+                    const resp = await service.disconnectDevice(params.id);
+                    if (resp.status === 200) {
+                      message.success(
+                        intl.formatMessage({
+                          id: 'pages.data.option.success',
+                          defaultMessage: '操作成功!',
+                        }),
+                      );
+                      getDetail(params.id);
+                    }
+                  },
+                }}
+                isPermission={permission.action}
+                tooltip={{
+                  title: '断开连接',
+                }}
+              >
+                断开连接
+              </PermissionButton>
+            )}
           </Space>
           </Space>
         </>
         </>
       }
       }

+ 2 - 1
src/pages/device/Instance/Import/index.tsx

@@ -12,6 +12,7 @@ import SystemConst from '@/utils/const';
 import Token from '@/utils/token';
 import Token from '@/utils/token';
 import { EventSourcePolyfill } from 'event-source-polyfill';
 import { EventSourcePolyfill } from 'event-source-polyfill';
 import { downloadFile } from '@/utils/util';
 import { downloadFile } from '@/utils/util';
+import encodeQuery from '@/utils/encodeQuery';
 
 
 interface Props {
 interface Props {
   visible: boolean;
   visible: boolean;
@@ -176,7 +177,7 @@ const Import = (props: Props) => {
   });
   });
 
 
   useEffect(() => {
   useEffect(() => {
-    service.getProductList({ paging: false }).then((resp) => {
+    service.getProductList(encodeQuery({ paging: false, terms: { state: 1 } })).then((resp) => {
       if (resp.status === 200) {
       if (resp.status === 200) {
         const list = resp.result.map((item: { name: any; id: any }) => ({
         const list = resp.result.map((item: { name: any; id: any }) => ({
           label: item.name,
           label: item.name,

+ 19 - 9
src/pages/device/Instance/Save/index.tsx

@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { UploadImage } from '@/components';
 import { UploadImage } from '@/components';
 import { debounce } from 'lodash';
 import { debounce } from 'lodash';
+import encodeQuery from '@/utils/encodeQuery';
 
 
 interface Props {
 interface Props {
   visible: boolean;
   visible: boolean;
@@ -31,15 +32,24 @@ const Save = (props: Props) => {
   const intl = useIntl();
   const intl = useIntl();
 
 
   useEffect(() => {
   useEffect(() => {
-    service.getProductList({ paging: false }).then((resp: any) => {
-      if (resp.status === 200) {
-        const list = resp.result.map((item: { name: any; id: any }) => ({
-          label: item.name,
-          value: item.id,
-        }));
-        setProductList(list);
-      }
-    });
+    service
+      .getProductList(
+        encodeQuery({
+          paging: false,
+          terms: {
+            state: 1,
+          },
+        }),
+      )
+      .then((resp: any) => {
+        if (resp.status === 200) {
+          const list = resp.result.map((item: { name: any; id: any }) => ({
+            label: item.name,
+            value: item.id,
+          }));
+          setProductList(list);
+        }
+      });
   }, []);
   }, []);
 
 
   const intlFormat = (
   const intlFormat = (

+ 10 - 3
src/pages/device/Instance/service.ts

@@ -33,6 +33,13 @@ class Service extends BaseService<DeviceInstance> {
       data: params,
       data: params,
     });
     });
 
 
+  // 断开连接
+  public disconnectDevice = (deviceId: string, params?: any) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/disconnect`, {
+      method: 'POST',
+      data: params,
+    });
+
   // 批量激活设备
   // 批量激活设备
   public batchDeployDevice = (params: any) =>
   public batchDeployDevice = (params: any) =>
     request(`/${SystemConst.API_BASE}/device-instance/batch/_deploy`, {
     request(`/${SystemConst.API_BASE}/device-instance/batch/_deploy`, {
@@ -206,14 +213,14 @@ class Service extends BaseService<DeviceInstance> {
     });
     });
   // 读取属性
   // 读取属性
   public readProperties = (deviceId: string, data: any) =>
   public readProperties = (deviceId: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/properties/_read`, {
+    request(`/${SystemConst.API_BASE}/device/instance/${deviceId}/properties/_read`, {
       method: 'POST',
       method: 'POST',
       data,
       data,
     });
     });
   // 设置属性
   // 设置属性
   public settingProperties = (deviceId: string, data: any) =>
   public settingProperties = (deviceId: string, data: any) =>
-    request(`/${SystemConst.API_BASE}//device-instance/${deviceId}/property`, {
-      method: 'POST',
+    request(`/${SystemConst.API_BASE}/device/instance/${deviceId}/property`, {
+      method: 'PUT',
       data,
       data,
     });
     });
   //获取协议设置的默认物模型
   //获取协议设置的默认物模型

+ 19 - 12
src/pages/device/Product/Detail/Access/index.tsx

@@ -338,7 +338,6 @@ const Access = () => {
                   请先
                   请先
                   <Button
                   <Button
                     type="link"
                     type="link"
-                    disabled={!!(productModel.current?.count && productModel.current?.count > 0)}
                     onClick={() => {
                     onClick={() => {
                       setConfigVisible(true);
                       setConfigVisible(true);
                     }}
                     }}
@@ -362,20 +361,28 @@ const Access = () => {
                   data={
                   data={
                     <span>
                     <span>
                       接入方式
                       接入方式
-                      <Button
-                        size="small"
-                        type="primary"
-                        ghost
-                        style={{ marginLeft: 20 }}
-                        disabled={
+                      <Tooltip
+                        title={
                           !!(productModel.current?.count && productModel.current?.count > 0)
                           !!(productModel.current?.count && productModel.current?.count > 0)
+                            ? '产品下有设备实例时不能更换接入方式'
+                            : ''
                         }
                         }
-                        onClick={() => {
-                          setConfigVisible(true);
-                        }}
                       >
                       >
-                        更换
-                      </Button>
+                        <Button
+                          size="small"
+                          type="primary"
+                          ghost
+                          style={{ marginLeft: 20 }}
+                          disabled={
+                            !!(productModel.current?.count && productModel.current?.count > 0)
+                          }
+                          onClick={() => {
+                            setConfigVisible(true);
+                          }}
+                        >
+                          更换
+                        </Button>
+                      </Tooltip>
                     </span>
                     </span>
                   }
                   }
                 />
                 />

+ 3 - 1
src/pages/link/AccessConfig/Detail/Provider/index.less

@@ -1,9 +1,11 @@
 .images {
 .images {
+  display: flex;
+  align-items: center;
+  justify-content: center;
   width: 64px;
   width: 64px;
   height: 64px;
   height: 64px;
   color: white;
   color: white;
   font-size: 12px;
   font-size: 12px;
-  line-height: 64px;
   text-align: center;
   text-align: center;
   background: linear-gradient(
   background: linear-gradient(
     128.453709216706deg,
     128.453709216706deg,

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

@@ -50,7 +50,7 @@ const Provider = (props: Props) => {
                     <div className={styles.images}>{item.name}</div>
                     <div className={styles.images}>{item.name}</div>
                     <div style={{ margin: '10px', width: 'calc(100% - 84px)' }}>
                     <div style={{ margin: '10px', width: 'calc(100% - 84px)' }}>
                       <div style={{ fontWeight: 600 }}>{item.name}</div>
                       <div style={{ fontWeight: 600 }}>{item.name}</div>
-                      <div className={styles.desc}>{item.description || '--'}</div>
+                      <div className={styles.desc}>{item?.description || '--'}</div>
                     </div>
                     </div>
                   </div>
                   </div>
                   <div style={{ width: '70px' }}>
                   <div style={{ width: '70px' }}>
@@ -92,7 +92,7 @@ const Provider = (props: Props) => {
                     <div className={styles.images}>{item.name}</div>
                     <div className={styles.images}>{item.name}</div>
                     <div style={{ margin: '10px', width: 'calc(100% - 84px)' }}>
                     <div style={{ margin: '10px', width: 'calc(100% - 84px)' }}>
                       <div style={{ fontWeight: 600 }}>{item.name}</div>
                       <div style={{ fontWeight: 600 }}>{item.name}</div>
-                      <div className={styles.desc}>{item.description}</div>
+                      <div className={styles.desc}>{item.description || '--'}</div>
                     </div>
                     </div>
                   </div>
                   </div>
                   <div style={{ width: '70px' }}>
                   <div style={{ width: '70px' }}>

+ 12 - 2
src/pages/link/Protocol/index.tsx

@@ -2,7 +2,7 @@ import { PageContainer } from '@ant-design/pro-layout';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
 import { Badge, message } from 'antd';
 import { Badge, message } from 'antd';
-import { useRef, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import {
 import {
   DeleteOutlined,
   DeleteOutlined,
   EditOutlined,
   EditOutlined,
@@ -11,7 +11,7 @@ import {
   StopOutlined,
   StopOutlined,
 } from '@ant-design/icons';
 } from '@ant-design/icons';
 import Service from '@/pages/link/Protocol/service';
 import Service from '@/pages/link/Protocol/service';
-import { useIntl } from 'umi';
+import { useIntl, useLocation } from 'umi';
 import SearchComponent from '@/components/SearchComponent';
 import SearchComponent from '@/components/SearchComponent';
 import { PermissionButton, ProTableCard } from '@/components';
 import { PermissionButton, ProTableCard } from '@/components';
 import ProcotolCard from '@/components/ProTableCard/CardItems/protocol';
 import ProcotolCard from '@/components/ProTableCard/CardItems/protocol';
@@ -161,6 +161,15 @@ const Protocol = () => {
     },
     },
   ];
   ];
 
 
+  const location = useLocation();
+
+  useEffect(() => {
+    if ((location as any).query?.save === 'true') {
+      setCurrent(undefined);
+      setVisible(true);
+    }
+  }, []);
+
   return (
   return (
     <PageContainer>
     <PageContainer>
       <SearchComponent<ProtocolItem>
       <SearchComponent<ProtocolItem>
@@ -294,6 +303,7 @@ const Protocol = () => {
           }}
           }}
           reload={() => {
           reload={() => {
             actionRef.current?.reload();
             actionRef.current?.reload();
+            setVisible(false);
           }}
           }}
         />
         />
       )}
       )}

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

@@ -203,7 +203,7 @@ const Save = (props: Props) => {
   const save = async (deploy: boolean) => {
   const save = async (deploy: boolean) => {
     const value = await form.submit<ProtocolItem>();
     const value = await form.submit<ProtocolItem>();
     let response = undefined;
     let response = undefined;
-    if (props.data?.id) {
+    if (!props.data?.id) {
       response = await service.save(value);
       response = await service.save(value);
     } else {
     } else {
       response = await service.update(value);
       response = await service.update(value);

+ 43 - 1
src/pages/system/Relationship/Save/index.tsx

@@ -1,6 +1,6 @@
 import { useIntl } from 'umi';
 import { useIntl } from 'umi';
+import { onFieldValueChange, onFieldInputValueChange, createForm } from '@formily/core';
 import type { Field } from '@formily/core';
 import type { Field } from '@formily/core';
-import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { createSchemaField } from '@formily/react';
 import React from 'react';
 import React from 'react';
 import * as ICONS from '@ant-design/icons';
 import * as ICONS from '@ant-design/icons';
@@ -39,6 +39,16 @@ const Save = (props: Props) => {
     );
     );
   };
   };
 
 
+  const _validator = async (object: string, target: string, relation: string) => {
+    if (!relation || !target || !object) return;
+    const resp = await service.validator({
+      relation,
+      objectType: JSON.parse(object).objectType,
+      targetType: JSON.parse(target).targetType,
+    });
+    return resp?.result.passed;
+  };
+
   const form = createForm({
   const form = createForm({
     validateFirst: true,
     validateFirst: true,
     initialValues: {
     initialValues: {
@@ -56,6 +66,38 @@ const Save = (props: Props) => {
           })
           })
         : undefined,
         : undefined,
     },
     },
+    effects() {
+      onFieldInputValueChange('relation', async (field, f1) => {
+        const relation = field.value;
+        const target = (field.query('target').take() as Field).value;
+        const object = (field.query('object').take() as Field).value;
+        if (!relation || !target || !object) return;
+        const temp = await _validator(object, target, relation);
+        f1.setFieldState('relation', (state) => {
+          state.selfErrors = !temp ? ['关系标识已存在'] : undefined;
+        });
+      });
+      onFieldValueChange('target', async (field, f1) => {
+        const target = field.value;
+        const relation = (field.query('relation').take() as Field).value;
+        const object = (field.query('object').take() as Field).value;
+        if (!relation || !target || !object) return;
+        const temp = await _validator(object, target, relation);
+        f1.setFieldState('relation', (state) => {
+          state.selfErrors = !temp ? ['关系标识已存在'] : undefined;
+        });
+      });
+      onFieldValueChange('object', async (field, f1) => {
+        const object = field.value;
+        const target = (field.query('target').take() as Field).value;
+        const relation = (field.query('relation').take() as Field).value;
+        if (!relation || !target || !object) return;
+        const temp = await _validator(object, target, relation);
+        f1.setFieldState('relation', (state) => {
+          state.selfErrors = !temp ? ['关系标识已存在'] : undefined;
+        });
+      });
+    },
   });
   });
 
 
   const SchemaField = createSchemaField({
   const SchemaField = createSchemaField({

+ 6 - 0
src/pages/system/Relationship/service.ts

@@ -7,6 +7,12 @@ class Service extends BaseService<ReationItem> {
     request(`/${SystemConst.API_BASE}/relation/types`, {
     request(`/${SystemConst.API_BASE}/relation/types`, {
       method: 'GET',
       method: 'GET',
     });
     });
+
+  validator = (params: any) =>
+    request(`/${SystemConst.API_BASE}/relation/_validate?`, {
+      method: 'GET',
+      params,
+    });
 }
 }
 
 
 export default Service;
 export default Service;