xieyonghong 3 lat temu
rodzic
commit
2eda0de6d4
29 zmienionych plików z 615 dodań i 315 usunięć
  1. 2 2
      src/app.tsx
  2. 2 1
      src/components/AMapComponent/amap.tsx
  3. 4 4
      src/components/FormItems/MetadataJsonInput/index.tsx
  4. 1 0
      src/components/ProTableCard/CardItems/DataCollect/channel.tsx
  5. 8 7
      src/components/ProTableCard/CardItems/protocol.tsx
  6. 239 0
      src/pages/device/Instance/Detail/Diagnose/Message/form.tsx
  7. 1 1
      src/pages/device/Instance/Detail/Diagnose/Message/index.less
  8. 9 112
      src/pages/device/Instance/Detail/Diagnose/Message/index.tsx
  9. 1 1
      src/pages/device/Instance/Detail/Functions/AdvancedMode.tsx
  10. 14 9
      src/pages/device/Instance/Detail/Running/Property/index.tsx
  11. 106 11
      src/pages/device/Instance/Detail/Tags/Edit.tsx
  12. 58 0
      src/pages/device/Instance/Detail/Tags/ValueForm/index.tsx
  13. 0 9
      src/pages/device/Instance/Detail/Tags/index.tsx
  14. 15 15
      src/pages/device/Product/Detail/Access/AccessConfig/index.tsx
  15. 1 1
      src/pages/device/components/Metadata/Cat/index.tsx
  16. 2 2
      src/pages/home/comprehensive/index.tsx
  17. 2 2
      src/pages/home/device/index.tsx
  18. 1 1
      src/pages/link/AccessConfig/Detail/data.ts
  19. 83 81
      src/pages/link/Protocol/index.tsx
  20. 23 34
      src/pages/link/Type/Detail/index.tsx
  21. 4 2
      src/pages/rule-engine/Alarm/Log/SolveComponent/index.tsx
  22. 2 2
      src/pages/rule-engine/Alarm/Log/service.ts
  23. 1 0
      src/pages/rule-engine/Alarm/Log/typings.d.ts
  24. 8 3
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/TypeModel.tsx
  25. 13 5
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/WriteProperty.tsx
  26. 7 2
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/index.tsx
  27. 1 1
      src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx
  28. 5 5
      src/pages/system/Basis/index.tsx
  29. 2 2
      src/utils/util.ts

+ 2 - 2
src/app.tsx

@@ -446,9 +446,9 @@ export function render(oldRender: any) {
         ],
       },
     ];
-    Service.settingDetail('api').then((res) => {
+    Service.settingDetail('amap').then((res) => {
       if (res && res.status === 200 && res.result) {
-        localStorage.setItem(SystemConst.AMAP_KEY, res.result.api);
+        localStorage.setItem(SystemConst.AMAP_KEY, res.result.apiKey);
       }
     });
     Service.getSystemVersion().then((resp) => {

+ 2 - 1
src/components/AMapComponent/amap.tsx

@@ -19,6 +19,7 @@ export default (props: AMapProps) => {
   const isOpenUi = 'AMapUI' in props || props.AMapUI;
 
   const amapKey = localStorage.getItem(SystemConst.AMAP_KEY);
+  console.log(amapKey);
 
   const getAMapUI = () => {
     const version = typeof props.AMapUI === 'string' ? props.AMapUI : '1.1';
@@ -36,7 +37,7 @@ export default (props: AMapProps) => {
     }
   };
 
-  console.log(isOpenUi, uiLoading);
+  // console.log(isOpenUi, uiLoading);
 
   return (
     <div style={style || { width: '100%', height: '100%' }} className={className}>

+ 4 - 4
src/components/FormItems/MetadataJsonInput/index.tsx

@@ -1,8 +1,8 @@
 import { Input, Modal } from 'antd';
 import { FormOutlined } from '@ant-design/icons';
 import { useEffect, useState } from 'react';
-import MonacoEditor from 'react-monaco-editor';
 import { isObject } from 'lodash';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 
 type MetaDataJsonInputProps = {
   json: Record<string, any>;
@@ -19,7 +19,7 @@ export const MetaDataJsonHandle = (data: any): Record<string, any> => {
 
     switch (type) {
       case 'object':
-        _JSON[id] = MetaDataJsonHandle((data as any)['json']['properties'][0]);
+        _JSON[id] = MetaDataJsonHandle((data as any)?.json?.properties?.[0]);
         break;
       case 'array':
         _JSON[id] = [];
@@ -101,13 +101,13 @@ export default (props: MetaDataJsonInputProps) => {
         }}
         width={700}
       >
-        <MonacoEditor
+        <JMonacoEditor
           width={'100%'}
           height={400}
           theme="vs-dark"
           language={'json'}
           value={monacoValue}
-          onChange={(newValue) => {
+          onChange={(newValue: any) => {
             setMonacoValue(newValue);
           }}
           editorDidMount={editorDidMountHandle}

+ 1 - 0
src/components/ProTableCard/CardItems/DataCollect/channel.tsx

@@ -28,6 +28,7 @@ export default (props: ChannelCardProps) => {
       status={props.state?.value}
       statusText={props.state?.text}
       showMask={false}
+      showStatus={false}
       statusNames={{
         running: StatusColorEnum.success,
         disabled: StatusColorEnum.processing,

+ 8 - 7
src/components/ProTableCard/CardItems/protocol.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { StatusColorEnum } from '@/components/BadgeStatus';
+// import { StatusColorEnum } from '@/components/BadgeStatus';
 import { Ellipsis, TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
@@ -19,12 +19,13 @@ export default (props: ProcotolCardProps) => {
     <TableCard
       showMask={false}
       actions={props.actions}
-      status={props.state === 1 ? 'enabled' : 'disabled'}
-      statusText={props.state === 1 ? '正常' : '禁用'}
-      statusNames={{
-        enabled: StatusColorEnum.success,
-        disabled: StatusColorEnum.error,
-      }}
+      showStatus={false}
+      // status={props.state === 1 ? 'enabled' : 'disabled'}
+      // statusText={props.state === 1 ? '正常' : '禁用'}
+      // statusNames={{
+      //   enabled: StatusColorEnum.success,
+      //   disabled: StatusColorEnum.error,
+      // }}
     >
       <div className={'pro-table-card-item'}>
         <div className={'card-item-avatar'}>

+ 239 - 0
src/pages/device/Instance/Detail/Diagnose/Message/form.tsx

@@ -0,0 +1,239 @@
+import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
+import ProForm, { ProFormInstance } from '@ant-design/pro-form';
+import { DatePicker, Input, InputNumber, Select, Tooltip } from 'antd';
+import { GeoPoint, MetadataJsonInput } from '@/components';
+import { EditableProTable, ProColumns } from '@jetlinks/pro-table';
+import { QuestionCircleOutlined } from '@ant-design/icons';
+
+type DiagnoseFormProps = {
+  data: any[];
+};
+
+type FunctionTableDataType = {
+  id: string;
+  name: string;
+  type: string;
+};
+
+const DiagnoseForm = forwardRef((props: DiagnoseFormProps, ref) => {
+  const formRef = useRef<ProFormInstance<any>>();
+  const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
+
+  const getItemNode = (record: any) => {
+    const type = record.type;
+    const name = record.name;
+
+    switch (type) {
+      case 'enum':
+        return (
+          <Select
+            style={{ width: '100%', textAlign: 'left' }}
+            options={record.options}
+            fieldNames={{ label: 'text', value: 'value' }}
+            placeholder={'请选择' + name}
+          />
+        );
+      case 'boolean':
+        return (
+          <Select
+            style={{ width: '100%', textAlign: 'left' }}
+            options={[
+              { label: 'true', value: true },
+              { label: 'false', value: false },
+            ]}
+            placeholder={'请选择' + name}
+          />
+        );
+      case 'int':
+      case 'long':
+      case 'float':
+      case 'double':
+        return <InputNumber style={{ width: '100%' }} placeholder={'请输入' + name} />;
+      case 'geoPoint':
+        return <GeoPoint />;
+      case 'object':
+        return <MetadataJsonInput json={record.json} />;
+      case 'date':
+        return (
+          // <>
+          //   {
+          //     // @ts-ignore
+          //     <DatePicker
+          //       format={record.format || 'YYYY-MM-DD HH:mm:ss'}
+          //       style={{ width: '100%' }}
+          //     />
+          //   }
+          // </>
+          // @ts-ignore
+          <DatePicker format={'YYYY-MM-DD HH:mm:ss'} style={{ width: '100%' }} showTime />
+        );
+      default:
+        return <Input placeholder={type === 'array' ? '多个数据用英文,分割' : '请输入' + name} />;
+    }
+  };
+
+  const columns: ProColumns<FunctionTableDataType>[] = [
+    {
+      dataIndex: 'name',
+      title: '参数名称',
+      width: 120,
+      editable: false,
+      ellipsis: true,
+    },
+    {
+      dataIndex: 'type',
+      title: '输入类型',
+      width: 120,
+      editable: false,
+      render: (row) => {
+        switch (row) {
+          case 'array':
+            return (
+              <span>
+                {row}
+                <Tooltip
+                  title={
+                    <div>
+                      <p>输入示例:</p>
+                      <p>配置类型为int时,输入[1,2,3,4]</p>
+                    </div>
+                  }
+                >
+                  <QuestionCircleOutlined style={{ marginLeft: 8 }} />
+                </Tooltip>
+              </span>
+            );
+          case 'object':
+            return (
+              <span>
+                {row}
+                <Tooltip
+                  title={
+                    <div>
+                      <p>请按照json格式输入</p>
+                    </div>
+                  }
+                >
+                  <QuestionCircleOutlined style={{ marginLeft: 8 }} />
+                </Tooltip>
+              </span>
+            );
+          case 'file':
+            return (
+              <span>
+                {row}
+                <Tooltip
+                  title={
+                    <div>
+                      <p>输入示例:</p>
+                      <p>模型配置为base64时,输入YXNkZmRzYWY=</p>
+                    </div>
+                  }
+                >
+                  <QuestionCircleOutlined style={{ marginLeft: 8 }} />
+                </Tooltip>
+              </span>
+            );
+          case 'geoPoint':
+            return (
+              <span>
+                {row}
+                <Tooltip
+                  title={
+                    <div>
+                      <p>输入示例:</p>
+                      <p>[102,12231, 39.251423]</p>
+                    </div>
+                  }
+                >
+                  <QuestionCircleOutlined style={{ marginLeft: 8 }} />
+                </Tooltip>
+              </span>
+            );
+          default:
+            return row;
+        }
+      },
+    },
+    {
+      title: '值',
+      dataIndex: 'value',
+      align: 'center',
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            message: '此项是必填项',
+          },
+        ],
+      },
+      renderFormItem: (_, row: any) => {
+        return getItemNode(row.record);
+      },
+    },
+  ];
+
+  const handleDataSource = (data: any) => {
+    const array = [];
+    for (const datum of data) {
+      const type = datum.valueType ? datum.valueType.type : '-';
+      array.push({
+        id: datum.id,
+        name: datum.name,
+        type: type,
+        format: datum.valueType ? datum.valueType.format : undefined,
+        options: datum.valueType ? datum.valueType.elements : undefined,
+        json: type === 'object' ? datum?.json?.properties?.[0] : undefined,
+        value: undefined,
+      });
+    }
+    setEditableRowKeys(array.map((d) => d.id));
+    formRef.current?.setFieldsValue({
+      table: array,
+    });
+  };
+
+  /**
+   * 执行
+   */
+  const actionRun = () => {
+    return new Promise(async (resolve) => {
+      const formData = await formRef.current?.validateFields().catch(() => {
+        resolve(false);
+      });
+      if (formData?.table) {
+        resolve(formData.table);
+      } else {
+        resolve(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    handleDataSource(props.data);
+  }, [props.data]);
+
+  useImperativeHandle(ref, () => ({
+    save: actionRun,
+  }));
+
+  return (
+    <div>
+      <ProForm<{ table: FunctionTableDataType[] }> formRef={formRef} submitter={false}>
+        <EditableProTable<FunctionTableDataType>
+          rowKey="id"
+          name="table"
+          columns={columns}
+          recordCreatorProps={false}
+          editable={{
+            type: 'multiple',
+            editableKeys,
+            onChange: setEditableRowKeys,
+          }}
+        />
+      </ProForm>
+    </div>
+  );
+});
+
+export default DiagnoseForm;

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

@@ -33,6 +33,6 @@
   }
 
   .inputs-parameter {
-    margin: 15px 0;
+    margin-bottom: 10px;
   }
 }

+ 9 - 112
src/pages/device/Instance/Detail/Diagnose/Message/index.tsx

@@ -2,11 +2,11 @@ import TitleComponent from '@/components/TitleComponent';
 import './index.less';
 import Dialog from './Dialog';
 import { Badge, Button, Col, Empty, Row } from 'antd';
-import { useEffect, useMemo, useState } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
-import { Field, onFieldReact } from '@formily/core';
+import { Field } from '@formily/core';
 import { createForm, onFieldValueChange } from '@formily/core';
 import { createSchemaField, FormProvider } from '@formily/react';
 import {
@@ -23,11 +23,13 @@ import { randomString } from '@/utils/util';
 import Log from './Log';
 import { DiagnoseStatusModel, messageStatusMap, messageStyleMap } from '../Status/model';
 import { observer } from '@formily/reactive-react';
+import DiagnoseForm from './form';
 
 const Message = observer(() => {
   const [subscribeTopic] = useSendWebsocketMessage();
   const [input, setInput] = useState<any>({});
   const [inputs, setInputs] = useState<any[]>([]);
+  const DiagnoseFormRef = useRef<{ save: any }>();
 
   const metadata = JSON.parse(InstanceModel.detail?.metadata || '{}');
 
@@ -115,42 +117,6 @@ const Message = observer(() => {
     }
   }, [DiagnoseStatusModel.state]);
 
-  const form = useMemo(
-    () =>
-      createForm({
-        initialValues: {
-          data: [...inputs],
-        },
-        effects() {
-          onFieldReact('data.*.valueType.type', (field) => {
-            const value = (field as Field).value;
-            const format = field.query('..value').take() as any;
-            if (format) {
-              switch (value) {
-                case 'date':
-                  format.setComponent(FDatePicker);
-                  break;
-                case 'boolean':
-                  format.setComponent(FSelect);
-                  format.setDataSource([
-                    { label: '是', value: true },
-                    { label: '否', value: false },
-                  ]);
-                  break;
-                case 'int':
-                  format.setComponent(NumberPicker);
-                  break;
-                default:
-                  format.setComponent(FInput);
-                  break;
-              }
-            }
-          });
-        },
-      }),
-    [],
-  );
-
   const _form = useMemo(
     () =>
       createForm({
@@ -206,9 +172,6 @@ const Message = observer(() => {
               const data = (metadata?.functions || []).find((item: any) => item.id === value);
               setInput(data);
               setInputs(data?.inputs || []);
-              form.setValues({
-                data: data?.inputs || [],
-              });
             }
           });
         },
@@ -370,73 +333,6 @@ const Message = observer(() => {
     },
   };
 
-  const schema = {
-    type: 'object',
-    properties: {
-      data: {
-        type: 'array',
-        'x-decorator': 'FormItem',
-        'x-component': 'ArrayTable',
-        'x-component-props': {
-          scroll: { x: '100%' },
-        },
-        items: {
-          type: 'object',
-          properties: {
-            column1: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                title: '参数名称',
-              },
-              properties: {
-                name: {
-                  type: 'string',
-                  'x-decorator': 'FormItem',
-                  'x-component': 'PreviewText.Input',
-                },
-              },
-            },
-            column2: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                title: '输入类型',
-              },
-              properties: {
-                'valueType.type': {
-                  type: 'string',
-                  'x-decorator': 'FormItem',
-                  'x-component': 'PreviewText.Input',
-                },
-              },
-            },
-            column3: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                title: '值',
-              },
-              properties: {
-                value: {
-                  type: 'string',
-                  'x-decorator': 'FormItem',
-                  'x-component': 'FInput',
-                  'x-validator': [
-                    {
-                      required: true,
-                      message: '改字段必填',
-                    },
-                  ],
-                },
-              },
-            },
-          },
-        },
-      },
-    },
-  };
-
   return (
     <Row gutter={24}>
       <Col span={16}>
@@ -479,7 +375,10 @@ const Message = observer(() => {
                     const values = await _form.submit<any>();
                     let _inputs = undefined;
                     if (inputs.length) {
-                      _inputs = await form.submit<any>();
+                      _inputs = await DiagnoseFormRef.current?.save();
+                      if (!_inputs) {
+                        return;
+                      }
                     }
                     if (values.type === 'function') {
                       const list = (_inputs?.data || []).filter((it: any) => !!it.value);
@@ -509,9 +408,7 @@ const Message = observer(() => {
               {inputs.length > 0 && (
                 <div className="inputs-parameter">
                   <h4>功能参数</h4>
-                  <FormProvider form={form}>
-                    <SchemaField schema={schema} />
-                  </FormProvider>
+                  <DiagnoseForm data={inputs} ref={DiagnoseFormRef} />
                 </div>
               )}
             </div>

+ 1 - 1
src/pages/device/Instance/Detail/Functions/AdvancedMode.tsx

@@ -51,7 +51,7 @@ export default (props: FunctionProps) => {
     for (const datum of properties) {
       switch (datum.valueType.type) {
         case 'object':
-          obj[datum.id] = MetaDataJsonHandle(datum['json']['properties'][0]);
+          obj[datum.id] = MetaDataJsonHandle(datum?.json?.properties?.[0]);
           break;
         case 'array':
           obj[datum.id] = [];

+ 14 - 9
src/pages/device/Instance/Detail/Running/Property/index.tsx

@@ -1,6 +1,6 @@
 import { Col, Empty, Input, Pagination, Row, Space, Spin, Table, Tooltip } from 'antd';
 import CheckButton from '@/components/CheckButton';
-import { useCallback, useEffect, useRef, useState } from 'react';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import PropertyCard from './PropertyCard';
 import {
@@ -229,6 +229,18 @@ const Property = (props: Props) => {
     });
   };
 
+  const memo = useMemo(
+    () => (
+      <EditProperty
+        data={currentInfo}
+        onCancel={() => {
+          setVisible(false);
+        }}
+      />
+    ),
+    [propertyValue],
+  );
+
   useEffect(() => {
     if (!loading1) {
       subscribeProperty();
@@ -352,14 +364,7 @@ const Property = (props: Props) => {
           </div>
         )}
       </Spin>
-      {visible && (
-        <EditProperty
-          data={currentInfo}
-          onCancel={() => {
-            setVisible(false);
-          }}
-        />
-      )}
+      {visible && memo}
       {infoVisible && <PropertyLog data={currentInfo} close={() => setInfoVisible(false)} />}
       {indicatorVisible && (
         <Indicators

+ 106 - 11
src/pages/device/Instance/Detail/Tags/Edit.tsx

@@ -1,12 +1,13 @@
-import { createForm } from '@formily/core';
+import { createForm, Field, FormPath, onFieldReact } from '@formily/core';
 import { createSchemaField, FormProvider } from '@formily/react';
 import { InstanceModel, service } from '@/pages/device/Instance';
-import { ArrayTable, FormItem, Input } from '@formily/antd';
+import { ArrayTable, FormItem, Input, NumberPicker, DatePicker, Select } from '@formily/antd';
 import { Modal } from 'antd';
 import { useIntl } from 'umi';
 import GeoComponent from './location/GeoComponent';
 import { onlyMessage } from '@/utils/util';
 import RemoveData from './RemoveData';
+import { MetadataJsonInput } from '@/components';
 
 interface Props {
   close: () => void;
@@ -22,15 +23,98 @@ const Edit = (props: Props) => {
     initialValues: {
       tags: tags,
     },
+    effects() {
+      onFieldReact('tags.*.id', (field, f) => {
+        const value = (field as Field).value;
+        const item = (tags || []).find((it: any) => it.id === value);
+        const valueType = item?.type || item?.dataType?.type;
+        const nextPath = FormPath.transform(field.path, /\d+/, (index) => {
+          return `tags.${index}.value`;
+        });
+        switch (valueType) {
+          case 'enum':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'Select';
+              state.dataSource = (item?.dataType?.elements || []).map((i: any) => {
+                return {
+                  label: i?.text,
+                  value: i?.value,
+                };
+              });
+              state.componentProps = {
+                placeholder: '请选择',
+              };
+            });
+            break;
+          case 'boolean':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'Select';
+              state.dataSource = [
+                { label: '是', value: true },
+                { label: '否', value: false },
+              ];
+              state.componentProps = {
+                placeholder: '请选择',
+              };
+            });
+            break;
+          case 'int':
+          case 'long':
+          case 'float':
+          case 'double':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'NumberPicker';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+          case 'geoPoint':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'GeoComponent';
+              state.componentProps = {
+                json: item?.json?.properties?.[0],
+              };
+            });
+            break;
+          case 'object':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'MetadataJsonInput';
+            });
+            break;
+          case 'date':
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'DatePicker';
+              state.componentProps = {
+                placeholder: '请选择',
+                format: 'YYYY-MM-DD HH:mm:ss',
+              };
+            });
+            break;
+          default:
+            f.setFieldState(nextPath, (state) => {
+              state.componentType = 'Input';
+              state.componentProps = {
+                placeholder: '请输入',
+              };
+            });
+            break;
+        }
+      });
+    },
   });
 
   const SchemaField = createSchemaField({
     components: {
       FormItem,
       Input,
+      Select,
       ArrayTable,
+      DatePicker,
+      NumberPicker,
       RemoveData,
       GeoComponent,
+      MetadataJsonInput,
     },
   });
 
@@ -53,6 +137,12 @@ const Edit = (props: Props) => {
               'x-component': 'ArrayTable.Column',
               'x-component-props': { width: 200, title: 'ID' },
               properties: {
+                id: {
+                  type: 'string',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Input',
+                  'x-hidden': true,
+                },
                 key: {
                   type: 'string',
                   'x-decorator': 'FormItem',
@@ -100,14 +190,14 @@ const Edit = (props: Props) => {
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
-                  'x-reactions': {
-                    dependencies: ['.type'],
-                    fulfill: {
-                      state: {
-                        componentType: '{{$deps[0]==="geoPoint"?"GeoComponent":"Input"}}',
-                      },
-                    },
-                  },
+                  // 'x-reactions': {
+                  //   dependencies: ['.type'],
+                  //   fulfill: {
+                  //     state: {
+                  //       componentType: '{{$deps[0]==="geoPoint"?"GeoComponent":"Input"}}',
+                  //     },
+                  //   },
+                  // },
                 },
               },
             },
@@ -161,7 +251,12 @@ const Edit = (props: Props) => {
         if (values?.tags.length === 0) {
           props.close();
         } else {
-          const list = (values?.tags || []).filter((item: any) => item?.key);
+          const list = (values?.tags || [])
+            .filter((item: any) => item?.key)
+            .map((i: any) => {
+              const { dataType, ...extra } = i;
+              return { ...extra };
+            });
           const resp = await service.saveTags(InstanceModel.detail?.id || '', list);
           if (resp.status === 200) {
             props.refresh();

+ 58 - 0
src/pages/device/Instance/Detail/Tags/ValueForm/index.tsx

@@ -0,0 +1,58 @@
+import { DatePicker, Input, InputNumber, Select } from 'antd';
+import { GeoPoint, MetadataJsonInput } from '@/components';
+
+type ValueFormProps = {
+  value: any;
+  data: any;
+  onChange: () => void;
+};
+
+const ValueForm = (props: ValueFormProps) => {
+  const { data } = props;
+  const getItemNode = (record: any) => {
+    const type = record.type;
+
+    switch (type) {
+      case 'enum':
+        return (
+          <Select
+            style={{ width: '100%', textAlign: 'left' }}
+            options={record.options}
+            fieldNames={{ label: 'text', value: 'value' }}
+            placeholder={'请选择' + name}
+          />
+        );
+      case 'boolean':
+        return (
+          <Select
+            style={{ width: '100%', textAlign: 'left' }}
+            options={[
+              { label: 'true', value: true },
+              { label: 'false', value: false },
+            ]}
+            placeholder={'请选择' + name}
+          />
+        );
+      case 'int':
+      case 'long':
+      case 'float':
+      case 'double':
+        return <InputNumber style={{ width: '100%' }} placeholder={'请输入' + name} />;
+      case 'geoPoint':
+        return <GeoPoint />;
+      case 'object':
+        return <MetadataJsonInput json={record.json} />;
+      case 'date':
+        return (
+          // @ts-ignore
+          <DatePicker format={'YYYY-MM-DD HH:mm:ss'} style={{ width: '100%' }} showTime />
+        );
+      default:
+        return <Input placeholder={type === 'array' ? '多个数据用英文,分割' : '请输入' + name} />;
+    }
+  };
+
+  return getItemNode(data);
+};
+
+export default ValueForm;

+ 0 - 9
src/pages/device/Instance/Detail/Tags/index.tsx

@@ -50,15 +50,6 @@ const Tags = () => {
               <EditOutlined />
               编辑
             </PermissionButton>
-            {/* <Button
-              type="link"
-              onClick={() => {
-                setVisible(true);
-              }}
-            >
-              <EditOutlined />
-              编辑
-            </Button> */}
           </span>
         }
       >

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

@@ -61,25 +61,25 @@ const AccessConfig = (props: Props) => {
                     termType: 'in',
                     value: 'child-device,edge-child-device',
                   },
-                  {
-                    column: 'state',
-                    termType: 'eq',
-                    value: 'enabled',
-                  },
+                  // {
+                  //   column: 'state',
+                  //   termType: 'eq',
+                  //   value: 'enabled',
+                  // },
                 ],
               },
             ]
           : [
               ...params?.terms,
-              {
-                terms: [
-                  {
-                    column: 'state',
-                    termType: 'eq',
-                    value: 'enabled',
-                  },
-                ],
-              },
+              // {
+              //   terms: [
+              //     {
+              //       column: 'state',
+              //       termType: 'eq',
+              //       value: 'enabled',
+              //     },
+              //   ],
+              // },
             ],
     };
     service.queryList({ ...temp, sorts: [{ name: 'createTime', order: 'desc' }] }).then((resp) => {
@@ -230,7 +230,7 @@ const AccessConfig = (props: Props) => {
         <Row gutter={[16, 16]}>
           {(dataSource?.data || []).map((item: any) => (
             <Col
-              key={item.name}
+              key={item.id}
               span={12}
               onClick={() => {
                 setCurrrent(item);

+ 1 - 1
src/pages/device/components/Metadata/Cat/index.tsx

@@ -71,7 +71,7 @@ const Cat = observer((props: Props) => {
             type="primary"
             onClick={async () => {
               try {
-                downloadObject(JSON.parse(value), `设备-物模型`);
+                downloadObject(JSON.parse(value), `设备-物模型`, 'YYYY/MM/DD');
               } catch (e) {
                 message.error('物模型格式错误!');
               }

+ 2 - 2
src/pages/home/comprehensive/index.tsx

@@ -46,7 +46,7 @@ const Comprehensive = () => {
       setProductCount(resp.result);
     }
     if (resp.status === 403) {
-      setProductMessage('暂无产品权限');
+      setProductMessage('暂无权限');
     }
   };
 
@@ -56,7 +56,7 @@ const Comprehensive = () => {
       setDeviceCount(resp.result);
     }
     if (resp.status === 403) {
-      setDeviceMessage('暂无设备权限');
+      setDeviceMessage('暂无权限');
     }
   };
 

+ 2 - 2
src/pages/home/device/index.tsx

@@ -30,7 +30,7 @@ const Device = () => {
       setProductCount(resp.result);
     }
     if (resp.status === 403) {
-      setProductMessage('暂无产品权限');
+      setProductMessage('暂无权限');
     }
   };
 
@@ -40,7 +40,7 @@ const Device = () => {
       setDeviceCount(resp.result);
     }
     if (resp.status === 403) {
-      setDeviceMessage('暂无设备权限');
+      setDeviceMessage('暂无权限');
     }
   };
 

+ 1 - 1
src/pages/link/AccessConfig/Detail/data.ts

@@ -2,7 +2,7 @@ export const ProtocolMapping = new Map();
 ProtocolMapping.set('websocket-server', 'WebSocket');
 ProtocolMapping.set('http-server-gateway', 'HTTP');
 ProtocolMapping.set('udp-device-gateway', 'UDP');
-ProtocolMapping.set('coap-server-gateway', 'COAP');
+ProtocolMapping.set('coap-server-gateway', 'CoAP');
 ProtocolMapping.set('mqtt-client-gateway', 'MQTT');
 ProtocolMapping.set('mqtt-server-gateway', 'MQTT');
 ProtocolMapping.set('tcp-server-gateway', 'TCP');

+ 83 - 81
src/pages/link/Protocol/index.tsx

@@ -1,20 +1,20 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { Badge } from 'antd';
+// import { Badge } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import {
   DeleteOutlined,
   EditOutlined,
-  PlayCircleOutlined,
+  // PlayCircleOutlined,
   PlusOutlined,
-  StopOutlined,
+  // StopOutlined,
 } from '@ant-design/icons';
 import Service from '@/pages/link/Protocol/service';
 import { useIntl, useLocation } from 'umi';
 import SearchComponent from '@/components/SearchComponent';
 import { PermissionButton, ProTableCard } from '@/components';
-import ProcotolCard from '@/components/ProTableCard/CardItems/protocol';
+import ProtocolCard from '@/components/ProTableCard/CardItems/protocol';
 import Save from './save';
 import { onlyMessage } from '@/utils/util';
 
@@ -28,15 +28,15 @@ const Protocol = () => {
   const { permission } = PermissionButton.usePermission('link/Protocol');
   const intl = useIntl();
 
-  const modifyState = async (id: string, type: 'deploy' | 'un-deploy') => {
-    const resp = await service.modifyState(id, type);
-    if (resp.status === 200) {
-      onlyMessage('操作成功!');
-    } else {
-      onlyMessage(resp?.message || '操作失败', 'error');
-    }
-    actionRef.current?.reload();
-  };
+  // const modifyState = async (id: string, type: 'deploy' | 'un-deploy') => {
+  //   const resp = await service.modifyState(id, type);
+  //   if (resp.status === 200) {
+  //     onlyMessage('操作成功!');
+  //   } else {
+  //     onlyMessage(resp?.message || '操作失败', 'error');
+  //   }
+  //   actionRef.current?.reload();
+  // };
 
   const columns: ProColumns<ProtocolItem>[] = [
     {
@@ -71,24 +71,24 @@ const Protocol = () => {
         },
       },
     },
-    {
-      dataIndex: 'state',
-      title: '状态',
-      valueType: 'select',
-      valueEnum: {
-        0: {
-          text: '禁用',
-          status: 0,
-        },
-        1: {
-          text: '正常',
-          status: 1,
-        },
-      },
-      renderText: (text) => (
-        <Badge color={text !== 1 ? 'red' : 'green'} text={text !== 1 ? '禁用' : '正常'} />
-      ),
-    },
+    // {
+    //   dataIndex: 'state',
+    //   title: '状态',
+    //   valueType: 'select',
+    //   valueEnum: {
+    //     0: {
+    //       text: '禁用',
+    //       status: 0,
+    //     },
+    //     1: {
+    //       text: '正常',
+    //       status: 1,
+    //     },
+    //   },
+    //   renderText: (text) => (
+    //     <Badge color={text !== 1 ? 'red' : 'green'} text={text !== 1 ? '禁用' : '正常'} />
+    //   ),
+    // },
     {
       dataIndex: 'description',
       ellipsis: true,
@@ -120,37 +120,38 @@ const Protocol = () => {
         >
           <EditOutlined />
         </PermissionButton>,
-        <PermissionButton
-          isPermission={permission.action}
-          key="action"
-          type={'link'}
-          style={{ padding: 0 }}
-          tooltip={{
-            title: record.state === 1 ? '禁用' : '启用',
-          }}
-          popConfirm={{
-            title: `确认${record.state === 1 ? '禁用' : '启用'}`,
-            onConfirm: () => {
-              if (record.state === 1) {
-                modifyState(record.id, 'un-deploy');
-              } else {
-                modifyState(record.id, 'deploy');
-              }
-            },
-          }}
-        >
-          {record.state === 1 ? <StopOutlined /> : <PlayCircleOutlined />}
-        </PermissionButton>,
+        // <PermissionButton
+        //   isPermission={permission.action}
+        //   key="action"
+        //   type={'link'}
+        //   style={{ padding: 0 }}
+        //   tooltip={{
+        //     title: record.state === 1 ? '禁用' : '启用',
+        //   }}
+        //   popConfirm={{
+        //     title: `确认${record.state === 1 ? '禁用' : '启用'}`,
+        //     onConfirm: () => {
+        //       if (record.state === 1) {
+        //         modifyState(record.id, 'un-deploy');
+        //       } else {
+        //         modifyState(record.id, 'deploy');
+        //       }
+        //     },
+        //   }}
+        // >
+        //   {record.state === 1 ? <StopOutlined /> : <PlayCircleOutlined />}
+        // </PermissionButton>,
         <PermissionButton
           isPermission={permission.delete}
           tooltip={{
-            title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
+            // title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
+            title: '删除',
           }}
           style={{ padding: 0 }}
-          disabled={record.state === 1}
+          // disabled={record.state === 1}
           popConfirm={{
             title: '确认删除',
-            disabled: record.state === 1,
+            // disabled: record.state === 1,
             onConfirm: async () => {
               const resp: any = await service.remove(record.id);
               if (resp.status === 200) {
@@ -234,7 +235,7 @@ const Protocol = () => {
           </PermissionButton>,
         ]}
         cardRender={(record) => (
-          <ProcotolCard
+          <ProtocolCard
             {...record}
             actions={[
               <PermissionButton
@@ -256,37 +257,38 @@ const Protocol = () => {
                 <EditOutlined />
                 编辑
               </PermissionButton>,
-              <PermissionButton
-                isPermission={permission.action}
-                key="action"
-                type={'link'}
-                style={{ padding: 0 }}
-                tooltip={{
-                  title: record.state === 1 ? '禁用' : '启用',
-                }}
-                popConfirm={{
-                  title: `确认${record.state === 1 ? '禁用' : '启用'}`,
-                  onConfirm: () => {
-                    if (record.state === 1) {
-                      modifyState(record.id, 'un-deploy');
-                    } else {
-                      modifyState(record.id, 'deploy');
-                    }
-                  },
-                }}
-              >
-                {record.state === 1 ? <StopOutlined /> : <PlayCircleOutlined />}
-                {record.state === 1 ? '禁用' : '启用'}
-              </PermissionButton>,
+              // <PermissionButton
+              //   isPermission={permission.action}
+              //   key="action"
+              //   type={'link'}
+              //   style={{ padding: 0 }}
+              //   tooltip={{
+              //     title: record.state === 1 ? '禁用' : '启用',
+              //   }}
+              //   popConfirm={{
+              //     title: `确认${record.state === 1 ? '禁用' : '启用'}`,
+              //     onConfirm: () => {
+              //       if (record.state === 1) {
+              //         modifyState(record.id, 'un-deploy');
+              //       } else {
+              //         modifyState(record.id, 'deploy');
+              //       }
+              //     },
+              //   }}
+              // >
+              //   {record.state === 1 ? <StopOutlined /> : <PlayCircleOutlined />}
+              //   {record.state === 1 ? '禁用' : '启用'}
+              // </PermissionButton>,
               <PermissionButton
                 isPermission={permission.delete}
                 tooltip={{
-                  title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
+                  // title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
+                  title: '删除',
                 }}
-                disabled={record.state === 1}
+                // disabled={record.state === 1}
                 popConfirm={{
                   title: '确认删除',
-                  disabled: record.state === 1,
+                  // disabled: record.state === 1,
                   onConfirm: async () => {
                     const resp: any = await service.remove(record.id);
                     if (resp.status === 200) {

+ 23 - 34
src/pages/link/Type/Detail/index.tsx

@@ -130,26 +130,14 @@ const Save = observer(() => {
             if (param?.id && param.id !== ':id') {
               const resp = await service.detail(param.id);
               const data = resp?.result || {};
-              // if (data?.shareCluster === false) {
-              //   const cluster = (data?.cluster || []).map((item: any) => {
-              //     return {
-              //       ...item.configuration,
-              //       configuration: item,
-              //     };
-              //   });
-              //   console.log(cluster, 'cluster')
-              //   const obj = _.cloneDeep({ ...data, cluster });
-              //   form1.setValues({ ...obj });
-              // }
               if (data?.shareCluster === false) {
-                const _cluster = _.cloneDeep(data.cluster[0]);
-                data.cluster[0] = {
-                  ..._cluster.configuration,
-                };
-                form1.setValues({ ...data });
-              } else {
-                form1.setValues({ ...data });
+                data.cluster = (data?.cluster || []).map((item: any) => {
+                  return {
+                    ...item.configuration,
+                  };
+                });
               }
+              form1.setValues({ ...data });
             }
           });
           onFieldValueChange('type', (field, f) => {
@@ -525,21 +513,21 @@ const Save = observer(() => {
             'x-decorator': 'FormItem',
             'x-component': 'Input',
           },
-          maxMessageSize: {
-            title: '最大消息长度',
-            'x-decorator-props': {
-              gridSpan: 1,
-              tooltip: '单次收发消息的最大长度,单位:字节;设置过大可能会影响性能',
-              layout: 'vertical',
-              labelAlign: 'left',
-            },
-            'x-component-props': {
-              placeholder: '请输入最大消息长度',
-            },
-            required: true,
-            'x-decorator': 'FormItem',
-            'x-component': 'Input',
-          },
+          // maxMessageSize: {
+          //   title: '最大消息长度',
+          //   'x-decorator-props': {
+          //     gridSpan: 1,
+          //     tooltip: '单次收发消息的最大长度,单位:字节;设置过大可能会影响性能',
+          //     layout: 'vertical',
+          //     labelAlign: 'left',
+          //   },
+          //   'x-component-props': {
+          //     placeholder: '请输入最大消息长度',
+          //   },
+          //   required: true,
+          //   'x-decorator': 'FormItem',
+          //   'x-component': 'Input',
+          // },
           topicPrefix: {
             title: '订阅前缀',
             'x-component-props': {
@@ -574,7 +562,8 @@ const Save = observer(() => {
           fulfill: {
             state: {
               // visible: '{{$deps[0]==="UDP"}}',
-              visible: '{{["MQTT_SERVER"].includes($deps[0])}}',
+              visible: '{{["MQTT_SERVER","MQTT_CLIENT"].includes($deps[0])}}',
+              // hidden:'{{["MQTT_SERVER"].includes($deps[0])}}'
             },
           },
         },

+ 4 - 2
src/pages/rule-engine/Alarm/Log/SolveComponent/index.tsx

@@ -26,11 +26,13 @@ const SolveComponent = (props: Props) => {
         layout="vertical"
         form={form}
         onFinish={async (values: any) => {
-          const resp = await service.handleLog(data?.id || '', {
-            id: data?.id || '',
+          const resp = await service.handleLog({
             describe: values.describe,
             type: 'user',
             state: 'normal',
+            alarmRecordId: data?.id || '',
+            alarmConfigId: data?.alarmConfigId || '',
+            alarmTime: data?.alarmTime || '',
           });
           if (resp.status === 200) {
             onlyMessage('操作成功!');

+ 2 - 2
src/pages/rule-engine/Alarm/Log/service.ts

@@ -8,8 +8,8 @@ class Service extends BaseService<AlarmLogItem> {
       method: 'GET',
     });
 
-  handleLog = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/alarm/record/${id}/_handle`, {
+  handleLog = (data: any) =>
+    request(`/${SystemConst.API_BASE}/alarm/record/_handle`, {
       method: 'POST',
       data,
     });

+ 1 - 0
src/pages/rule-engine/Alarm/Log/typings.d.ts

@@ -10,6 +10,7 @@ type AlarmLogItem = {
   level: number;
   description?: string;
   state: Record<string, any>;
+  alarmTime?: number | string;
 };
 
 type AlarmLogSolveHistoryItem = {

+ 8 - 3
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/TypeModel.tsx

@@ -95,10 +95,14 @@ export default observer((props: Props) => {
 
   useEffect(() => {
     setValue(props.value);
-    setLabelValue(props.label);
+    // setLabelValue(props.label);
     // console.log('typemodel', props.value);
   }, [props.value]);
 
+  useEffect(() => {
+    setLabelValue(props.label);
+  }, []);
+
   const renderNode = (type: string) => {
     switch (type) {
       case 'int':
@@ -126,11 +130,12 @@ export default observer((props: Props) => {
             fieldNames={{ label: 'text', value: 'value' }}
             placeholder={'请选择'}
             onChange={(e, options: any) => {
-              // console.log(options);
+              console.log(options?.text);
               setValue(e);
               setLabelValue(options?.text);
+              // DeviceModel.propertiesValue = options?.text
               if (props.onChange) {
-                props.onChange(e, source);
+                props.onChange(e, source, options?.text);
               }
               // onChange(e)
             }}

+ 13 - 5
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/WriteProperty.tsx

@@ -7,7 +7,7 @@ interface Props {
   properties: any[];
   value?: any;
   id?: string;
-  onChange?: (value?: any, text?: any) => void;
+  onChange?: (value?: any, text?: any, valueLable?: any) => void;
   propertiesChange?: (value?: string) => void;
   name?: any;
   onColumns?: (col: any, text?: any) => void;
@@ -25,6 +25,7 @@ export default (props: Props) => {
   const [format, setFormat] = useState<any>('HH:mm:ss');
   const [enumList, setEnumList] = useState<any>([]);
   const [label, setLabel] = useState<any>();
+  const ref = useRef<any>('');
 
   useEffect(() => {
     // console.log(props.value);
@@ -47,8 +48,8 @@ export default (props: Props) => {
                 console.log(propertiesItem.valueType.elements, props.value[key].value);
                 setEnumList(propertiesItem.valueType.elements);
                 const text = propertiesItem.valueType.elements.find(
-                  (item: any) => item.value === props.value[key].value?.[0],
-                ).text;
+                  (item: any) => item.value === props.value[key].value,
+                )?.text;
                 setLabel(text);
                 // console.log(text);
               }
@@ -69,7 +70,12 @@ export default (props: Props) => {
           source: source,
         },
       };
-      props.onChange(obj, textRef.current);
+      //处理枚举外部回显
+      if (ref.current) {
+        props.onChange(obj, textRef.current, ref.current);
+      } else {
+        props.onChange(obj, textRef.current);
+      }
     }
   }, [propertiesValue, source]);
 
@@ -119,8 +125,10 @@ export default (props: Props) => {
                 props.onColumns(col);
               }
             }}
-            onChange={(value, sources) => {
+            onChange={(value, sources, text) => {
               // console.log(value, sources);
+              console.log(text);
+              ref.current = text;
               setPropertiesValue(value);
               setSource(sources);
             }}

+ 7 - 2
src/pages/rule-engine/Scene/Save/action/DeviceOutput/actions/index.tsx

@@ -193,10 +193,15 @@ export default observer((props: Props) => {
                 DeviceModel.actionName = text;
                 // console.log(text)
               }}
-              onChange={(value, text) => {
+              onChange={(value, text, valueLable) => {
                 const item = value[Object.keys(value)?.[0]]?.value;
+                // console.log(`valueLable`,valueLable)
                 DeviceModel.propertiesName = text;
-                DeviceModel.propertiesValue = item;
+                if (valueLable) {
+                  DeviceModel.propertiesValue = valueLable;
+                } else {
+                  DeviceModel.propertiesValue = item;
+                }
               }}
               onRest={(value: any) => {
                 form.setFieldValue(['message', 'properties'], {

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/DeviceOutput/index.tsx

@@ -140,7 +140,7 @@ export default observer((props: Props) => {
       }));
       // console.log(_options.taglist, 'taglist')
     }
-    // console.log(item,_options);
+    // console.log(DeviceModel.propertiesValue,_options);
     props.save(item, _options);
     init();
   };

+ 5 - 5
src/pages/system/Basis/index.tsx

@@ -23,10 +23,10 @@ const Basis = () => {
       const basis = res.result?.filter((item: any) => item.scope === 'front');
       const api = res.result?.filter((item: any) => item.scope === 'amap');
       const basePath = res.result?.filter((item: any) => item.scope === 'paths');
-      localStorage.setItem(SystemConst.AMAP_KEY, api[0].properties.api);
+      localStorage.setItem(SystemConst.AMAP_KEY, api[0].properties.apiKey);
       form.setFieldsValue({
         ...basis[0].properties,
-        apikey: api[0].properties.api,
+        apiKey: api[0].properties.apiKey,
         'base-path': basePath[0].properties['base-path'],
       });
       setInitialState({
@@ -45,14 +45,14 @@ const Basis = () => {
           scope: 'front',
           properties: {
             ...formData,
-            apikey: '',
+            apiKey: '',
             'base-path': '',
           },
         },
         {
           scope: 'amap',
           properties: {
-            api: formData.apikey,
+            apiKey: formData.apiKey,
           },
         },
         {
@@ -105,7 +105,7 @@ const Basis = () => {
               </Form.Item>
               <Form.Item
                 label="高德API Key"
-                name="apikey"
+                name="apiKey"
                 tooltip="配置后平台可调用高德地图GIS服务"
               >
                 <Input />

+ 2 - 2
src/utils/util.ts

@@ -50,11 +50,11 @@ export const downloadFileByUrl = (url: string, name: string, type: string) => {
  * @param record
  * @param fileName
  */
-export const downloadObject = (record: Record<string, any>, fileName: string) => {
+export const downloadObject = (record: Record<string, any>, fileName: string, format?: string) => {
   // 创建隐藏的可下载链接
   const ghostLink = document.createElement('a');
   ghostLink.download = `${record?.name || ''}${fileName}_${moment(new Date()).format(
-    'YYYY/MM/DD HH:mm:ss',
+    format || 'YYYY/MM/DD HH:mm:ss',
   )}.json`;
   ghostLink.style.display = 'none';
   //字符串内容转成Blob地址