wzyyy преди 3 години
родител
ревизия
f8571923d1
променени са 59 файла, в които са добавени 1062 реда и са изтрити 1132 реда
  1. 1 0
      src/components/DashBoard/timePicker.tsx
  2. 2 2
      src/components/FormItems/MetadataJsonInput/index.tsx
  3. 1 1
      src/components/PermissionButton/index.tsx
  4. 20 8
      src/components/ProTableCard/CardItems/AccessConfig/index.tsx
  5. 10 6
      src/components/ProTableCard/CardItems/networkCard.tsx
  6. 1 1
      src/components/ProTableCard/CardItems/product.tsx
  7. 9 0
      src/components/ProTableCard/index.less
  8. 1 1
      src/locales/zh-CN/pages.ts
  9. 1 2
      src/pages/Northbound/AliCloud/Detail/index.tsx
  10. 43 3
      src/pages/Northbound/DuerOS/Detail/index.tsx
  11. 23 2
      src/pages/account/NotificationRecord/index.tsx
  12. 3 0
      src/pages/account/NotificationRecord/mark_chat_read.svg
  13. 2 2
      src/pages/device/Alarm/index.tsx
  14. 2 2
      src/pages/device/Command/index.tsx
  15. 2 2
      src/pages/device/Firmware/index.tsx
  16. 29 28
      src/pages/device/Instance/Detail/Config/index.tsx
  17. 75 12
      src/pages/device/Instance/Detail/Functions/form.tsx
  18. 17 10
      src/pages/device/Instance/Detail/Functions/index.tsx
  19. 8 2
      src/pages/device/Instance/Detail/Info/index.tsx
  20. 6 2
      src/pages/device/Instance/Detail/MetadataMap/index.tsx
  21. 1 0
      src/pages/device/Instance/Detail/Running/Property/EditProperty.tsx
  22. 2 2
      src/pages/device/Instance/Detail/Running/Property/Indicators.tsx
  23. 59 21
      src/pages/device/Instance/Detail/Running/Property/index.tsx
  24. 5 1
      src/pages/device/Instance/Save/index.tsx
  25. 13 9
      src/pages/device/Instance/index.tsx
  26. 1 0
      src/pages/device/Product/Detail/Access/AccessConfig/index.tsx
  27. 80 64
      src/pages/device/Product/Detail/index.tsx
  28. 12 1
      src/pages/device/Product/index.tsx
  29. 10 1
      src/pages/device/Product/service.ts
  30. 3 0
      src/pages/device/components/Metadata/Base/Edit/index.tsx
  31. 2 4
      src/pages/home/components/Guide.tsx
  32. 1 0
      src/pages/home/components/index.less
  33. 30 16
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  34. 142 126
      src/pages/link/AccessConfig/index.tsx
  35. 1 1
      src/pages/link/Channel/Opcua/Save/index.tsx
  36. 2 2
      src/pages/link/Protocol/save/index.tsx
  37. 1 1
      src/pages/link/Type/Detail/index.tsx
  38. 19 14
      src/pages/link/Type/index.tsx
  39. 2 2
      src/pages/media/Cascade/Save/index.tsx
  40. 1 1
      src/pages/media/Device/Channel/index.tsx
  41. 20 12
      src/pages/media/Device/index.tsx
  42. 119 102
      src/pages/media/Stream/index.tsx
  43. 49 0
      src/pages/notice/Config/index.tsx
  44. 31 22
      src/pages/notice/Template/Debug/index.tsx
  45. 4 4
      src/pages/notice/Template/index.tsx
  46. 3 9
      src/pages/rule-engine/Alarm/Configuration/Save/index.tsx
  47. 0 181
      src/pages/rule-engine/Alarm/Log/TabComponent/index copy.less
  48. 0 307
      src/pages/rule-engine/Alarm/Log/TabComponent/index copy.tsx
  49. 1 0
      src/pages/rule-engine/Alarm/Log/TabComponent/index.less
  50. 124 108
      src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx
  51. 1 1
      src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx
  52. 6 5
      src/pages/rule-engine/Scene/Save/action/device/index.tsx
  53. 4 2
      src/pages/rule-engine/Scene/Save/index.tsx
  54. 35 19
      src/pages/system/DataSource/Management/EditTable.tsx
  55. 13 1
      src/pages/system/DataSource/Save/index.tsx
  56. 3 0
      src/pages/system/Platforms/Api/leftTree.tsx
  57. 1 2
      src/pages/system/Platforms/Api/swagger-ui/debugging.tsx
  58. 4 4
      src/pages/system/Platforms/save.tsx
  59. 1 1
      src/pages/system/Tenant/Detail/Member/Bind.tsx

+ 1 - 0
src/components/DashBoard/timePicker.tsx

@@ -72,6 +72,7 @@ export default forwardRef((props: ExtraTimePickerProps, ref) => {
         // @ts-ignore
         <DatePicker.RangePicker
           {...extraProps}
+          allowClear={false}
           showTime={props.showTime}
           value={
             value && [

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

@@ -1,5 +1,5 @@
 import { Input, Modal } from 'antd';
-import { EnvironmentOutlined } from '@ant-design/icons';
+import { FormOutlined } from '@ant-design/icons';
 import { useEffect, useState } from 'react';
 import MonacoEditor from 'react-monaco-editor';
 import { isObject } from 'lodash';
@@ -75,7 +75,7 @@ export default (props: MetaDataJsonInputProps) => {
     <>
       <Input
         addonAfter={
-          <EnvironmentOutlined
+          <FormOutlined
             onClick={() => {
               setMonacoValue(value);
               setVisible(true);

+ 1 - 1
src/components/PermissionButton/index.tsx

@@ -53,7 +53,7 @@ const PermissionButton = (props: PermissionButtonProps) => {
     if (isPermission) {
       if (popConfirm) {
         popConfirm.children = tooltip ? isTooltip : defaultButton;
-        return <Popconfirm disabled={!isPermission} {...popConfirm} />;
+        return <Popconfirm disabled={!isPermission || props.disabled} {...popConfirm} />;
       } else if (tooltip && !popConfirm) {
         return isTooltip;
       } else {

+ 20 - 8
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -50,16 +50,28 @@ export default (props: AccessConfigCardProps) => {
               {props?.channelInfo?.name && (
                 <div className="server">
                   <div className="subTitle">{props?.channelInfo?.name}</div>
-                  <div className="serverItem">
-                    {props.channelInfo?.addresses.slice(0, 1).map((i: any, index: number) => (
-                      <div className="subItem" key={i.address + `_address${index}`}>
-                        <Tooltip placement="topLeft" title={i.address}>
+                  <Tooltip
+                    placement="topLeft"
+                    title={
+                      <div>
+                        {[...props.channelInfo?.addresses].map((i: any, index: number) => (
+                          <div key={i.address + `_address${index}`}>
+                            <Badge color={i.health === -1 ? 'red' : 'green'} />
+                            {i.address}
+                          </div>
+                        ))}
+                      </div>
+                    }
+                  >
+                    <div className="serverItem">
+                      {props.channelInfo?.addresses.slice(0, 1).map((i: any, index: number) => (
+                        <div className="subItem" key={i.address + `_address${index}`}>
                           <Badge color={i.health === -1 ? 'red' : 'green'} />
                           {i.address}
-                        </Tooltip>
-                      </div>
-                    ))}
-                  </div>
+                        </div>
+                      ))}
+                    </div>
+                  </Tooltip>
                 </div>
               )}
               {props.protocolDetail?.name && (

+ 10 - 6
src/components/ProTableCard/CardItems/networkCard.tsx

@@ -19,23 +19,27 @@ export default (props: NoticeCardProps) => {
   const createDetail = () => {
     const record = props;
     if (record.shareCluster) {
-      const publicHost = record.configuration.publicHost;
-      const publicPort = record.configuration.publicPort;
-      return publicHost ? (
+      const host = record.configuration.publicHost || record.configuration.remoteHost;
+      const port = record.configuration.publicPort || record.configuration.remotePort;
+      return host ? (
         <>
           {networkMap[record.type]}
-          {publicHost}:{publicPort}
+          {host}:{port}
         </>
       ) : null;
     } else {
       const log = record.cluster?.map(
-        (item) => `${item.configuration.publicHost}:${item.configuration.publicPort}`,
+        (item) =>
+          `${item.configuration.publicHost || record.configuration.remoteHost}:${
+            item.configuration.publicPort || record.configuration.remotePort
+          }`,
       );
       return (
         <>
           {log.map((item) => (
             <div key={item}>
-              `${networkMap[record.type]}${item}`
+              {networkMap[record.type]}
+              {item}
             </div>
           ))}
         </>

+ 1 - 1
src/components/ProTableCard/CardItems/product.tsx

@@ -136,7 +136,7 @@ export default (props: ProductCardProps) => {
             </div>
             <div>
               <label>接入方式</label>
-              <div className={'ellipsis'}>{props.transportProtocol || '未接入'}</div>
+              <div className={'ellipsis'}>{props.protocolName || '未接入'}</div>
             </div>
           </div>
         </div>

+ 9 - 0
src/components/ProTableCard/index.less

@@ -217,6 +217,15 @@
       cursor: pointer;
       transition: all 0.3s;
 
+      > div {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        padding: 0 !important;
+      }
+
       &.show {
         background-color: rgba(#000, 0.5);
         visibility: visible;

+ 1 - 1
src/locales/zh-CN/pages.ts

@@ -57,7 +57,7 @@ export default {
   'pages.form.tip.max64': '最多输入64个字符',
   'pages.form.tip.id': '请输入英文或者数字或者-或者_',
   'pages.form.tooltip.id': '若不填写,系统将自动生成唯一ID',
-  'pages.form.tip.existsID': 'ID重复',
+  'pages.form.tip.existsID': '该ID已存在',
   'pages.form.tip.input.props': '请输入{name}',
   'pages.form.tip.select.props': '请选择{name}',
 

+ 1 - 2
src/pages/Northbound/AliCloud/Detail/index.tsx

@@ -11,8 +11,7 @@ import {
   Select,
 } from '@formily/antd';
 import type { Field } from '@formily/core';
-import { onFormInit } from '@formily/core';
-import { createForm, FormPath, onFieldChange, onFieldValueChange } from '@formily/core';
+import { createForm, FormPath, onFieldChange, onFieldValueChange, onFormInit } from '@formily/core';
 import { createSchemaField, observer } from '@formily/react';
 import { Card, Col, Image, Row } from 'antd';
 import { useMemo } from 'react';

+ 43 - 3
src/pages/Northbound/DuerOS/Detail/index.tsx

@@ -5,11 +5,13 @@ import { Card, Col, Row } from 'antd';
 import {
   ArrayCollapse,
   ArrayTable,
+  DatePicker,
   Form,
   FormButtonGroup,
   FormGrid,
   FormItem,
   Input,
+  NumberPicker,
   PreviewText,
   Select,
 } from '@formily/antd';
@@ -29,6 +31,7 @@ import { Store } from 'jetlinks-store';
 import { useParams } from 'umi';
 import Doc from '@/pages/Northbound/DuerOS/Detail/Doc';
 import _ from 'lodash';
+import FUpload from '@/components/Upload';
 
 const Save = () => {
   const SchemaField = createSchemaField({
@@ -99,7 +102,7 @@ const Save = () => {
             );
             const value = (field as Field).value;
 
-            const title = actions.find((item: any) => item.id === value)?.name;
+            const title = actions?.find((item: any) => item.id === value)?.name;
             f.setFieldState(actionPath, (state) => {
               state.componentProps = {
                 header: title || '动作映射',
@@ -122,7 +125,7 @@ const Save = () => {
               const product = field.query('id').value();
               const _functionList = findProductMetadata(product)?.functions;
               const _function =
-                _functionList && _functionList.find((item: any) => item.id === functionId);
+                _functionList && _functionList?.find((item: any) => item.id === functionId);
               form1.setFieldState(field.query('.inputs'), (state) => {
                 state.value = _function?.inputs.map((item: any) => ({
                   ...item,
@@ -141,7 +144,7 @@ const Save = () => {
               (index) => `propertyMappings.${index}`,
             );
             const value = (field as Field).value;
-            const title = propertiesList.find((item: any) => item.id === value)?.name;
+            const title = propertiesList?.find((item: any) => item.id === value)?.name;
             f.setFieldState(propertyMappingPath, (state) => {
               state.componentProps = {
                 header: title || '属性映射',
@@ -152,6 +155,39 @@ const Save = () => {
             const product = field.query('id').value();
             (field as Field).setDataSource(findProductMetadata(product)?.properties);
           });
+          onFieldReact('actionMappings.*.layout.command.message.inputs.*.valueType', (field) => {
+            const value = (field as Field).value;
+            const format = field.query('.value').take() as any;
+            if (!format) return;
+            switch (value) {
+              case 'date':
+                format.setComponent(DatePicker);
+                break;
+              case 'string':
+                format.setComponent(Input);
+                break;
+              case 'number':
+              case 'int':
+                format.setComponent(NumberPicker);
+                break;
+              case 'boolean':
+                format.setComponent(Select);
+                format.setDataSource([
+                  { label: '是', value: true },
+                  { label: '否', value: false },
+                ]);
+                break;
+              case 'file':
+                format.setComponent(FUpload, {
+                  type: 'file',
+                });
+                break;
+
+              case 'other':
+                format.setComponent(Input);
+                break;
+            }
+          });
         },
       }),
     [],
@@ -246,6 +282,10 @@ const Save = () => {
                 label: 'name',
                 value: 'id',
               },
+              showSearch: true,
+              showArrow: true,
+              filterOption: (input: string, option: any) =>
+                option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0,
             },
             'x-reactions': '{{useAsyncDataSource(getTypes)}}',
             required: true,

+ 23 - 2
src/pages/account/NotificationRecord/index.tsx

@@ -1,7 +1,7 @@
 import { useIntl } from '@/.umi/plugin-locale/localeExports';
 import PermissionButton from '@/components/PermissionButton';
 import SearchComponent from '@/components/SearchComponent';
-import { ReadOutlined, SearchOutlined } from '@ant-design/icons';
+import Icon, { SearchOutlined } from '@ant-design/icons';
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
@@ -12,6 +12,7 @@ import Service from './service';
 import encodeQuery from '@/utils/encodeQuery';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
+import type { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
 
 export const service = new Service('notifications');
 
@@ -34,6 +35,25 @@ const NotificationRecord = () => {
     });
   }, []);
 
+  const ReadSvg = () => (
+    <svg
+      width="1em"
+      height="1em"
+      viewBox="0 0 24 24"
+      fill="none"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <path
+        d="M12 18H6L2 22V2C2 2 2.9 2 4 2H20C21.1 2 22 2 22 2V11H20V4H4V16H12V18ZM23 14.34L21.59 12.93L17.35 17.17L15.23 15.05L13.82 16.46L17.34 20L23 14.34Z"
+        fill="currentColor"
+      />
+    </svg>
+  );
+
+  const ReadIcon = (props: Partial<CustomIconComponentProps>) => (
+    <Icon component={ReadSvg} {...props} />
+  );
+
   const columns: ProColumns<NotifitionRecord>[] = [
     {
       dataIndex: 'topicProvider',
@@ -112,7 +132,8 @@ const NotificationRecord = () => {
             },
           }}
         >
-          <ReadOutlined />
+          {/* <ReadOutlined /> */}
+          <ReadIcon />
         </PermissionButton>,
         <PermissionButton
           key={'action'}

+ 3 - 0
src/pages/account/NotificationRecord/mark_chat_read.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12 18H6L2 22V2C2 2 2.9 2 4 2H20C21.1 2 22 2 22 2V11H20V4H4V16H12V18ZM23 14.34L21.59 12.93L17.35 17.17L15.23 15.05L13.82 16.46L17.34 20L23 14.34Z" fill="#323232"/>
+</svg>

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

@@ -1,12 +1,12 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import BaseService from '@/utils/BaseService';
 import { useRef } from 'react';
-import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
 import moment from 'moment';
 import { Form, Input, Modal, Tag, Tooltip } from 'antd';
 import { CheckOutlined, EyeOutlined } from '@ant-design/icons';
 import { useIntl } from '@@/plugin-locale/localeExports';
-import ProTable from '@jetlinks/pro-table';
 import { request } from 'umi';
 import SystemConst from '@/utils/const';
 import { onlyMessage } from '@/utils/util';

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

@@ -1,13 +1,13 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { useRef } from 'react';
-import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
 import type { CommandItem } from '@/pages/device/Command/typings';
 import { Button, Tooltip } from 'antd';
 import moment from 'moment';
 import { EyeOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import Service from '@/pages/device/Command/service';
-import ProTable from '@jetlinks/pro-table';
 import Create from '@/pages/device/Command/create';
 import encodeQuery from '@/utils/encodeQuery';
 import { model } from '@formily/reactive';

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

@@ -1,5 +1,6 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
 import { Button, Popconfirm, Tooltip } from 'antd';
 import moment from 'moment';
 import { useRef } from 'react';
@@ -7,7 +8,6 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { EditOutlined, EyeOutlined, MinusOutlined, PlusOutlined } from '@ant-design/icons';
 import { Link } from 'umi';
 import { model } from '@formily/reactive';
-import ProTable from '@jetlinks/pro-table';
 import { observer } from '@formily/react';
 import type { FirmwareItem, TaskItem } from '@/pages/device/Firmware/typings';
 import Service from '@/pages/device/Firmware/service';

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

@@ -151,34 +151,35 @@ const Config = () => {
       </div>
       <div>
         {(metadata || []).map((i) => (
-          <Descriptions
-            size="small"
-            column={3}
-            key={i.name}
-            bordered
-            title={<h4 style={{ fontSize: 15 }}>{i.name}</h4>}
-          >
-            {(i?.properties || []).map((item: any) => (
-              <Descriptions.Item
-                span={1}
-                label={
-                  item?.description ? (
-                    <div>
-                      <span style={{ marginRight: '10px' }}>{item.name}</span>
-                      <Tooltip title={item.description}>
-                        <QuestionCircleOutlined />
-                      </Tooltip>
-                    </div>
-                  ) : (
-                    item.name
-                  )
-                }
-                key={item.property}
-              >
-                {renderComponent(item)}
-              </Descriptions.Item>
-            ))}
-          </Descriptions>
+          <div key={i.name} style={{ marginBottom: 20 }}>
+            <Descriptions
+              size="small"
+              column={3}
+              bordered
+              title={<h4 style={{ fontSize: 15 }}>{i.name}</h4>}
+            >
+              {(i?.properties || []).map((item: any) => (
+                <Descriptions.Item
+                  span={1}
+                  label={
+                    item?.description ? (
+                      <div>
+                        <span style={{ marginRight: '10px' }}>{item.name}</span>
+                        <Tooltip title={item.description}>
+                          <QuestionCircleOutlined />
+                        </Tooltip>
+                      </div>
+                    ) : (
+                      item.name
+                    )
+                  }
+                  key={item.property}
+                >
+                  {renderComponent(item)}
+                </Descriptions.Item>
+              ))}
+            </Descriptions>
+          </div>
         ))}
       </div>
       {visible && (

+ 75 - 12
src/pages/device/Instance/Detail/Functions/form.tsx

@@ -1,6 +1,6 @@
 import type { FunctionMetadata } from '@/pages/device/Product/typings';
 import React, { useEffect, useRef, useState } from 'react';
-import { Button, DatePicker, Input, InputNumber, Select } from 'antd';
+import { Button, DatePicker, Input, InputNumber, Select, Tooltip } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import type { ProColumns } from '@jetlinks/pro-table';
 import { EditableProTable } from '@jetlinks/pro-table';
@@ -10,6 +10,7 @@ import { InstanceModel, service } from '@/pages/device/Instance';
 import moment from 'moment';
 import './index.less';
 import { GeoPoint, MetadataJsonInput } from '@/components';
+import { QuestionCircleOutlined } from '@ant-design/icons';
 
 type FunctionProps = {
   data: FunctionMetadata;
@@ -82,22 +83,88 @@ export default (props: FunctionProps) => {
   const columns: ProColumns<FunctionTableDataType>[] = [
     {
       dataIndex: 'name',
-      title: '名称',
+      title: '参数名称',
       width: 120,
       editable: false,
       ellipsis: true,
     },
     {
       dataIndex: 'type',
-      title: '类型',
+      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: intl.formatMessage({
-        id: 'pages.data.option',
-        defaultMessage: '操作',
-      }),
+      title: '值',
       dataIndex: 'value',
       align: 'center',
       renderFormItem: (_, row: any) => {
@@ -111,7 +178,6 @@ export default (props: FunctionProps) => {
     const properties = data.valueType ? data.valueType.properties : data.inputs;
 
     for (const datum of properties) {
-      console.log(datum);
       const type = datum.valueType ? datum.valueType.type : '-';
 
       array.push({
@@ -191,10 +257,7 @@ export default (props: FunctionProps) => {
               });
             }}
           >
-            {intl.formatMessage({
-              id: 'pages.data.option.cancel',
-              defaultMessage: '取消',
-            })}
+            清空
           </Button>
         </div>
       </div>

+ 17 - 10
src/pages/device/Instance/Detail/Functions/index.tsx

@@ -5,6 +5,7 @@ import FnForm from './form';
 import AModel from './AdvancedMode';
 import { useDomFullHeight } from '@/hooks';
 import Empty from '@/pages/device/components/Empty';
+import { ExclamationCircleOutlined } from '@ant-design/icons';
 
 const Functions = () => {
   const functionList = JSON.parse(InstanceModel.detail.metadata || '{}')
@@ -17,16 +18,22 @@ const Functions = () => {
       {functionList ? (
         <Tabs>
           <Tabs.TabPane tab={'精简模式'} key={1}>
-            <Tabs tabPosition="left">
-              {functionList &&
-                functionList.map((fn) => {
-                  return (
-                    <Tabs.TabPane tab={fn.name} key={fn.id}>
-                      <FnForm data={fn} />
-                    </Tabs.TabPane>
-                  );
-                })}
-            </Tabs>
+            <>
+              <div style={{ paddingBottom: 12 }}>
+                <ExclamationCircleOutlined style={{ marginRight: 12 }} />
+                精简模式下参数只支持已输入框的方式录入
+              </div>
+              <Tabs tabPosition="left">
+                {functionList &&
+                  functionList.map((fn) => {
+                    return (
+                      <Tabs.TabPane tab={fn.name} key={fn.id}>
+                        <FnForm data={fn} />
+                      </Tabs.TabPane>
+                    );
+                  })}
+              </Tabs>
+            </>
           </Tabs.TabPane>
           <Tabs.TabPane tab={'高级模式'} key={2}>
             <Tabs tabPosition="left">

+ 8 - 2
src/pages/device/Instance/Detail/Info/index.tsx

@@ -6,7 +6,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import Config from '@/pages/device/Instance/Detail/Config';
 import Reation from '@/pages/device/Instance/Detail/Reation';
 import Save from '../../Save';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import type { DeviceInstance } from '../../typings';
 import { EditOutlined } from '@ant-design/icons';
 import Tags from '@/pages/device/Instance/Detail/Tags';
@@ -19,6 +19,10 @@ const Info = observer(() => {
   const { permission } = PermissionButton.usePermission('device/Instance');
   const { minHeight } = useDomFullHeight(`.device-detail-body`);
 
+  useEffect(() => {
+    console.log(InstanceModel.detail);
+  }, [InstanceModel.detail]);
+
   return (
     <>
       <Card className={'device-detail-body'} style={{ minHeight }}>
@@ -103,7 +107,8 @@ const Info = observer(() => {
               defaultMessage: '注册时间',
             })}
           >
-            {moment(InstanceModel.detail?.registerTime).format('YYYY-MM-DD HH:mm:ss')}
+            {InstanceModel.detail?.registerTime &&
+              moment(InstanceModel.detail?.registerTime).format('YYYY-MM-DD HH:mm:ss')}
           </Descriptions.Item>
           <Descriptions.Item
             label={intl.formatMessage({
@@ -141,6 +146,7 @@ const Info = observer(() => {
             InstanceModel.detail = {
               ...InstanceModel.detail,
               ...data,
+              describe: data.description,
             };
           }
         }}

+ 6 - 2
src/pages/device/Instance/Detail/MetadataMap/index.tsx

@@ -134,7 +134,6 @@ const MetadataMap = (props: Props) => {
           </span>
         );
       }
-
       if (!description) {
         return <EditableTable data={data} type={type} />;
       } else {
@@ -157,7 +156,12 @@ const MetadataMap = (props: Props) => {
   }, [props.type]);
 
   return (
-    <Card bordered={false} className="device-detail-metadataMap" style={{ minHeight }}>
+    <Card
+      bordered={false}
+      bodyStyle={{ padding: type === 'device' ? 24 : 0 }}
+      className="device-detail-metadataMap"
+      style={{ minHeight }}
+    >
       {renderComponent()}
     </Card>
   );

+ 1 - 0
src/pages/device/Instance/Detail/Running/Property/EditProperty.tsx

@@ -5,6 +5,7 @@ import { createSchemaField, FormProvider } from '@formily/react';
 import { service } from '@/pages/device/Instance';
 import { useParams } from 'umi';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
+import { onlyMessage } from '@/utils/util';
 
 interface Props {
   data: Partial<PropertyMetadata>;

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

@@ -180,10 +180,10 @@ const Indicators = (props: Props) => {
       service.queryMetric(InstanceModel.detail.id || '', data.id || '').then((resp) => {
         if (resp.status === 200) {
           if ((resp?.result || []).length > 0) {
-            const list = resp.result.map((item: any) => {
+            const list = resp?.result.map((item: any) => {
               return {
                 ...item,
-                value: item.value.split(','),
+                value: Array.isArray(item?.value) ? item?.value : item?.value?.split(','),
               };
             });
             setMetrics(list);

+ 59 - 21
src/pages/device/Instance/Detail/Running/Property/index.tsx

@@ -1,9 +1,14 @@
-import { Col, Empty, Input, Pagination, Row, Space, Spin, Table } from 'antd';
+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 type { PropertyMetadata } from '@/pages/device/Product/typings';
 import PropertyCard from './PropertyCard';
-import { EditOutlined, SyncOutlined, UnorderedListOutlined } from '@ant-design/icons';
+import {
+  ClockCircleOutlined,
+  EditOutlined,
+  SyncOutlined,
+  UnorderedListOutlined,
+} from '@ant-design/icons';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
@@ -14,6 +19,7 @@ import styles from './index.less';
 import { groupBy, throttle, toArray } from 'lodash';
 import PropertyTable from './PropertyTable';
 import { onlyMessage } from '@/utils/util';
+import Indicators from './Indicators';
 
 interface Props {
   data: Partial<PropertyMetadata>[];
@@ -44,6 +50,7 @@ const Property = (props: Props) => {
     pageSize: 8,
     currentPage: 0,
   });
+  const [indicatorVisible, setIndicatorVisible] = useState<boolean>(false);
   const [loading, setLoading] = useState<boolean>(true);
 
   const [check, setCheck] = useState<boolean>(true);
@@ -83,30 +90,53 @@ const Property = (props: Props) => {
       key: 'action',
       render: (text: any, record: any) => (
         <Space size="middle">
-          {(record.expands?.readOnly === false || record.expands?.readOnly === 'false') && (
+          {record.expands?.type?.includes('write') && (
+            <Tooltip placement="top" title="设置属性至设备">
+              <a
+                onClick={() => {
+                  setVisible(true);
+                }}
+              >
+                <EditOutlined />
+              </a>
+            </Tooltip>
+          )}
+          {(record.expands?.metrics || []).length > 0 &&
+            ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(
+              record.valueType?.type || '',
+            ) && (
+              <Tooltip placement="top" title="指标">
+                <a
+                  onClick={() => {
+                    setIndicatorVisible(true);
+                    setCurrentInfo(record);
+                  }}
+                >
+                  <ClockCircleOutlined />
+                </a>
+              </Tooltip>
+            )}
+          {record.expands?.type?.includes('read') && (
+            <Tooltip placement="top" title="获取最新属性值">
+              <a
+                onClick={() => {
+                  refreshProperty(record?.id);
+                }}
+              >
+                <SyncOutlined />
+              </a>
+            </Tooltip>
+          )}
+          <Tooltip placement="top" title="详情">
             <a
               onClick={() => {
-                setVisible(true);
+                setCurrentInfo(record);
+                setInfoVisible(true);
               }}
             >
-              <EditOutlined />
+              <UnorderedListOutlined />
             </a>
-          )}
-          <a
-            onClick={() => {
-              refreshProperty(record?.id);
-            }}
-          >
-            <SyncOutlined />
-          </a>
-          <a
-            onClick={() => {
-              setCurrentInfo(record);
-              setInfoVisible(true);
-            }}
-          >
-            <UnorderedListOutlined />
-          </a>
+          </Tooltip>
         </Space>
       ),
     },
@@ -318,6 +348,14 @@ const Property = (props: Props) => {
         />
       )}
       {infoVisible && <PropertyLog data={currentInfo} close={() => setInfoVisible(false)} />}
+      {indicatorVisible && (
+        <Indicators
+          data={currentInfo}
+          onCancel={() => {
+            setIndicatorVisible(false);
+          }}
+        />
+      )}
     </div>
   );
 };

+ 5 - 1
src/pages/device/Instance/Save/index.tsx

@@ -37,6 +37,9 @@ const Save = (props: Props) => {
       .getProductList(
         encodeQuery({
           paging: false,
+          sorts: {
+            createTime: 'desc',
+          },
           terms: {
             state: 1,
           },
@@ -222,6 +225,7 @@ const Save = (props: Props) => {
               <Select
                 showSearch
                 options={productList}
+                disabled={props.model === 'edit'}
                 onSelect={(_: any, node: any) => {
                   form.setFieldsValue({
                     productName: node.label,
@@ -238,7 +242,7 @@ const Save = (props: Props) => {
         </Row>
         <Row>
           <Col span={24}>
-            <Form.Item label={intlFormat('pages.table.description', '说明')} name={'describe'}>
+            <Form.Item label={intlFormat('pages.table.description', '说明')} name={'description'}>
               <Input.TextArea
                 placeholder={
                   intlFormat('pages.form.tip.input', '请输入') +

+ 13 - 9
src/pages/device/Instance/index.tsx

@@ -250,7 +250,9 @@ const Instance = () => {
       dataIndex: 'registryTime',
       width: '200px',
       valueType: 'dateTime',
-      render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
+      render: (_: any, row) => {
+        return row.registryTime ? moment(row.registryTime).format('YYYY-MM-DD HH:mm:ss') : '/';
+      },
       sorter: true,
     },
     {
@@ -662,14 +664,16 @@ const Instance = () => {
                   disabled: record.state.value !== 'notActive',
                   onConfirm: async () => {
                     if (record.state.value === 'notActive') {
-                      await service.remove(record.id);
-                      onlyMessage(
-                        intl.formatMessage({
-                          id: 'pages.data.option.success',
-                          defaultMessage: '操作成功!',
-                        }),
-                      );
-                      actionRef.current?.reload();
+                      const resp: any = await service.remove(record.id);
+                      if (resp.code === 200) {
+                        onlyMessage(
+                          intl.formatMessage({
+                            id: 'pages.data.option.success',
+                            defaultMessage: '操作成功!',
+                          }),
+                        );
+                        actionRef.current?.reload();
+                      }
                     } else {
                       onlyMessage(
                         intl.formatMessage({ id: 'pages.device.instance.deleteTip' }),

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

@@ -129,6 +129,7 @@ const AccessConfig = (props: Props) => {
         <SearchComponent
           field={columns}
           enableSave={false}
+          model="simple"
           onSearch={(data: any) => {
             const dt = {
               pageSize: 4,

+ 80 - 64
src/pages/device/Product/Detail/index.tsx

@@ -33,6 +33,76 @@ const ProductDetail = observer(() => {
   const location = useLocation();
   const history = useHistory();
 
+  const initList = [
+    {
+      key: 'base',
+      tab: intl.formatMessage({
+        id: 'pages.device.productDetail.base',
+        defaultMessage: '配置信息',
+      }),
+      component: (
+        <BaseInfo
+          onJump={(type) => {
+            if (type) {
+              setMode(type);
+            }
+          }}
+        />
+      ),
+    },
+    {
+      key: 'metadata',
+      tab: (
+        <>
+          {intl.formatMessage({
+            id: 'pages.device.instanceDetail.metadata',
+            defaultMessage: '物模型',
+          })}
+          <Tooltip
+            title={
+              <>
+                属性:
+                <br />
+                用于描述设备运行时具体信息和状态。
+                <br />
+                功能:
+                <br />
+                指设备可供外部调用的指令或方法。
+                <br />
+                事件:
+                <br />
+                设备运行时,主动上报给云端的信息。
+                <br />
+                标签:
+                <br />
+                统一为设备添加拓展字段,添加后将在设备信息页显示。
+              </>
+            }
+          >
+            <QuestionCircleOutlined style={{ marginLeft: 5 }} />
+          </Tooltip>
+        </>
+      ),
+      component: <Metadata type="product" />,
+    },
+    {
+      key: 'access',
+      tab: '设备接入',
+      component: <Access />,
+    },
+  ];
+
+  const pList = [
+    'websocket-server',
+    'http-server-gateway',
+    'udp-device-gateway',
+    'coap-server-gateway',
+    'mqtt-client-gateway',
+    'mqtt-server-gateway',
+    'tcp-server-gateway',
+  ];
+  const [list, setList] = useState<any[]>([...initList]);
+
   const { minHeight } = useDomFullHeight('.product-detail-body');
 
   const { permission } = PermissionButton.usePermission('device/Product');
@@ -74,6 +144,16 @@ const ProductDetail = observer(() => {
         const metadata: DeviceMetadata = JSON.parse(data.metadata);
         MetadataAction.insert(metadata);
       }
+      if (data?.accessProvider && pList.includes(data?.accessProvider)) {
+        setList([
+          ...initList,
+          {
+            key: 'metadata-map',
+            tab: '物模型映射',
+            component: <MetadataMap type="product" />,
+          },
+        ]);
+      }
       service.instanceCount(encodeQuery({ terms: { productId: param?.id } })).then((res: any) => {
         if (res.status === 200) {
           productModel.current = { ...data, count: res.result };
@@ -146,70 +226,6 @@ const ProductDetail = observer(() => {
     return subscription.unsubscribe;
   }, [changeDeploy, param.id]);
 
-  const list = [
-    {
-      key: 'base',
-      tab: intl.formatMessage({
-        id: 'pages.device.productDetail.base',
-        defaultMessage: '配置信息',
-      }),
-      component: (
-        <BaseInfo
-          onJump={(type) => {
-            if (type) {
-              setMode(type);
-            }
-          }}
-        />
-      ),
-    },
-    {
-      key: 'metadata',
-      tab: (
-        <>
-          {intl.formatMessage({
-            id: 'pages.device.instanceDetail.metadata',
-            defaultMessage: '物模型',
-          })}
-          <Tooltip
-            title={
-              <>
-                属性:
-                <br />
-                用于描述设备运行时具体信息和状态。
-                <br />
-                功能:
-                <br />
-                指设备可供外部调用的指令或方法。
-                <br />
-                事件:
-                <br />
-                设备运行时,主动上报给云端的信息。
-                <br />
-                标签:
-                <br />
-                统一为设备添加拓展字段,添加后将在设备信息页显示。
-              </>
-            }
-          >
-            <QuestionCircleOutlined style={{ marginLeft: 5 }} />
-          </Tooltip>
-        </>
-      ),
-      component: <Metadata type="product" />,
-    },
-    {
-      key: 'access',
-      tab: '设备接入',
-      component: <Access />,
-    },
-    {
-      key: 'metadata-map',
-      tab: '物模型映射',
-      component: <MetadataMap type="product" />,
-    },
-  ];
-
   useEffect(() => {
     const { state } = location;
     if (state && state?.tab) {

+ 12 - 1
src/pages/device/Product/index.tsx

@@ -258,8 +258,19 @@ const Product = observer(() => {
     },
     {
       title: '接入方式',
-      dataIndex: 'transportProtocol',
+      dataIndex: 'accessId',
       width: 150,
+      valueType: 'select',
+      render: (_, row) => {
+        return row.protocolName;
+      },
+      request: () =>
+        service.queryGatewayList().then((resp: any) =>
+          resp.result.map((item: any) => ({
+            label: item.name,
+            value: item.id,
+          })),
+        ),
     },
     {
       title: '设备类型',

+ 10 - 1
src/pages/device/Product/service.ts

@@ -111,7 +111,7 @@ class Service extends BaseService<ProductItem> {
   public category = () =>
     request(`/${SystemConst.API_BASE}/device/category/_tree?paging=false`, {
       method: 'GET',
-      params: encodeQuery({ sorts: { id: 'desc' } }),
+      params: encodeQuery({ sorts: { sortIndex: 'asc' } }),
     });
 
   public getOrg = () =>
@@ -150,6 +150,15 @@ class Service extends BaseService<ProductItem> {
     );
 
   public existsID = (id: string) => request(`${this.uri}/${id}/exists`, { method: 'GET' });
+
+  //接入网关
+  public queryGatewayList = () =>
+    request(`/${SystemConst.API_BASE}/gateway/device/_query/no-paging`, {
+      method: 'POST',
+      data: {
+        paging: false,
+      },
+    });
 }
 
 export default Service;

+ 3 - 0
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -1057,6 +1057,9 @@ const Edit = observer((props: Props) => {
         }
         MetadataModel.edit = false;
         MetadataModel.item = {};
+        if (InstanceModel.detail) {
+          InstanceModel.detail.independentMetadata = true;
+        }
       }
     } else {
       onlyMessage('操作失败!', 'error');

+ 2 - 4
src/pages/home/components/Guide.tsx

@@ -1,5 +1,4 @@
 import './index.less';
-import { getMenuPathByCode } from '@/utils/menu';
 import { message } from 'antd';
 import useHistory from '@/hooks/route/useHistory';
 import Title from './Title';
@@ -26,12 +25,11 @@ interface GuideItemProps {
 }
 
 const GuideItem = (props: GuideItemProps) => {
-  const path = getMenuPathByCode(props.url);
   const history = useHistory();
 
   const jumpPage = () => {
-    if (path && props.auth) {
-      history.push(`${path}`, props.param);
+    if (props.url && props.auth) {
+      history.push(`${props.url}`, props.param);
     } else {
       message.warning('暂无权限,请联系管理员');
     }

+ 1 - 0
src/pages/home/components/index.less

@@ -26,6 +26,7 @@
   background: linear-gradient(135.62deg, #f6f7fd 22.27%, rgba(255, 255, 255, 0.86) 91.82%);
   border-radius: 2px;
   box-shadow: 0 4px 18px #efefef;
+  cursor: pointer;
 
   .item-english {
     color: #4f4f4f;

+ 30 - 16
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -11,7 +11,7 @@ import TitleComponent from '@/components/TitleComponent';
 import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
-import { MetworkTypeMapping, ProcotoleMapping, descriptionList } from './data';
+import { descriptionList, MetworkTypeMapping, ProcotoleMapping } from './data';
 
 interface Props {
   change: () => void;
@@ -307,22 +307,36 @@ const Access = (props: Props) => {
                         </Tooltip>
                       </div>
                       <div className={styles.cardContent}>
-                        <div
-                          style={{
-                            width: '100%',
-                            height: '20px',
-                            display: 'flex',
-                            flexDirection: 'column',
-                            alignItems: 'center',
-                            justifyContent: 'center',
-                          }}
-                        >
-                          {item.addresses.slice(0, 1).map((i: any) => (
-                            <div className={styles.item} key={i.address}>
-                              <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
+                        <Tooltip
+                          placement="topLeft"
+                          title={
+                            <div>
+                              {[...item.addresses].map((i: any) => (
+                                <div key={i.address}>
+                                  <Badge color={i.health === -1 ? 'red' : 'green'} />
+                                  {i.address}
+                                </div>
+                              ))}
                             </div>
-                          ))}
-                        </div>
+                          }
+                        >
+                          <div
+                            style={{
+                              width: '100%',
+                              height: '20px',
+                              display: 'flex',
+                              flexDirection: 'column',
+                              alignItems: 'center',
+                              justifyContent: 'center',
+                            }}
+                          >
+                            {item.addresses.slice(0, 1).map((i: any) => (
+                              <div className={styles.item} key={i.address}>
+                                <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
+                              </div>
+                            ))}
+                          </div>
+                        </Tooltip>
                         <div className={styles.desc}>
                           <Tooltip
                             placement="topLeft"

+ 142 - 126
src/pages/link/AccessConfig/index.tsx

@@ -8,7 +8,7 @@ import { useHistory } from 'umi';
 import Service from './service';
 import { DeleteOutlined, EditOutlined, PlayCircleOutlined, StopOutlined } from '@ant-design/icons';
 import AccessConfigCard from '@/components/ProTableCard/CardItems/AccessConfig';
-import { PermissionButton, Empty } from '@/components';
+import { Empty, PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 import { Store } from 'jetlinks-store';
 import { onlyMessage } from '@/utils/util';
@@ -20,7 +20,7 @@ const AccessConfig = () => {
   const [param, setParam] = useState<any>({ pageSize: 10, terms: [] });
   const { permission } = PermissionButton.usePermission('link/AccessConfig');
 
-  const { minHeight } = useDomFullHeight(`.link-accessConfig`);
+  const { minHeight } = useDomFullHeight(`.link-accessConfig`, 36);
 
   const columns: ProColumns<any>[] = [
     {
@@ -102,133 +102,149 @@ const AccessConfig = () => {
           handleSearch(dt);
         }}
       />
-      <Card className={'link-accessConfig'} style={{ minHeight }}>
-        <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
-          <PermissionButton
-            isPermission={permission.add}
-            onClick={() => {
-              history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}`);
-            }}
-            key="button"
-            type="primary"
-          >
-            新增
-          </PermissionButton>
-        </div>
-        {dataSource?.data.length > 0 ? (
-          <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
-            {(dataSource?.data || []).map((item: any) => (
-              <Col key={item.id} span={12}>
-                <AccessConfigCard
-                  {...item}
-                  actions={[
-                    <PermissionButton
-                      isPermission={permission.update}
-                      onClick={() => {
-                        history.push(
-                          `${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}?id=${
-                            item.id
-                          }`,
-                        );
-                      }}
-                      key="button"
-                      type="link"
-                    >
-                      <EditOutlined />
-                      编辑
-                    </PermissionButton>,
-                    <PermissionButton
-                      type={'link'}
-                      key={'state'}
-                      style={{ padding: 0 }}
-                      popConfirm={{
-                        title: `确认${item.state.value !== 'disabled' ? '禁用' : '启用'}`,
-                        onConfirm: () => {
-                          if (item.state.value !== 'disabled') {
-                            service.shutDown(item.id).then((resp) => {
-                              if (resp.status === 200) {
-                                onlyMessage('操作成功!');
-                                handleSearch(param);
-                              }
-                            });
-                          } else {
-                            service.startUp(item.id).then((resp) => {
-                              if (resp.status === 200) {
-                                onlyMessage('操作成功!');
-                                handleSearch(param);
+      <Card>
+        <div style={{ position: 'relative', minHeight }} className={'link-accessConfig'}>
+          <div style={{ height: '100%', paddingBottom: 48 }}>
+            <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
+              <PermissionButton
+                isPermission={permission.add}
+                onClick={() => {
+                  history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}`);
+                }}
+                key="button"
+                type="primary"
+              >
+                新增
+              </PermissionButton>
+            </div>
+            {dataSource?.data.length > 0 ? (
+              <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
+                {(dataSource?.data || []).map((item: any) => (
+                  <Col key={item.id} span={12}>
+                    <AccessConfigCard
+                      {...item}
+                      actions={[
+                        <PermissionButton
+                          isPermission={permission.update}
+                          onClick={() => {
+                            history.push(
+                              `${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}?id=${
+                                item.id
+                              }`,
+                            );
+                          }}
+                          key="button"
+                          type="link"
+                        >
+                          <EditOutlined />
+                          编辑
+                        </PermissionButton>,
+                        <PermissionButton
+                          type={'link'}
+                          key={'state'}
+                          style={{ padding: 0 }}
+                          popConfirm={{
+                            title: `确认${item.state.value !== 'disabled' ? '禁用' : '启用'}`,
+                            onConfirm: () => {
+                              if (item.state.value !== 'disabled') {
+                                service.shutDown(item.id).then((resp) => {
+                                  if (resp.status === 200) {
+                                    onlyMessage('操作成功!');
+                                    handleSearch(param);
+                                  }
+                                });
+                              } else {
+                                service.startUp(item.id).then((resp) => {
+                                  if (resp.status === 200) {
+                                    onlyMessage('操作成功!');
+                                    handleSearch(param);
+                                  }
+                                });
                               }
-                            });
-                          }
-                        },
-                      }}
-                      isPermission={permission.action}
-                      tooltip={{
-                        title: item.state.value !== 'disabled' ? '禁用' : '启用',
-                      }}
-                    >
-                      {item.state.value !== 'disabled' ? <StopOutlined /> : <PlayCircleOutlined />}
-                      {item.state.value !== 'disabled' ? '禁用' : '启用'}
-                    </PermissionButton>,
-                    <PermissionButton
-                      isPermission={permission.delete}
-                      disabled={item.state.value !== 'disabled'}
-                      tooltip={{
-                        title: item.state.value !== 'disabled' ? '请先禁用,再删除' : '',
-                      }}
-                      popConfirm={{
-                        title: '确认删除',
-                        disabled: item.state.value !== 'disabled',
-                        onConfirm: () => {
-                          service.remove(item.id).then((resp: any) => {
-                            if (resp.status === 200) {
-                              onlyMessage('操作成功!');
-                              handleSearch(param);
-                            } else {
-                              onlyMessage(resp?.message || '操作失败', 'error');
-                            }
-                          });
-                        },
-                      }}
-                      key="delete"
-                      type="link"
-                    >
-                      <DeleteOutlined />
-                    </PermissionButton>,
-                  ]}
-                />
-              </Col>
-            ))}
-          </Row>
-        ) : (
-          <div style={{ height: minHeight - 150 }}>
-            <Empty />
+                            },
+                          }}
+                          isPermission={permission.action}
+                          tooltip={{
+                            title: item.state.value !== 'disabled' ? '禁用' : '启用',
+                          }}
+                        >
+                          {item.state.value !== 'disabled' ? (
+                            <StopOutlined />
+                          ) : (
+                            <PlayCircleOutlined />
+                          )}
+                          {item.state.value !== 'disabled' ? '禁用' : '启用'}
+                        </PermissionButton>,
+                        <PermissionButton
+                          isPermission={permission.delete}
+                          disabled={item.state.value !== 'disabled'}
+                          tooltip={{
+                            title: item.state.value !== 'disabled' ? '请先禁用,再删除' : '',
+                          }}
+                          popConfirm={{
+                            title: '确认删除',
+                            disabled: item.state.value !== 'disabled',
+                            onConfirm: () => {
+                              service.remove(item.id).then((resp: any) => {
+                                if (resp.status === 200) {
+                                  onlyMessage('操作成功!');
+                                  handleSearch(param);
+                                } else {
+                                  onlyMessage(resp?.message || '操作失败', 'error');
+                                }
+                              });
+                            },
+                          }}
+                          key="delete"
+                          type="link"
+                        >
+                          <DeleteOutlined />
+                        </PermissionButton>,
+                      ]}
+                    />
+                  </Col>
+                ))}
+              </Row>
+            ) : (
+              <div style={{ height: minHeight - 150 }}>
+                <Empty />
+              </div>
+            )}
           </div>
-        )}
-        {dataSource.data.length > 0 && (
-          <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-            <Pagination
-              showSizeChanger
-              size="small"
-              className={'pro-table-card-pagination'}
-              total={dataSource?.total || 0}
-              current={dataSource?.pageIndex + 1}
-              onChange={(page, size) => {
-                handleSearch({
-                  ...param,
-                  pageIndex: page - 1,
-                  pageSize: size,
-                });
-              }}
-              pageSizeOptions={[10, 20, 50, 100]}
-              pageSize={dataSource?.pageSize}
-              showTotal={(num) => {
-                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+          {dataSource.data.length > 0 && (
+            <div
+              style={{
+                display: 'flex',
+                justifyContent: 'flex-end',
+                position: 'absolute',
+                width: '100%',
+                bottom: 0,
               }}
-            />
-          </div>
-        )}
+            >
+              <Pagination
+                showSizeChanger
+                size="small"
+                className={'pro-table-card-pagination'}
+                total={dataSource?.total || 0}
+                current={dataSource?.pageIndex + 1}
+                onChange={(page, size) => {
+                  handleSearch({
+                    ...param,
+                    pageIndex: page - 1,
+                    pageSize: size,
+                  });
+                }}
+                pageSizeOptions={[10, 20, 50, 100]}
+                pageSize={dataSource?.pageSize}
+                showTotal={(num) => {
+                  const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
+                  const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
+                  return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+                }}
+              />
+            </div>
+          )}
+        </div>
       </Card>
     </PageContainer>
   );

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

@@ -1,4 +1,5 @@
 import { useIntl } from 'umi';
+import type { Field } from '@formily/core';
 import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { Form, FormGrid, FormItem, Input, Select } from '@formily/antd';
@@ -8,7 +9,6 @@ import { Modal } from '@/components';
 import { useMemo } from 'react';
 import { action } from '@formily/reactive';
 import type { Response } from '@/utils/typings';
-import type { Field } from '@formily/core';
 import { onlyMessage } from '@/utils/util';
 
 interface Props {

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

@@ -8,9 +8,9 @@ import type { ISchema } from '@formily/json-schema';
 import { service } from '@/pages/link/Protocol';
 import FileUpload from '../FileUpload';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { PermissionButton } from '@/components';
-import { RadioCard } from '@/components';
+import { PermissionButton, RadioCard } from '@/components';
 import { onlyMessage } from '@/utils/util';
+
 interface Props {
   data: ProtocolItem | undefined;
   close: () => void;

+ 1 - 1
src/pages/link/Type/Detail/index.tsx

@@ -282,7 +282,7 @@ const Save = observer(() => {
         'x-decorator': 'FormItem',
         'x-component': 'Select',
         'x-component-props': {
-          placeholder: '请输入本地端口',
+          placeholder: '请选择本地端口',
         },
         'x-reactions': {
           dependencies: ['type'],

+ 19 - 14
src/pages/link/Type/index.tsx

@@ -92,25 +92,30 @@ const Network = () => {
       dataIndex: 'configuration',
       title: '详情',
       ellipsis: true,
+      hideInSearch: true,
       renderText: (text, record) => {
         if (record.shareCluster) {
-          const publicHost = record.configuration.publicHost;
-          const publicPort = record.configuration.publicPort;
-          return publicHost ? (
+          const host = record.configuration.publicHost || record.configuration.remoteHost;
+          const port = record.configuration.publicPort || record.configuration.remotePort;
+          return host ? (
             <>
               {networkMap[record.type]}
-              {publicHost}:{publicPort}
+              {host}:{port}
             </>
           ) : null;
         } else {
           const log = record.cluster?.map(
-            (item) => `${item.configuration.publicHost}:${item.configuration.publicPort}`,
+            (item) =>
+              `${item.configuration.publicHost || record.configuration.remoteHost}:${
+                item.configuration.publicPort || record.configuration.remotePort
+              }`,
           );
           return (
             <>
               {log.map((item) => (
                 <div key={item}>
-                  `${networkMap[record.type]}${item}`
+                  {networkMap[record.type]}
+                  {item}
                 </div>
               ))}
             </>
@@ -325,6 +330,7 @@ const Network = () => {
               </PermissionButton>,
               <PermissionButton
                 type="link"
+                key="other-delete"
                 style={{ padding: 0 }}
                 isPermission={networkPermission.delete}
                 disabled={record.state.value === 'enabled'}
@@ -337,19 +343,18 @@ const Network = () => {
                         })
                       : '请先禁用该组件,再删除。',
                 }}
-              >
-                <Popconfirm
-                  title="确认删除?"
-                  onConfirm={async () => {
+                popConfirm={{
+                  title: '确认删除?',
+                  onConfirm: async () => {
                     const response: any = await service.remove(record.id);
                     if (response.status === 200) {
                       onlyMessage('删除成功');
                       actionRef.current?.reload();
                     }
-                  }}
-                >
-                  <DeleteOutlined />
-                </Popconfirm>
+                  },
+                }}
+              >
+                <DeleteOutlined />
               </PermissionButton>,
             ]}
           />

+ 2 - 2
src/pages/media/Cascade/Save/index.tsx

@@ -2,10 +2,12 @@ import TitleComponent from '@/components/TitleComponent';
 import { InfoCircleFilled, QuestionCircleOutlined } from '@ant-design/icons';
 import { PageContainer } from '@ant-design/pro-layout';
 import {
+  Alert,
   Button,
   Card,
   Col,
   Form,
+  Image,
   Input,
   InputNumber,
   message,
@@ -13,8 +15,6 @@ import {
   Row,
   Select,
   Tooltip,
-  Image,
-  Alert,
 } from 'antd';
 import SipComponent from '@/components/SipComponent';
 import SipSelectComponent from '@/components/SipSelectComponent';

+ 1 - 1
src/pages/media/Device/Channel/index.tsx

@@ -12,8 +12,8 @@ import { Button, message, Popconfirm, Tooltip } from 'antd';
 import {
   DeleteOutlined,
   EditOutlined,
-  PlusOutlined,
   LeftOutlined,
+  PlusOutlined,
   VideoCameraAddOutlined,
   VideoCameraOutlined,
 } from '@ant-design/icons';

+ 20 - 12
src/pages/media/Device/index.tsx

@@ -218,7 +218,7 @@ const Device = () => {
         </PermissionButton>,
         <PermissionButton
           tooltip={{
-            title: '查看设备',
+            title: '查看通道',
           }}
           style={{ padding: 0 }}
           type={'link'}
@@ -248,15 +248,23 @@ const Device = () => {
           </Button>
         </Tooltip>,
         <PermissionButton
-          tooltip={{
-            title:
-              record.provider === ProviderValue.FIXED
-                ? '接入方式为固定地址时不支持更新通道'
-                : '更新通道',
-          }}
+          tooltip={
+            record.state.value === 'offline' || record.provider === providerType['fixed-media']
+              ? {
+                  title:
+                    record.provider === providerType['fixed-media']
+                      ? '固定地址无法更新通道'
+                      : record.state.value === 'offline'
+                      ? '设备已离线'
+                      : '',
+                }
+              : undefined
+          }
           key={'updateChannel'}
-          isPermission={permission.action}
-          disabled={record.state.value === 'offline' || record.provider === ProviderValue.FIXED}
+          isPermission={permission.update}
+          disabled={
+            record.state.value === 'offline' || record.provider === providerType['fixed-media']
+          }
           style={{ padding: 0 }}
           type={'link'}
           onClick={() => {
@@ -273,13 +281,13 @@ const Device = () => {
           popConfirm={{
             title: intl.formatMessage({
               id:
-                record.state.value === 'offline'
-                  ? 'pages.device.productDetail.deleteTip'
+                record.state.value !== 'offline'
+                  ? 'pages.device.instance.deleteTip'
                   : 'page.table.isDelete',
               defaultMessage: '是否删除?',
             }),
             onConfirm: async () => {
-              if (record.state.value !== 'offline') {
+              if (record.state.value === 'offline') {
                 await deleteItem(record.id);
               } else {
                 onlyMessage('在线设备不能进行删除操作', 'error');

+ 119 - 102
src/pages/media/Stream/index.tsx

@@ -26,7 +26,7 @@ const Stream = () => {
   const [param, setParam] = useState<any>({ pageSize: 10, terms: [] });
   const permissionCode = 'media/Stream';
   const { permission } = PermissionButton.usePermission(permissionCode);
-  const { minHeight } = useDomFullHeight(`.stream`);
+  const { minHeight } = useDomFullHeight(`.stream`, 36);
 
   const columns: ProColumns<StreamItem>[] = [
     {
@@ -77,108 +77,125 @@ const Stream = () => {
           });
         }}
       />
-      <Card className="stream" style={{ minHeight }}>
-        {dataSource.data.length > 0 ? (
-          <>
-            <PermissionButton
-              isPermission={permission.add}
-              onClick={() => {
-                history.push(`${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'])}`);
-                StreamModel.current = {};
-              }}
-              key="button"
-              icon={<PlusOutlined />}
-              type="primary"
-            >
-              新增
-            </PermissionButton>
-            <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
-              {(dataSource?.data || []).map((item: any) => (
-                <Col key={item.id} span={12}>
-                  <StreamCard
-                    {...item}
-                    actions={[
-                      <PermissionButton
-                        isPermission={permission.update}
-                        onClick={() => {
-                          history.push(
-                            `${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'], item.id)}`,
-                          );
-                          StreamModel.current = { ...item };
-                        }}
-                        key="button"
-                        type="link"
-                      >
-                        <EditOutlined />
-                        编辑
-                      </PermissionButton>,
-                      <PermissionButton
-                        isPermission={permission.delete}
-                        popConfirm={{
-                          title: '确认删除',
-                          onConfirm: () => {
-                            service.remove(item.id).then((resp: any) => {
-                              if (resp.status === 200) {
-                                onlyMessage('操作成功!');
-                                handleSearch({ pageSize: 10, terms: [] });
-                              }
-                            });
-                          },
-                        }}
-                        key="delete"
-                        type="link"
-                      >
-                        <DeleteOutlined />
-                      </PermissionButton>,
-                    ]}
-                  />
-                </Col>
-              ))}
-            </Row>
-            <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-              <Pagination
-                showSizeChanger
-                size="small"
-                className={'pro-table-card-pagination'}
-                total={dataSource?.total || 0}
-                current={dataSource?.pageIndex + 1}
-                onChange={(page, size) => {
-                  handleSearch({
-                    ...param,
-                    pageIndex: page - 1,
-                    pageSize: size,
-                  });
-                }}
-                pageSizeOptions={[10, 20, 50, 100]}
-                pageSize={dataSource?.pageSize}
-                showTotal={(num) => {
-                  const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                  const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                  return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-                }}
+      <Card>
+        <div className="stream" style={{ position: 'relative', minHeight }}>
+          <div style={{ height: '100%', paddingBottom: 48 }}>
+            {dataSource.data.length > 0 ? (
+              <div>
+                <div>
+                  <PermissionButton
+                    isPermission={permission.add}
+                    onClick={() => {
+                      history.push(`${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'])}`);
+                      StreamModel.current = {};
+                    }}
+                    key="button"
+                    icon={<PlusOutlined />}
+                    type="primary"
+                  >
+                    新增
+                  </PermissionButton>
+                  <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
+                    {(dataSource?.data || []).map((item: any) => (
+                      <Col key={item.id} span={12}>
+                        <StreamCard
+                          {...item}
+                          actions={[
+                            <PermissionButton
+                              isPermission={permission.update}
+                              onClick={() => {
+                                history.push(
+                                  `${getMenuPathByParams(
+                                    MENUS_CODE['media/Stream/Detail'],
+                                    item.id,
+                                  )}`,
+                                );
+                                StreamModel.current = { ...item };
+                              }}
+                              key="button"
+                              type="link"
+                            >
+                              <EditOutlined />
+                              编辑
+                            </PermissionButton>,
+                            <PermissionButton
+                              isPermission={permission.delete}
+                              popConfirm={{
+                                title: '确认删除',
+                                onConfirm: () => {
+                                  service.remove(item.id).then((resp: any) => {
+                                    if (resp.status === 200) {
+                                      onlyMessage('操作成功!');
+                                      handleSearch({ pageSize: 10, terms: [] });
+                                    }
+                                  });
+                                },
+                              }}
+                              key="delete"
+                              type="link"
+                            >
+                              <DeleteOutlined />
+                            </PermissionButton>,
+                          ]}
+                        />
+                      </Col>
+                    ))}
+                  </Row>
+                </div>
+              </div>
+            ) : (
+              <Empty
+                style={{ marginTop: '10%' }}
+                description={
+                  <span>
+                    暂无数据,请先
+                    <Button
+                      type="link"
+                      disabled={getButtonPermission('media/Stream', ['add'])}
+                      onClick={() => {
+                        history.push(`${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'])}`);
+                        StreamModel.current = {};
+                      }}
+                    >
+                      新增流媒体服务
+                    </Button>
+                  </span>
+                }
               />
-            </div>
-          </>
-        ) : (
-          <Empty
-            style={{ marginTop: '10%' }}
-            description={
-              <span>
-                暂无数据,请先
-                <Button
-                  type="link"
-                  disabled={getButtonPermission('media/Stream', ['add'])}
-                  onClick={() => {
-                    history.push(`${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'])}`);
-                    StreamModel.current = {};
-                  }}
-                >
-                  新增流媒体服务
-                </Button>
-              </span>
-            }
-          />
-        )}
+            )}
+          </div>
+          <div
+            style={{
+              display: 'flex',
+              justifyContent: 'flex-end',
+              position: 'absolute',
+              bottom: 0,
+              width: '100%',
+            }}
+          >
+            <Pagination
+              showSizeChanger
+              size="small"
+              className={'pro-table-card-pagination'}
+              total={dataSource?.total || 0}
+              current={dataSource?.pageIndex + 1}
+              onChange={(page, size) => {
+                handleSearch({
+                  ...param,
+                  pageIndex: page - 1,
+                  pageSize: size,
+                });
+              }}
+              pageSizeOptions={[10, 20, 50, 100]}
+              pageSize={dataSource?.pageSize}
+              showTotal={(num) => {
+                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
+                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
+                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+              }}
+            />
+          </div>
+        </div>
       </Card>
     </PageContainer>
   );

+ 49 - 0
src/pages/notice/Config/index.tsx

@@ -42,6 +42,53 @@ export const state = model<{
   log: false,
   syncUser: false,
 });
+const list = {
+  weixin: {
+    corpMessage: {
+      text: '企业消息',
+      status: 'corpMessage',
+    },
+    // officialMessage: {
+    //   text: '服务号消息',
+    //   status: 'officialMessage',
+    // },
+  },
+  dingTalk: {
+    dingTalkMessage: {
+      text: '钉钉消息',
+      status: 'dingTalkMessage',
+    },
+    dingTalkRobotWebHook: {
+      text: '群机器人消息',
+      status: 'dingTalkRobotWebHook',
+    },
+  },
+  voice: {
+    aliyun: {
+      text: '阿里云语音',
+      status: 'aliyun',
+    },
+  },
+  sms: {
+    aliyunSms: {
+      text: '阿里云短信',
+      status: 'aliyunSms',
+    },
+  },
+  email: {
+    embedded: {
+      text: '默认',
+      status: 'embedded',
+    },
+  },
+  webhook: {
+    http: {
+      text: 'Webhook',
+      status: 'http',
+    },
+  },
+};
+
 const Config = observer(() => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
@@ -62,6 +109,8 @@ const Config = observer(() => {
       dataIndex: 'provider',
       title: '通知方式',
       renderText: (text, record) => typeList[record.type][record.provider],
+      valueType: 'select',
+      valueEnum: list[id],
     },
     {
       dataIndex: 'description',

+ 31 - 22
src/pages/notice/Template/Debug/index.tsx

@@ -102,7 +102,6 @@ const Debug = observer(() => {
     // 从后端接口来获取变量参数
     service.getVariableDefinitions(state.current?.id || '').then((resp) => {
       const _template = resp.result;
-      console.log(resp, 'userEfffect', state.current);
       if (_template?.variableDefinitions?.length > 0) {
         variableRef.current = _template?.variableDefinitions;
         form.setFieldState('variableDefinitions', (state1) => {
@@ -215,6 +214,7 @@ const Debug = observer(() => {
               'x-component-props': { title: '值', width: '120px' },
               properties: {
                 value: {
+                  required: true,
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
@@ -236,27 +236,36 @@ const Debug = observer(() => {
   const [spinning, setSpinning] = useState<boolean>(false);
   const start = async () => {
     setSpinning(true);
-    const data: { configId: string; variableDefinitions: any } = await form.submit();
-    // 应该取选择的配置信息
-    if (!state.current) return;
-    const resp = await service.debug(
-      data.configId,
-      state?.current.id,
-      data.variableDefinitions?.reduce(
-        (previousValue: any, currentValue: { id: any; value: any }) => {
-          return {
-            ...previousValue,
-            [currentValue.id]: currentValue.value,
-          };
-        },
-        {},
-      ),
-    );
-    if (resp.status === 200) {
-      onlyMessage('操作成功!');
-      setSpinning(false);
-      state.debug = false;
-    }
+    // const data: { configId: string; variableDefinitions: any } = await form.submit();
+    form
+      .submit()
+      .then(async (data: any) => {
+        // 应该取选择的配置信息
+        if (!state.current) return;
+        const resp = await service.debug(
+          data.configId,
+          state?.current.id,
+          data.variableDefinitions?.reduce(
+            (previousValue: any, currentValue: { id: any; value: any }) => {
+              return {
+                ...previousValue,
+                [currentValue.id]: currentValue.value,
+              };
+            },
+            {},
+          ),
+        );
+        if (resp.status === 200) {
+          onlyMessage('操作成功!');
+          setSpinning(false);
+          state.debug = false;
+        } else {
+          setSpinning(false);
+        }
+      })
+      .catch(() => {
+        setSpinning(false);
+      });
   };
   return (
     <Modal

+ 4 - 4
src/pages/notice/Template/index.tsx

@@ -45,10 +45,10 @@ const list = {
       text: '企业消息',
       status: 'corpMessage',
     },
-    officialMessage: {
-      text: '服务号消息',
-      status: 'officialMessage',
-    },
+    // officialMessage: {
+    //   text: '服务号消息',
+    //   status: 'officialMessage',
+    // },
   },
   dingTalk: {
     dingTalkMessage: {

+ 3 - 9
src/pages/rule-engine/Alarm/Configuration/Save/index.tsx

@@ -140,6 +140,8 @@ const Save = (props: Props) => {
     }
   };
 
+  const { permission } = PermissionButton.usePermission('rule-engine/Scene');
+
   const schema: ISchema = {
     type: 'object',
     properties: {
@@ -234,20 +236,12 @@ const Save = (props: Props) => {
             <PermissionButton
               type="link"
               style={{ padding: 0 }}
-              isPermission={true}
+              isPermission={permission.add}
               onClick={() => {
                 const tab: any = window.open(`${origin}/#/iot/rule-engine/scene/Save`);
                 tab!.onTabSaveSuccess = (value: any) => {
                   form.setFieldState('sceneId', async (state) => {
                     state.dataSource = await getScene();
-                    // .then((resp) =>
-                    //   resp.result?.map((item: Record<string, unknown>) => ({
-                    //     ...item,
-                    //     label: item.name,
-                    //     value: item.id,
-                    //   })),
-                    // );
-                    console.log(value, 'value');
                     state.value = value?.result?.id;
                   });
                 };

+ 0 - 181
src/pages/rule-engine/Alarm/Log/TabComponent/index copy.less

@@ -1,181 +0,0 @@
-@import '~antd/es/style/themes/default.less';
-
-@alarm-log-padding-left: 60px;
-
-.ellipsis {
-  width: 100%;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-}
-
-.alarm-log-card {
-  .alarm-log-item {
-    display: flex;
-    margin-bottom: 20px;
-
-    .alarm-log-title {
-      position: relative;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 15%;
-      padding: 10px;
-      overflow: hidden;
-      color: @primary-color;
-      background-color: #f0f2f3;
-      .alarm-log-level {
-        position: absolute;
-        top: 10px;
-        right: -12px;
-        display: flex;
-        justify-content: center;
-        width: 100px;
-        padding: 2px 0;
-        color: white;
-        background-color: red;
-        transform: skewX(45deg);
-        .alarm-log-text {
-          transform: skewX(-45deg);
-        }
-      }
-      .alarm-log-title-text {
-        margin: 0 10px;
-        .ellipsis();
-      }
-    }
-    .alarm-log-content {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      width: 87%;
-      padding: 20px;
-      background: url('/images/alarm/background.png') no-repeat;
-      background-size: 100% 100%;
-
-      .alarm-log-data {
-        display: flex;
-        align-items: center;
-        min-width: 60%;
-        max-width: 100%;
-
-        .alarm-log-image {
-          display: flex;
-          align-items: center;
-          padding-right: @alarm-log-padding-left;
-          border-right: 1px solid rgba(0, 0, 0, 0.09);
-
-          .alarm-type {
-            width: 40px;
-
-            .name {
-              color: #000;
-              font-size: 18px;
-            }
-
-            .text {
-              margin-top: 8px;
-              color: #666;
-              font-size: 14px;
-              .ellipsis();
-            }
-          }
-        }
-        .alarm-log-right {
-          display: flex;
-          justify-content: space-between;
-          width: 50%;
-          padding-left: @alarm-log-padding-left;
-
-          .alarm-log-time {
-            max-width: 165px;
-
-            .log-title {
-              margin-top: 8px;
-              color: #666;
-              font-size: 12px;
-            }
-
-            .context {
-              margin-top: 8px;
-              color: rgba(#000, 0.85);
-              font-size: 14px;
-              .ellipsis();
-            }
-          }
-
-          .alarm-log-status {
-            margin-left: @alarm-log-padding-left;
-          }
-        }
-      }
-      .alarm-log-actions {
-        .alarm-log-action {
-          display: flex;
-          justify-content: center;
-          width: 72px;
-          height: 72px;
-          background-color: #fff;
-          border: 1px solid @primary-color;
-
-          .btn {
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            width: 72px;
-            height: 72px;
-            .icon {
-              margin-bottom: 5px;
-              color: @primary-color;
-              font-size: 25px;
-            }
-
-            div {
-              color: @primary-color;
-              font-size: 12px;
-            }
-          }
-        }
-
-        .alarm-log-action:hover {
-          background-color: @primary-color;
-
-          .icon,
-          div {
-            color: #fff;
-          }
-        }
-        .alarm-log-disabled {
-          display: flex;
-          justify-content: center;
-          width: 72px;
-          height: 72px;
-          border: 1px solid rgba(0, 0, 0, 0.25);
-          .btn {
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            width: 72px;
-            height: 72px;
-            .icon {
-              margin-bottom: 5px;
-              color: rgba(0, 0, 0, 0.25);
-              font-size: 25px;
-            }
-
-            div {
-              color: rgba(0, 0, 0, 0.25);
-              font-size: 12px;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .alarm-log-item:hover {
-    box-shadow: 0 2px 16px rgba(0, 0, 0, 0.1);
-  }
-}

+ 0 - 307
src/pages/rule-engine/Alarm/Log/TabComponent/index copy.tsx

@@ -1,307 +0,0 @@
-import SearchComponent from '@/components/SearchComponent';
-import { FileFilled, FileTextFilled, ToolFilled } from '@ant-design/icons';
-import type { ProColumns } from '@jetlinks/pro-table';
-import { Badge, Button, Card, Col, Empty, Pagination, Row, Space, Tooltip } from 'antd';
-import { useEffect, useState } from 'react';
-import './index.less';
-import SolveComponent from '../SolveComponent';
-import SolveLog from '../SolveLog';
-import { AlarmLogModel } from '../model';
-import moment from 'moment';
-import { observer } from '@formily/reactive-react';
-import { service } from '@/pages/rule-engine/Alarm/Log';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
-import { useHistory } from 'umi';
-import PermissionButton from '@/components/PermissionButton';
-import classNames from 'classnames';
-
-interface Props {
-  type: string;
-}
-
-const imgMap = new Map();
-imgMap.set('product', require('/public/images/alarm/product.png'));
-imgMap.set('device', require('/public/images/alarm/device.png'));
-imgMap.set('other', require('/public/images/alarm/other.png'));
-imgMap.set('org', require('/public/images/alarm/org.png'));
-
-const titleMap = new Map();
-titleMap.set('product', '产品');
-titleMap.set('device', '设备');
-titleMap.set('other', '其他');
-titleMap.set('org', '部门');
-
-const colorMap = new Map();
-colorMap.set(1, '#E50012');
-colorMap.set(2, '#FF9457');
-colorMap.set(3, '#FABD47');
-colorMap.set(4, '#999999');
-colorMap.set(5, '#C4C4C4');
-
-const TabComponent = observer((props: Props) => {
-  const { permission } = PermissionButton.usePermission('rule-engine/Alarm/Log');
-  const columns: ProColumns<any>[] = [
-    {
-      title: '名称',
-      dataIndex: 'alarmName',
-    },
-    {
-      title: '最近告警时间',
-      dataIndex: 'alarmTime',
-      valueType: 'dateTime',
-    },
-    {
-      title: '状态',
-      dataIndex: 'state',
-      valueType: 'select',
-      valueEnum: {
-        warning: {
-          text: '告警中',
-          status: 'warning',
-        },
-        normal: {
-          text: '无告警',
-          status: 'normal',
-        },
-      },
-    },
-  ];
-
-  const [param, setParam] = useState<any>({ pageSize: 10, terms: [] });
-  const history = useHistory<Record<string, string>>();
-
-  const [dataSource, setDataSource] = useState<any>({
-    data: [],
-    pageSize: 10,
-    pageIndex: 0,
-    total: 0,
-  });
-
-  const handleSearch = (params: any) => {
-    setParam(params);
-    const terms = [...params.terms];
-    if (props.type !== 'all') {
-      terms.push({
-        termType: 'eq',
-        column: 'targetType',
-        value: props.type,
-        type: 'and',
-      });
-    }
-    service
-      .query({
-        ...params,
-        terms: [...terms],
-        sorts: [{ name: 'alarmTime', order: 'desc' }],
-      })
-      .then((resp) => {
-        if (resp.status === 200) {
-          setDataSource(resp.result);
-        }
-      });
-  };
-
-  useEffect(() => {
-    handleSearch(param);
-  }, [props.type]);
-
-  return (
-    <div className="alarm-log-card">
-      <SearchComponent<any>
-        field={columns}
-        target="alarm-log"
-        onSearch={(data) => {
-          const dt = {
-            pageSize: 10,
-            terms: [...data?.terms],
-          };
-          handleSearch(dt);
-        }}
-      />
-      <Card>
-        {dataSource?.data.length > 0 ? (
-          <Row gutter={24} style={{ marginTop: 10 }}>
-            {(dataSource?.data || []).map((item: any) => (
-              <Col key={item.id} span={24}>
-                <div className="alarm-log-item">
-                  <div className="alarm-log-title">
-                    <div
-                      className="alarm-log-level"
-                      style={{ backgroundColor: colorMap.get(item.level) }}
-                    >
-                      <div className="alarm-log-text">
-                        {AlarmLogModel.defaultLevel.find((i) => i.level === item.level)?.title ||
-                          item.level}
-                      </div>
-                    </div>
-                    <div className="alarm-log-title-text">
-                      <Tooltip placement="topLeft" title={item.alarmName}>
-                        {item.alarmName}
-                      </Tooltip>
-                    </div>
-                  </div>
-                  <div className="alarm-log-content">
-                    <div className="alarm-log-data">
-                      <div className="alarm-log-image">
-                        <img
-                          width={88}
-                          height={88}
-                          src={imgMap.get(props.type)}
-                          alt={''}
-                          style={{ marginRight: 20 }}
-                        />
-                        <div className="alarm-type">
-                          <div className="name">{titleMap.get(item.targetType)}</div>
-                          <div className="text">
-                            <Tooltip placement="topLeft" title={item.targetName}>
-                              {item.targetName}
-                            </Tooltip>
-                          </div>
-                        </div>
-                      </div>
-                      <div className="alarm-log-right">
-                        <div className="alarm-log-time">
-                          <div className="log-title">最近告警时间</div>
-                          <div className="context">
-                            {moment(item.alarmTime).format('YYYY-MM-DD HH:mm:ss')}
-                          </div>
-                        </div>
-                        <div
-                          className="alarm-log-time alarm-log-status"
-                          style={{ paddingLeft: 10 }}
-                        >
-                          <div className="log-title">状态</div>
-                          <div className="context">
-                            <Badge status={item.state.value === 'warning' ? 'error' : 'default'} />
-                            <span
-                              style={{
-                                color: item.state.value === 'warning' ? '#E50012' : 'black',
-                              }}
-                            >
-                              {item.state.text}
-                            </span>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                    <div className="alarm-log-actions">
-                      <Space>
-                        {item.state.value === 'warning' && (
-                          <div
-                            className={classNames(
-                              permission.action ? 'alarm-log-action' : 'alarm-log-disabled',
-                            )}
-                          >
-                            <Tooltip title={permission.action ? '' : '暂无权限,请联系管理员'}>
-                              <Button
-                                type={'link'}
-                                disabled={!permission.action}
-                                onClick={() => {
-                                  AlarmLogModel.solveVisible = true;
-                                  AlarmLogModel.current = item;
-                                }}
-                              >
-                                <div className="btn">
-                                  <ToolFilled className="icon" />
-                                  <div>告警处理</div>
-                                </div>
-                              </Button>
-                            </Tooltip>
-                          </div>
-                        )}
-                        <div className="alarm-log-action">
-                          <Button
-                            type={'link'}
-                            onClick={() => {
-                              AlarmLogModel.current = item;
-                              const url = getMenuPathByParams(
-                                MENUS_CODE['rule-engine/Alarm/Log/Detail'],
-                                item.id,
-                              );
-                              history.push(url);
-                            }}
-                          >
-                            <div className="btn">
-                              <FileFilled className="icon" />
-                              <div>告警日志</div>
-                            </div>
-                          </Button>
-                        </div>
-                        <div className="alarm-log-action">
-                          <Button
-                            type={'link'}
-                            onClick={() => {
-                              AlarmLogModel.logVisible = true;
-                              AlarmLogModel.current = item;
-                            }}
-                          >
-                            <div className="btn">
-                              <FileTextFilled className="icon" />
-                              <div>处理记录</div>
-                            </div>
-                          </Button>
-                        </div>
-                      </Space>
-                    </div>
-                  </div>
-                </div>
-              </Col>
-            ))}
-          </Row>
-        ) : (
-          <Empty />
-        )}
-        {dataSource.data.length > 0 && (
-          <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-            <Pagination
-              showSizeChanger
-              size="small"
-              className={'pro-table-card-pagination'}
-              total={dataSource?.total || 0}
-              current={dataSource?.pageIndex + 1}
-              onChange={(page, size) => {
-                handleSearch({
-                  ...param,
-                  pageIndex: page - 1,
-                  pageSize: size,
-                });
-              }}
-              pageSizeOptions={[10, 20, 50, 100]}
-              pageSize={dataSource?.pageSize}
-              showTotal={(num) => {
-                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-              }}
-            />
-          </div>
-        )}
-      </Card>
-      {AlarmLogModel.solveVisible && (
-        <SolveComponent
-          close={() => {
-            AlarmLogModel.solveVisible = false;
-            AlarmLogModel.current = {};
-          }}
-          reload={() => {
-            AlarmLogModel.solveVisible = false;
-            AlarmLogModel.current = {};
-            handleSearch(param);
-          }}
-          data={AlarmLogModel.current}
-        />
-      )}
-      {AlarmLogModel.logVisible && (
-        <SolveLog
-          close={() => {
-            AlarmLogModel.logVisible = false;
-            AlarmLogModel.current = {};
-          }}
-          data={AlarmLogModel.current}
-        />
-      )}
-    </div>
-  );
-});
-
-export default TabComponent;

+ 1 - 0
src/pages/rule-engine/Alarm/Log/TabComponent/index.less

@@ -69,6 +69,7 @@
 
   .card-state-content {
     transform: skewX(-45deg);
+
     .stateText {
       width: 70px;
       overflow: hidden;

+ 124 - 108
src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx

@@ -14,6 +14,7 @@ import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { useHistory } from 'umi';
 import classNames from 'classnames';
 import { useDomFullHeight } from '@/hooks';
+import PermissionButton from '@/components/PermissionButton';
 
 interface Props {
   type: string;
@@ -39,7 +40,9 @@ colorMap.set(4, '#999999');
 colorMap.set(5, '#C4C4C4');
 
 const TabComponent = observer((props: Props) => {
-  const { minHeight } = useDomFullHeight(`.alarmLog`);
+  const { minHeight } = useDomFullHeight(`.alarmLog`, 36);
+  const { permission } = PermissionButton.usePermission('rule-engine/Alarm/Log');
+
   const columns: ProColumns<any>[] = [
     {
       title: '名称',
@@ -106,21 +109,30 @@ const TabComponent = observer((props: Props) => {
   }, [props.type]);
 
   const tools = (record: any) => [
-    <Button
-      type={'link'}
+    <Tooltip
       key={'solve'}
-      style={{ padding: 0 }}
-      disabled={record.state.value === 'normal'}
-      onClick={() => {
-        AlarmLogModel.solveVisible = true;
-        AlarmLogModel.current = record;
-      }}
+      title={
+        !permission.action
+          ? '暂无权限,请联系管理员'
+          : record.state?.value === 'normal'
+          ? '无告警'
+          : ''
+      }
     >
-      <Tooltip title={record.state?.value === 'normal' ? '无告警' : ''}>
+      <Button
+        type={'link'}
+        key={'solve'}
+        style={{ padding: 0 }}
+        disabled={record.state.value === 'normal' || !permission.action}
+        onClick={() => {
+          AlarmLogModel.solveVisible = true;
+          AlarmLogModel.current = record;
+        }}
+      >
         <ToolFilled />
         告警处理
-      </Tooltip>
-    </Button>,
+      </Button>
+    </Tooltip>,
     <Button
       type={'link'}
       style={{ padding: 0 }}
@@ -180,114 +192,118 @@ const TabComponent = observer((props: Props) => {
           handleSearch(dt);
         }}
       />
-      <Card className="alarmLog" style={{ minHeight }}>
-        {dataSource?.data.length > 0 ? (
-          <Row gutter={[24, 24]} style={{ marginTop: 10 }}>
-            {(dataSource?.data || []).map((item: any) => (
-              <Col key={item.id} span={12}>
-                <div className={classNames('iot-card')}>
-                  <div className={'card-warp'}>
-                    <div className={classNames('card-content')}>
-                      <div
-                        style={{ fontSize: 20, fontWeight: 700, marginBottom: 20 }}
-                        className="ellipsis"
-                      >
-                        <Tooltip title={item.alarmName}>
-                          <a>{item.alarmName}</a>
-                        </Tooltip>
-                      </div>
-                      <div className="alarm-log-context">
-                        <div className="context-left">
-                          <div className="context-img">
-                            <img width={70} height={70} src={defaultImage} alt={''} />
-                          </div>
-                          <div className="left-item">
-                            <div className="left-item-title">{titleMap.get(item.targetType)}</div>
-                            <div className="left-item-value ellipsis">
-                              <Tooltip placement="topLeft" title={item.targetName}>
-                                {item.targetName}
-                              </Tooltip>
-                            </div>
+      <Card>
+        <div className="alarmLog" style={{ minHeight, position: 'relative' }}>
+          <div style={{ height: '100%', paddingBottom: 48 }}>
+            {dataSource?.data.length > 0 ? (
+              <Row gutter={[24, 24]} style={{ marginTop: 10 }}>
+                {(dataSource?.data || []).map((item: any) => (
+                  <Col key={item.id} span={12}>
+                    <div className={classNames('iot-card')}>
+                      <div className={'card-warp'}>
+                        <div className={classNames('card-content')}>
+                          <div
+                            style={{ fontSize: 20, fontWeight: 700, marginBottom: 20 }}
+                            className="ellipsis"
+                          >
+                            <Tooltip title={item.alarmName}>
+                              <a style={{ cursor: 'default' }}>{item.alarmName}</a>
+                            </Tooltip>
                           </div>
-                        </div>
-                        <div className="context-right">
-                          <div className="right-item">
-                            <div className="right-item-title">最近告警时间</div>
-                            <div className="right-item-value ellipsis">
-                              {moment(item.alarmTime).format('YYYY-MM-DD HH:mm:ss')}
+                          <div className="alarm-log-context">
+                            <div className="context-left">
+                              <div className="context-img">
+                                <img width={70} height={70} src={defaultImage} alt={''} />
+                              </div>
+                              <div className="left-item">
+                                <div className="left-item-title">
+                                  {titleMap.get(item.targetType)}
+                                </div>
+                                <div className="left-item-value ellipsis">
+                                  <Tooltip placement="topLeft" title={item.targetName}>
+                                    {item.targetName}
+                                  </Tooltip>
+                                </div>
+                              </div>
                             </div>
-                          </div>
-                          <div className="right-item">
-                            <div className="right-item-title">状态</div>
-                            <div className="right-item-value">
-                              <Badge
-                                status={item.state.value === 'warning' ? 'error' : 'default'}
-                              />
-                              <span
-                                style={{
-                                  color: item.state.value === 'warning' ? '#E50012' : 'black',
-                                }}
-                              >
-                                {item.state.text}
-                              </span>
+                            <div className="context-right">
+                              <div className="right-item">
+                                <div className="right-item-title">最近告警时间</div>
+                                <div className="right-item-value ellipsis">
+                                  {moment(item.alarmTime).format('YYYY-MM-DD HH:mm:ss')}
+                                </div>
+                              </div>
+                              <div className="right-item">
+                                <div className="right-item-title">状态</div>
+                                <div className="right-item-value">
+                                  <Badge
+                                    status={item.state.value === 'warning' ? 'error' : 'default'}
+                                  />
+                                  <span
+                                    style={{
+                                      color: item.state.value === 'warning' ? '#E50012' : 'black',
+                                    }}
+                                  >
+                                    {item.state.text}
+                                  </span>
+                                </div>
+                              </div>
                             </div>
                           </div>
-                        </div>
-                      </div>
-                      <div
-                        className={'alarm-log-state'}
-                        style={{ backgroundColor: colorMap.get(item.level) }}
-                      >
-                        <div className={'card-state-content'}>
-                          <Tooltip
-                            placement="topLeft"
-                            title={
-                              AlarmLogModel.defaultLevel.find((i) => i.level === item.level)
-                                ?.title || item.level
-                            }
+                          <div
+                            className={'alarm-log-state'}
+                            style={{ backgroundColor: colorMap.get(item.level) }}
                           >
-                            <div className={'stateText'}>
+                            <div className={'card-state-content'}>
                               {AlarmLogModel.defaultLevel.find((i) => i.level === item.level)
                                 ?.title || item.level}
                             </div>
-                          </Tooltip>
+                          </div>
                         </div>
                       </div>
+                      <div className={'card-tools'}>{getAction(tools(item))}</div>
                     </div>
-                  </div>
-                  <div className={'card-tools'}>{getAction(tools(item))}</div>
-                </div>
-              </Col>
-            ))}
-          </Row>
-        ) : (
-          <Empty style={{ marginTop: '10%' }} />
-        )}
-        {dataSource.data.length > 0 && (
-          <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>
-            <Pagination
-              showSizeChanger
-              size="small"
-              className={'pro-table-card-pagination'}
-              total={dataSource?.total || 0}
-              current={dataSource?.pageIndex + 1}
-              onChange={(page, size) => {
-                handleSearch({
-                  ...param,
-                  pageIndex: page - 1,
-                  pageSize: size,
-                });
-              }}
-              pageSizeOptions={[10, 20, 50, 100]}
-              pageSize={dataSource?.pageSize}
-              showTotal={(num) => {
-                const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
-                const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
-                return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
-              }}
-            />
+                  </Col>
+                ))}
+              </Row>
+            ) : (
+              <Empty style={{ marginTop: '10%' }} />
+            )}
           </div>
-        )}
+          {dataSource.data.length > 0 && (
+            <div
+              style={{
+                display: 'flex',
+                justifyContent: 'flex-end',
+                position: 'absolute',
+                bottom: 0,
+                width: '100%',
+              }}
+            >
+              <Pagination
+                showSizeChanger
+                size="small"
+                className={'pro-table-card-pagination'}
+                total={dataSource?.total || 0}
+                current={dataSource?.pageIndex + 1}
+                onChange={(page, size) => {
+                  handleSearch({
+                    ...param,
+                    pageIndex: page - 1,
+                    pageSize: size,
+                  });
+                }}
+                pageSizeOptions={[10, 20, 50, 100]}
+                pageSize={dataSource?.pageSize}
+                showTotal={(num) => {
+                  const minSize = dataSource?.pageIndex * dataSource?.pageSize + 1;
+                  const MaxSize = (dataSource?.pageIndex + 1) * dataSource?.pageSize;
+                  return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
+                }}
+              />
+            </div>
+          )}
+        </div>
       </Card>
       {AlarmLogModel.solveVisible && (
         <SolveComponent

+ 1 - 1
src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx

@@ -8,7 +8,7 @@ import {
   queryRelationUsers,
   queryWechatUsers,
 } from '@/pages/rule-engine/Scene/Save/action/service';
-import { forkJoin, filter, from, defer, map } from 'rxjs';
+import { defer, filter, forkJoin, from, map } from 'rxjs';
 
 type ChangeType = {
   source?: string;

+ 6 - 5
src/pages/rule-engine/Scene/Save/action/device/index.tsx

@@ -25,10 +25,7 @@ enum SourceEnum {
   'relation' = 'relation',
 }
 
-const DefaultSourceOptions = [
-  { label: '固定设备', value: SourceEnum.fixed },
-  { label: '按标签', value: SourceEnum.tag },
-];
+const DefaultSourceOptions = [{ label: '固定设备', value: SourceEnum.fixed }];
 
 export enum MessageTypeEnum {
   'WRITE_PROPERTY' = 'WRITE_PROPERTY',
@@ -82,7 +79,11 @@ export default (props: DeviceProps) => {
   useEffect(() => {
     props.form?.resetFields([['actions', name, 'device', 'selector']]);
     if (props.triggerType === 'device') {
-      setSourceList([...DefaultSourceOptions, { label: '按关系', value: SourceEnum.relation }]);
+      setSourceList([
+        ...DefaultSourceOptions,
+        { label: '按关系', value: SourceEnum.relation },
+        { label: '按标签', value: SourceEnum.tag },
+      ]);
     } else {
       setSourceList(DefaultSourceOptions);
     }

+ 4 - 2
src/pages/rule-engine/Scene/Save/index.tsx

@@ -150,7 +150,8 @@ export default () => {
         <>
           <InputNumber
             value={shakeLimit.time}
-            min={0}
+            min={1}
+            max={100}
             onChange={(e: number) => {
               const newShake = {
                 ...shakeLimit,
@@ -162,7 +163,8 @@ export default () => {
           <span> 秒内发生 </span>
           <InputNumber
             value={shakeLimit.threshold}
-            min={0}
+            min={1}
+            max={100}
             onChange={(e: number) => {
               const newShake = {
                 ...shakeLimit,

+ 35 - 19
src/pages/system/DataSource/Management/EditTable.tsx

@@ -1,8 +1,8 @@
+import { randomString } from '@/utils/util';
 import { ArrayTable, Editable, Form, FormItem, Input, NumberPicker, Radio } from '@formily/antd';
 import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { Button } from 'antd';
-import { useEffect } from 'react';
 import RemoveData from './RemoveData';
 
 interface Props {
@@ -15,6 +15,7 @@ interface Props {
 }
 
 const EditTable = (props: Props) => {
+  const _data = (props?.data || []).map((item) => ({ ...item, old_id: randomString() })) || [];
   const SchemaField = createSchemaField({
     components: {
       FormItem,
@@ -29,7 +30,7 @@ const EditTable = (props: Props) => {
 
   const form = createForm({
     initialValues: {
-      array: props.data,
+      array: _data,
     },
   });
 
@@ -52,6 +53,12 @@ const EditTable = (props: Props) => {
               'x-component': 'ArrayTable.Column',
               'x-component-props': { title: '列名' },
               properties: {
+                old_id: {
+                  type: 'string',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Input',
+                  'x-hidden': true,
+                },
                 name: {
                   type: 'string',
                   'x-decorator': 'FormItem',
@@ -71,6 +78,13 @@ const EditTable = (props: Props) => {
                     },
                   ],
                   required: true,
+                  'x-reactions': (field: any) => {
+                    const old_id = field.query('.old_id').take().value;
+                    const flag = _data.find((item: any) => {
+                      return old_id && item?.old_id && item?.old_id === old_id;
+                    });
+                    field.disabled = !!flag;
+                  },
                 },
               },
             },
@@ -107,23 +121,24 @@ const EditTable = (props: Props) => {
               'x-component-props': { title: '长度' },
               properties: {
                 length: {
-                  type: 'string',
+                  type: 'number',
                   'x-decorator': 'FormItem',
                   'x-component': 'NumberPicker',
                   'x-component-props': {
                     placeholder: '请输入长度',
                   },
+                  default: 0,
                   'x-validator': [
-                    {
-                      required: true,
-                      message: '请输入长度',
-                    },
+                    // {
+                    //   required: true,
+                    //   message: '请输入长度',
+                    // },
                     {
                       maximum: 99999,
                       minimum: 1,
                     },
                   ],
-                  required: true,
+                  // required: true,
                 },
               },
             },
@@ -133,23 +148,24 @@ const EditTable = (props: Props) => {
               'x-component-props': { title: '精度' },
               properties: {
                 scale: {
-                  type: 'string',
+                  type: 'number',
                   'x-decorator': 'FormItem',
                   'x-component': 'NumberPicker',
                   'x-component-props': {
                     placeholder: '请输入精度',
                   },
+                  default: 0,
                   'x-validator': [
-                    {
-                      required: true,
-                      message: '请输入精度',
-                    },
+                    // {
+                    //   required: true,
+                    //   message: '请输入精度',
+                    // },
                     {
                       maximum: 99999,
                       minimum: 0,
                     },
                   ],
-                  required: true,
+                  // required: true,
                 },
               },
             },
@@ -239,10 +255,6 @@ const EditTable = (props: Props) => {
     },
   };
 
-  useEffect(() => {
-    form.setValues({ array: props?.data || [] });
-  }, [props.data]);
-
   return (
     <div>
       <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
@@ -251,7 +263,11 @@ const EditTable = (props: Props) => {
           style={{ marginBottom: 20 }}
           onClick={async () => {
             const data: any = await form.submit();
-            props.onChange(data);
+            const list = (data?.array || []).map((i: any) => {
+              const { old_id, ...extra } = i;
+              return { ...extra };
+            });
+            props.onChange({ array: list });
           }}
         >
           保存

+ 13 - 1
src/pages/system/DataSource/Save/index.tsx

@@ -1,5 +1,5 @@
 import { Form, FormGrid, FormItem, Input, Password, Select } from '@formily/antd';
-import { createForm } from '@formily/core';
+import { createForm, onFieldValueChange } from '@formily/core';
 import type { ISchema } from '@formily/react';
 import { createSchemaField } from '@formily/react';
 import { Modal } from 'antd';
@@ -17,6 +17,18 @@ const Save = (props: Props) => {
   const form = createForm({
     validateFirst: true,
     initialValues: props.data,
+    effects: () => {
+      onFieldValueChange('typeId', (field, form1) => {
+        if (field.modified) {
+          form1.setFieldState('description', (state) => {
+            state.value = '';
+          });
+          form1.setFieldState('shareConfig.*', (state) => {
+            state.value = '';
+          });
+        }
+      });
+    },
   });
 
   const SchemaField = createSchemaField({

+ 3 - 0
src/pages/system/Platforms/Api/leftTree.tsx

@@ -64,6 +64,9 @@ export default (props: LeftTreeType) => {
   );
 
   const handleTreeData = (data: any) => {
+    if (data && Object.keys(data).length) {
+      return [];
+    }
     const newArr = data.tags.map((item: any) => ({ id: item.name, name: item.name, isLeaf: true }));
 
     Object.keys(data.paths).forEach((a: any) => {

+ 1 - 2
src/pages/system/Platforms/Api/swagger-ui/debugging.tsx

@@ -196,7 +196,6 @@ export default observer(() => {
             {ApiModel.swagger.method ? ApiModel.swagger.method.toUpperCase() : ''}
           </Button>
           <Input
-            allowClear
             style={{
               width: `calc(100% - ${ApiModel.swagger.method !== 'delete' ? '140px' : '150px'})`,
             }}
@@ -237,7 +236,7 @@ export default observer(() => {
       </div>
       <div className={'swagger-content-item'}>
         <TitleComponent data={'响应内容'} />
-        <div>
+        <div style={{ border: '1px solid #fefefe', borderRadius: 2 }}>
           {
             // @ts-ignore
             <ReactJson

+ 4 - 4
src/pages/system/Platforms/save.tsx

@@ -384,10 +384,10 @@ export default (props: SaveProps) => {
               tooltip: '授权后自动跳转的页面地址',
             },
             'x-validator': [
-              {
-                max: 128,
-                message: '最多可输入128个字符',
-              },
+              // {
+              //   max: 128,
+              //   message: '最多可输入128个字符',
+              // },
               {
                 required: true,
                 message: '请输入redirectUrl',

+ 1 - 1
src/pages/system/Tenant/Detail/Member/Bind.tsx

@@ -1,4 +1,4 @@
-import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { service } from '@/pages/system/Tenant';
 import { Space } from 'antd';