Browse Source

feat: merge

xieyonghong 3 năm trước cách đây
mục cha
commit
3a621c0316
57 tập tin đã thay đổi với 716 bổ sung391 xóa
  1. 4 35
      src/components/ProTableCard/CardItems/AlarmConfig.tsx
  2. 0 1
      src/pages/Log/System/index.tsx
  3. 26 11
      src/pages/account/Center/index.tsx
  4. 4 0
      src/pages/account/Center/service.ts
  5. 34 5
      src/pages/device/Category/index.tsx
  6. 4 1
      src/pages/device/Instance/Detail/Diagnose/Message/Dialog/index.tsx
  7. 14 1
      src/pages/device/Instance/Detail/Diagnose/Message/index.less
  8. 256 190
      src/pages/device/Instance/Detail/Diagnose/Message/index.tsx
  9. 2 1
      src/pages/device/Instance/Detail/Log/index.tsx
  10. 3 3
      src/pages/device/Instance/Detail/MetadataLog/Property/Detail.tsx
  11. 1 1
      src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx
  12. 11 9
      src/pages/device/Instance/Detail/Running/Event/index.tsx
  13. 5 0
      src/pages/device/Instance/Detail/Running/Property/EditProperty.tsx
  14. 38 10
      src/pages/device/Instance/index.tsx
  15. 12 6
      src/pages/device/components/Metadata/Base/index.tsx
  16. 20 16
      src/pages/home/Api/index.tsx
  17. 10 0
      src/pages/home/device/index.tsx
  18. 1 1
      src/pages/home/index.tsx
  19. 9 4
      src/pages/home/service.ts
  20. 3 0
      src/pages/iot-card/Home/index.less
  21. 3 1
      src/pages/iot-card/Home/index.tsx
  22. 5 0
      src/pages/iot-card/Recharge/index.less
  23. 1 0
      src/pages/iot-card/Recharge/index.tsx
  24. 1 1
      src/pages/link/AccessConfig/Detail/Cloud/index.tsx
  25. 3 2
      src/pages/link/DataCollect/DataGathering/index.less
  26. 1 1
      src/pages/link/DataCollect/DataGathering/index.tsx
  27. 2 1
      src/pages/link/DataCollect/components/Channel/index.tsx
  28. 3 1
      src/pages/link/DataCollect/components/Device/index.tsx
  29. 3 0
      src/pages/link/DataCollect/components/Point/Save/modbus.tsx
  30. 3 0
      src/pages/link/DataCollect/components/Point/Save/opc-ua.tsx
  31. 3 1
      src/pages/link/DataCollect/components/Point/index.tsx
  32. 6 0
      src/pages/link/DataCollect/components/Tree/index.less
  33. 2 0
      src/pages/link/DataCollect/components/Tree/index.tsx
  34. 23 4
      src/pages/link/Type/Detail/index.tsx
  35. 3 2
      src/pages/media/Cascade/Save/index.tsx
  36. 5 5
      src/pages/media/DashBoard/index.tsx
  37. 15 10
      src/pages/media/Device/Save/ProviderSelect.tsx
  38. 8 7
      src/pages/media/Stream/Detail/index.tsx
  39. 1 1
      src/pages/rule-engine/Alarm/Configuration/Save/Base/index.tsx
  40. 1 1
      src/pages/rule-engine/Alarm/Configuration/Save/Scene/Save/index.tsx
  41. 11 0
      src/pages/rule-engine/Alarm/Configuration/Save/Scene/index.less
  42. 3 2
      src/pages/rule-engine/Alarm/Configuration/Save/Scene/index.tsx
  43. 43 31
      src/pages/rule-engine/Alarm/Configuration/index.tsx
  44. 8 3
      src/pages/rule-engine/Alarm/Configuration/service.ts
  45. 1 0
      src/pages/rule-engine/Alarm/Configuration/typings.d.ts
  46. 1 1
      src/pages/rule-engine/DashBoard/index.less
  47. 2 2
      src/pages/system/Basis/index.tsx
  48. 2 5
      src/pages/system/DataSource/Save/index.tsx
  49. 7 2
      src/pages/system/DataSource/index.tsx
  50. 5 2
      src/pages/system/Platforms/Api/base.tsx
  51. 1 0
      src/pages/system/Platforms/Api/swagger-ui/base.tsx
  52. 36 3
      src/pages/system/Platforms/Api/swagger-ui/debugging.tsx
  53. 2 0
      src/pages/system/Platforms/Api/swagger-ui/index.tsx
  54. 18 1
      src/pages/system/Relationship/Save/index.tsx
  55. 11 1
      src/pages/system/Relationship/index.tsx
  56. 7 1
      src/pages/system/Role/Detail/Permission/index.tsx
  57. 9 4
      src/pages/system/User/Save/index.tsx

+ 4 - 35
src/components/ProTableCard/CardItems/AlarmConfig.tsx

@@ -1,11 +1,9 @@
 import React from 'react';
-import { Ellipsis, PermissionButton, TableCard } from '@/components';
+import { Ellipsis, TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 import { Store } from 'jetlinks-store';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
-import { useHistory } from 'umi';
 
 export interface AlarmConfigProps extends ConfigurationItem {
   detail?: React.ReactNode;
@@ -16,7 +14,6 @@ export interface AlarmConfigProps extends ConfigurationItem {
 export const aliyunSms = require('/public/images/alarm/alarm-config.png');
 
 export default (props: AlarmConfigProps) => {
-  const history = useHistory();
   return (
     <TableCard
       actions={props.actions}
@@ -43,24 +40,9 @@ export default (props: AlarmConfigProps) => {
           <div className={'card-item-content'}>
             <div>
               <label>关联场景联动</label>
-              <div className={'ellipsis'}>
-                <PermissionButton
-                  type={'link'}
-                  isPermission={!!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])}
-                  style={{ padding: 0, height: 'auto' }}
-                  tooltip={{
-                    title: !!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])
-                      ? props?.sceneName
-                      : '没有权限,请联系管理员',
-                  }}
-                  onClick={() => {
-                    const url = getMenuPathByCode('rule-engine/Scene/Save');
-                    history.push(`${url}?id=${props.sceneId}`);
-                  }}
-                >
-                  {props?.sceneName || ''}
-                </PermissionButton>
-              </div>
+              <Ellipsis
+                title={(props?.scene || []).map((item: any) => item.name).join(',') || ''}
+              />
             </div>
             <div>
               <label>告警级别</label>
@@ -71,19 +53,6 @@ export default (props: AlarmConfigProps) => {
                   )?.title || props?.level
                 }
               />
-              {/*<div className={'ellipsis'}>*/}
-              {/*  <Tooltip*/}
-              {/*    title={*/}
-              {/*      (Store.get('default-level') || []).find(*/}
-              {/*        (item: any) => item?.level === props?.level,*/}
-              {/*      )?.title || props?.level*/}
-              {/*    }*/}
-              {/*  >*/}
-              {/*    {(Store.get('default-level') || []).find(*/}
-              {/*      (item: any) => item?.level === props?.level,*/}
-              {/*    )?.title || props?.level}*/}
-              {/*  </Tooltip>*/}
-              {/*</div>*/}
             </div>
           </div>
         </div>

+ 0 - 1
src/pages/Log/System/index.tsx

@@ -63,7 +63,6 @@ const System = () => {
       }),
       dataIndex: 'message',
       ellipsis: true,
-      width: 200,
     },
     {
       title: intl.formatMessage({

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

@@ -37,6 +37,7 @@ const Center = () => {
   const [infos, setInfos] = useState<boolean>(false);
   const [password, setPassword] = useState<boolean>(false);
   const [bindList, setBindList] = useState<any>([]);
+  const [apiUser, setApiUser] = useState<any>();
 
   const iconMap = new Map();
   iconMap.set('dingtalk-ent-app', require('/public/images/notice/dingtalk.png'));
@@ -58,6 +59,17 @@ const Center = () => {
       setImageUrl(res.result.avatar);
     });
   };
+
+  const getApiUser = async () => {
+    const res = await service.queryCurrent();
+    if (res.status === 200) {
+      const isApiUser = res.result.dimensions.find(
+        (item: any) => item.type === 'api-client' || item.type.id === 'api-client',
+      );
+      setApiUser(isApiUser);
+    }
+  };
+
   const uploadProps: UploadProps = {
     showUploadList: false,
     accept: 'image/jpeg,image/png',
@@ -136,6 +148,7 @@ const Center = () => {
 
   useEffect(() => {
     getDetail();
+    getApiUser();
     if (isNoCommunity) {
       getBindInfo();
     }
@@ -330,17 +343,19 @@ const Center = () => {
           </Row>
         </Card>
       )}
-      <Card
-        style={{ marginTop: 15 }}
-        title={
-          <div style={{ fontSize: '22px' }}>
-            <Divider type="vertical" style={{ backgroundColor: '#2F54EB', width: 3 }} />
-            首页视图
-          </div>
-        }
-      >
-        <AccountInit />,
-      </Card>
+      {!apiUser && (
+        <Card
+          style={{ marginTop: 15 }}
+          title={
+            <div style={{ fontSize: '22px' }}>
+              <Divider type="vertical" style={{ backgroundColor: '#2F54EB', width: 3 }} />
+              首页视图
+            </div>
+          }
+        >
+          <AccountInit />,
+        </Card>
+      )}
       {infos && (
         <InfoEdit
           data={data}

+ 4 - 0
src/pages/account/Center/service.ts

@@ -74,6 +74,10 @@ class Service extends BaseService<UserItem> {
     request(`/${SystemConst.API_BASE}/system/config/${scopes}`, {
       method: 'GET',
     });
+  queryCurrent = () =>
+    request(`/${SystemConst.API_BASE}/authorize/me`, {
+      method: 'GET',
+    });
 }
 
 export default Service;

+ 34 - 5
src/pages/device/Category/index.tsx

@@ -13,6 +13,8 @@ import SearchComponent from '@/components/SearchComponent';
 import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
+import { service as api } from '@/pages/device/Product';
+import { Spin } from 'antd';
 
 export const service = new Service('device/category');
 
@@ -55,6 +57,8 @@ const Category = observer(() => {
   const permissionCode = 'device/Category';
   const { permission } = PermissionButton.usePermission(permissionCode);
   const { minHeight } = useDomFullHeight(`.device-category`, 24);
+  const [loading, setLoading] = useState<boolean>(true);
+  const [title, setTitle] = useState<string>('');
 
   const intl = useIntl();
 
@@ -135,11 +139,30 @@ const Category = observer(() => {
           type="link"
           key="delete"
           style={{ padding: 0 }}
+          onClick={async () => {
+            const res: any = await api.queryNoPagingPost({
+              terms: [{ terms: [{ column: 'classifiedId', value: record.id }] }],
+            });
+            if (res.status === 200) {
+              if (res.result.length === 0) {
+                setTitle('确定删除?');
+              } else {
+                setTitle('该数据已被产品引用,确定删除?');
+              }
+              setLoading(false);
+            } else {
+              setLoading(false);
+            }
+          }}
           popConfirm={{
-            title: intl.formatMessage({
-              id: 'pages.system.role.option.delete',
-              defaultMessage: '确定要删除吗',
-            }),
+            title: <>{loading ? <Spin /> : title}</>,
+            okButtonProps: {
+              loading: loading,
+            },
+            onCancel: () => {
+              setTitle('');
+              setLoading(true);
+            },
             onConfirm: async () => {
               const resp = (await service.remove(record.id)) as Response<any>;
               if (resp.status === 200) {
@@ -180,7 +203,13 @@ const Category = observer(() => {
         request={async (params) => {
           const response = await service.queryTree({
             paging: false,
-            sorts: [sortParam],
+            sorts: [
+              sortParam,
+              {
+                name: 'createTime',
+                order: 'desc',
+              },
+            ],
             ...params,
           });
           setTreeData(response.result);

+ 4 - 1
src/pages/device/Instance/Detail/Diagnose/Message/Dialog/index.tsx

@@ -50,7 +50,10 @@ const Dialog = (props: Props) => {
             <div className="dialog-box">
               <div className="dialog-header">
                 <div className="dialog-title">
-                  <Badge color={statusColor.get(item.error ? 'error' : 'success')} />
+                  <Badge
+                    color={statusColor.get(item.error ? 'error' : 'success')}
+                    style={{ marginRight: 5 }}
+                  />
                   {operationMap.get(item.operation) || item?.operation}
                 </div>
                 <div className="dialog-time">

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

@@ -19,7 +19,20 @@
   padding: 15px;
   background-color: #e7eaec;
 
-  .parameter {
+  .function-item {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    .function-item-form {
+      width: 80%;
+      min-width: 500px;
+    }
+    .function-item-btn {
+      min-width: 65px;
+    }
+  }
+
+  .inputs-parameter {
     margin: 15px 0;
   }
 }

+ 256 - 190
src/pages/device/Instance/Detail/Diagnose/Message/index.tsx

@@ -1,13 +1,13 @@
 import TitleComponent from '@/components/TitleComponent';
 import './index.less';
 import Dialog from './Dialog';
-import { Badge, Button, Col, DatePicker, Empty, Input, InputNumber, Row, Select } from 'antd';
+import { Badge, Button, Col, Empty, Row } from 'antd';
 import { useEffect, useMemo, useState } from 'react';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
-import type { Field } from '@formily/core';
-import { createForm, onFieldReact } from '@formily/core';
+import { Field, onFieldReact } from '@formily/core';
+import { createForm, onFieldValueChange } from '@formily/core';
 import { createSchemaField, FormProvider } from '@formily/react';
 import {
   ArrayTable,
@@ -16,6 +16,7 @@ import {
   Input as FInput,
   NumberPicker,
   PreviewText,
+  FormGrid,
   Select as FSelect,
 } from '@formily/antd';
 import { randomString } from '@/utils/util';
@@ -23,33 +24,11 @@ import Log from './Log';
 import { DiagnoseStatusModel, messageStatusMap, messageStyleMap } from '../Status/model';
 import { observer } from '@formily/reactive-react';
 
-// const DataTypeComponent = (value: string) => {
-//   switch (value) {
-//     case 'array':
-//       return <div>{value}array</div>
-//     // case 'enum':
-//     //   return <div>{value}</div>
-//     case 'geo':
-//       return <div>{value}geo</div>
-//     case 'file':
-//       return <div>{value}file</div>
-//     default:
-//       return <div>{value}</div>
-//   }
-// }
-
-const DatePicker1: any = DatePicker;
-
 const Message = observer(() => {
   const [subscribeTopic] = useSendWebsocketMessage();
-  const [type, setType] = useState<'property' | 'function'>('function');
   const [input, setInput] = useState<any>({});
   const [inputs, setInputs] = useState<any[]>([]);
 
-  const [propertyType, setPropertyType] = useState<'read' | 'setting'>('read');
-  const [property, setProperty] = useState<any>({});
-  const [propertyValue, setPropertyValue] = useState<any>('');
-
   const metadata = JSON.parse(InstanceModel.detail?.metadata || '{}');
 
   const subscribeLog = () => {
@@ -130,56 +109,6 @@ const Message = observer(() => {
       });
   };
 
-  const getItemNode = (t: string) => {
-    switch (t) {
-      case 'boolean':
-        return (
-          <Select
-            style={{ width: '100%', textAlign: 'left' }}
-            options={[
-              { label: 'true', value: true },
-              { label: 'false', value: false },
-            ]}
-            placeholder={'请选择'}
-            onChange={(value) => {
-              setPropertyValue(value);
-            }}
-          />
-        );
-      case 'int':
-      case 'long':
-      case 'float':
-      case 'double':
-        return (
-          <InputNumber
-            style={{ width: '100%' }}
-            placeholder={'请输入'}
-            onChange={(value) => {
-              setPropertyValue(value);
-            }}
-          />
-        );
-      case 'date':
-        return (
-          <DatePicker1
-            style={{ width: '100%' }}
-            onChange={(value: any) => {
-              setPropertyValue(value);
-            }}
-          />
-        );
-      default:
-        return (
-          <Input
-            onChange={(e) => {
-              setPropertyValue(e.target.value);
-            }}
-            placeholder="填写属性值"
-            style={{ width: '100%' }}
-          />
-        );
-    }
-  };
   useEffect(() => {
     if (DiagnoseStatusModel.state === 'success') {
       subscribeLog();
@@ -222,81 +151,76 @@ const Message = observer(() => {
     [],
   );
 
-  const dataRender = () => {
-    switch (type) {
-      case 'function':
-        return (
-          <Col span={5}>
-            <Select
-              style={{ width: '100%' }}
-              onChange={(value: any) => {
-                const data = (metadata?.functions || []).find((item: any) => item.id === value);
-                setInput(data);
-                setInputs(data?.inputs || []);
-                form.setValues({
-                  data: data?.inputs || [],
+  const _form = useMemo(
+    () =>
+      createForm({
+        initialValues: {
+          type: 'function',
+        },
+        effects() {
+          onFieldValueChange('property', (field) => {
+            const value = (field as Field).value;
+            const valueType = (metadata?.properties || []).find((it: any) => it.id === value)
+              ?.valueType?.type;
+            const format = field.query('.propertyValue').take() as any;
+            format.setValue('');
+            switch (valueType) {
+              case 'date':
+                format.setComponent(FDatePicker);
+                format.setComponentProps({
+                  placeholder: '请选择',
                 });
-              }}
-            >
-              {(metadata?.functions || []).map((i: any) => (
-                <Select.Option key={i.id} value={i.id}>
-                  {i.name}
-                </Select.Option>
-              ))}
-            </Select>
-          </Col>
-        );
-      case 'property':
-        return (
-          <>
-            <Col span={5}>
-              <Select
-                style={{ width: '100%' }}
-                value={propertyType}
-                placeholder="请选择"
-                onChange={(value: any) => {
-                  setPropertyType(value);
-                }}
-              >
-                <Select.Option value={'read'}>读取属性</Select.Option>
-                <Select.Option value={'setting'}>设置属性</Select.Option>
-              </Select>
-            </Col>
-            <Col span={5}>
-              <Select
-                style={{ width: '100%' }}
-                value={property}
-                placeholder="选择属性"
-                onChange={(value: any) => {
-                  setProperty(value);
-                }}
-              >
-                {(metadata?.properties || []).map((i: any) => (
-                  <Select.Option key={i.id} value={i.id}>
-                    {i.name}
-                  </Select.Option>
-                ))}
-              </Select>
-            </Col>
-            {!!property && propertyType === 'setting' && (
-              <Col span={5}>
-                {getItemNode(
-                  (metadata?.properties || []).find((it: any) => it.id === property)?.valueType
-                    ?.type || '',
-                )}
-              </Col>
-            )}
-          </>
-        );
-      default:
-        return null;
-    }
-  };
+                break;
+              case 'boolean':
+                format.setComponent(FSelect);
+                format.setDataSource([
+                  { label: '是', value: true },
+                  { label: '否', value: false },
+                ]);
+                format.setComponentProps({
+                  placeholder: '请选择',
+                });
+                break;
+              case 'int':
+              case 'long':
+              case 'float':
+              case 'double':
+                format.setComponent(NumberPicker);
+                format.setComponentProps({
+                  placeholder: '请输入',
+                });
+                break;
+              default:
+                format.setComponent(FInput);
+                format.setComponentProps({
+                  placeholder: '请输入',
+                });
+                break;
+            }
+          });
+          onFieldValueChange('function', (field) => {
+            const value = (field as Field).value;
+            setInputs([]);
+            setInput({});
+            if (value) {
+              const data = (metadata?.functions || []).find((item: any) => item.id === value);
+              setInput(data);
+              setInputs(data?.inputs || []);
+              form.setValues({
+                data: data?.inputs || [],
+              });
+            }
+          });
+        },
+      }),
+    [],
+  );
 
   const SchemaField = createSchemaField({
     components: {
       FormItem,
       FInput,
+      FormGrid,
       ArrayTable,
       PreviewText,
       FSelect,
@@ -305,6 +229,147 @@ const Message = observer(() => {
     },
   });
 
+  const _schema = {
+    type: 'object',
+    properties: {
+      grid: {
+        type: 'void',
+        'x-component': 'FormGrid',
+        'x-component-props': {
+          minColumns: [4],
+        },
+        properties: {
+          type: {
+            type: 'string',
+            title: '',
+            'x-decorator': 'FormItem',
+            'x-component': 'FSelect',
+            'x-component-props': {
+              placeholder: '请选择',
+            },
+            enum: [
+              { label: '调用功能', value: 'function' },
+              { label: '操作属性', value: 'property' },
+            ],
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择',
+              },
+            ],
+          },
+          function: {
+            type: 'string',
+            title: '',
+            'x-decorator': 'FormItem',
+            'x-component': 'FSelect',
+            'x-component-props': {
+              placeholder: '请选择',
+            },
+            enum: (metadata?.functions || []).map((item: any) => {
+              return { label: item.name, value: item.id };
+            }),
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择',
+              },
+            ],
+            'x-reactions': [
+              {
+                dependencies: ['.type'],
+                fulfill: {
+                  state: {
+                    visible: '{{$deps[0] === "function"}}',
+                  },
+                },
+              },
+            ],
+          },
+          propertyType: {
+            type: 'string',
+            title: '',
+            'x-decorator': 'FormItem',
+            'x-component': 'FSelect',
+            'x-component-props': {
+              placeholder: '请选择',
+            },
+            enum: [
+              { label: '读取属性', value: 'read' },
+              { label: '设置属性', value: 'setting' },
+            ],
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择',
+              },
+            ],
+            'x-reactions': [
+              {
+                dependencies: ['.type'],
+                fulfill: {
+                  state: {
+                    visible: '{{$deps[0] === "property"}}',
+                  },
+                },
+              },
+            ],
+          },
+          property: {
+            type: 'string',
+            title: '',
+            'x-decorator': 'FormItem',
+            'x-component': 'FSelect',
+            'x-component-props': {
+              placeholder: '请选择属性',
+            },
+            enum: (metadata?.properties || []).map((item: any) => {
+              return { label: item.name, value: item.id };
+            }),
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择属性',
+              },
+            ],
+            'x-reactions': [
+              {
+                dependencies: ['.type'],
+                fulfill: {
+                  state: {
+                    visible: '{{$deps[0] === "property"}}',
+                  },
+                },
+              },
+            ],
+          },
+          propertyValue: {
+            type: 'string',
+            title: '',
+            'x-decorator': 'FormItem',
+            'x-component': 'FInput',
+            'x-validator': [
+              {
+                required: true,
+                message: '改字段必填',
+              },
+            ],
+            'x-reactions': [
+              {
+                dependencies: ['.property', 'propertyType'],
+                fulfill: {
+                  state: {
+                    visible: '{{!!$deps[0] && $deps[1] === "setting"}}',
+                  },
+                },
+              },
+            ],
+          },
+        },
+      },
+    },
+  };
+
   const schema = {
     type: 'object',
     properties: {
@@ -356,7 +421,13 @@ const Message = observer(() => {
                 value: {
                   type: 'string',
                   'x-decorator': 'FormItem',
-                  'x-component': FInput,
+                  'x-component': 'FInput',
+                  'x-validator': [
+                    {
+                      required: true,
+                      message: '改字段必填',
+                    },
+                  ],
                 },
               },
             },
@@ -377,7 +448,7 @@ const Message = observer(() => {
                 return (
                   <Col key={key} span={12}>
                     <div style={messageStyleMap.get(obj.status)} className="message-status">
-                      <Badge status={messageStatusMap.get(obj.status)} />
+                      <Badge status={messageStatusMap.get(obj.status)} style={{ marginRight: 5 }} />
                       {obj.text}
                     </div>
                   </Col>
@@ -395,53 +466,48 @@ const Message = observer(() => {
               </div>
             </div>
             <div className="function">
-              <Row gutter={24}>
-                <Col span={5}>
-                  <Select
-                    value={type}
-                    placeholder="请选择"
-                    style={{ width: '100%' }}
-                    onChange={(value) => {
-                      setType(value);
-                      setInputs([]);
-                      setInput({});
-                    }}
-                  >
-                    <Select.Option value="function">调用功能</Select.Option>
-                    <Select.Option value="property">操作属性</Select.Option>
-                  </Select>
-                </Col>
-                {dataRender()}
-                <Col span={3}>
-                  <Button
-                    type="primary"
-                    onClick={async () => {
-                      if (type === 'function') {
-                        const list = (form?.values?.data || []).filter((it) => !!it.value);
-                        const obj = {};
-                        list.map((it) => {
-                          obj[it.id] = it.value;
-                        });
-                        await service.executeFunctions(InstanceModel.detail?.id || '', input.id, {
-                          ...obj,
-                        });
+              <div className={'function-item'}>
+                <div className={'function-item-form'}>
+                  <FormProvider form={_form}>
+                    <SchemaField schema={_schema} />
+                  </FormProvider>
+                </div>
+                <Button
+                  type="primary"
+                  className={'function-item-btn'}
+                  onClick={async () => {
+                    const values = await _form.submit<any>();
+                    let _inputs = undefined;
+                    if (inputs.length) {
+                      _inputs = await form.submit<any>();
+                    }
+                    if (values.type === 'function') {
+                      const list = (_inputs?.data || []).filter((it: any) => !!it.value);
+                      const obj = {};
+                      list.map((it: any) => {
+                        obj[it.id] = it.value;
+                      });
+                      await service.executeFunctions(InstanceModel.detail?.id || '', input.id, {
+                        ...obj,
+                      });
+                    } else {
+                      if (values.propertyType === 'read') {
+                        await service.readProperties(InstanceModel.detail?.id || '', [
+                          values.property,
+                        ]);
                       } else {
-                        if (propertyType === 'read') {
-                          await service.readProperties(InstanceModel.detail?.id || '', [property]);
-                        } else {
-                          await service.settingProperties(InstanceModel.detail?.id || '', {
-                            [property]: propertyValue,
-                          });
-                        }
+                        await service.settingProperties(InstanceModel.detail?.id || '', {
+                          [values.property]: values.propertyValue,
+                        });
                       }
-                    }}
-                  >
-                    发送
-                  </Button>
-                </Col>
-              </Row>
+                    }
+                  }}
+                >
+                  发送
+                </Button>
+              </div>
               {inputs.length > 0 && (
-                <div className="parameter">
+                <div className="inputs-parameter">
                   <h4>功能参数</h4>
                   <FormProvider form={form}>
                     <SchemaField schema={schema} />

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

@@ -80,7 +80,7 @@ const Log = () => {
   ];
 
   return (
-    <Card className={'device-detail-log'} style={{ minHeight }}>
+    <Card className={'device-detail-log'} style={{ minHeight }} bodyStyle={{ padding: 0 }}>
       <SearchComponent<LogItem>
         field={[...columns]}
         target="logs"
@@ -108,6 +108,7 @@ const Log = () => {
         pagination={{
           pageSize: 10,
         }}
+        style={{ padding: '0 24px' }}
         request={async (params, sort) => {
           let sorts;
           if (sort.timestamp) {

+ 3 - 3
src/pages/device/Instance/Detail/MetadataLog/Property/Detail.tsx

@@ -23,7 +23,7 @@ const Detail = (props: Props) => {
                 displayDataTypes={false}
                 style={{ marginTop: 10 }}
                 name={false}
-                src={value}
+                src={value?.formatValue}
               />
             }
           </div>
@@ -33,14 +33,14 @@ const Detail = (props: Props) => {
       return (
         <div>
           <div>自定义属性</div>
-          <Input.TextArea value={value} rows={3} />
+          <Input.TextArea value={value?.formatValue} rows={3} />
         </div>
       );
     } else {
       return (
         <div>
           <div>自定义属性</div>
-          <Input value={value} disabled />
+          <Input value={value?.formatValue} disabled />
         </div>
       );
     }

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

@@ -93,7 +93,7 @@ const PropertyLog = (props: Props) => {
             <SearchOutlined
               onClick={() => {
                 setDetailVisible(true);
-                setCurrent(record.value);
+                setCurrent(record);
               }}
             />
           )}

+ 11 - 9
src/pages/device/Instance/Detail/Running/Event/index.tsx

@@ -8,7 +8,7 @@ import moment from 'moment';
 import { Form, Modal } from 'antd';
 import { SearchOutlined } from '@ant-design/icons';
 import { useRef, useState } from 'react';
-import MonacoEditor from 'react-monaco-editor';
+import { JMonacoEditor } from '@/components/FMonacoEditor';
 
 interface Props {
   data: Partial<EventMetadata>;
@@ -46,14 +46,16 @@ const EventLog = (props: Props) => {
               title: '详情',
               width: 850,
               content: (
-                <Form.Item wrapperCol={{ span: 22 }} labelCol={{ span: 2 }} label={data.name}>
-                  <MonacoEditor
-                    height={350}
-                    theme="vs"
-                    language="json"
-                    value={JSON.stringify(record, null, 2)}
-                  />
-                </Form.Item>
+                <Form layout={'vertical'}>
+                  <Form.Item label={data.name}>
+                    <JMonacoEditor
+                      height={350}
+                      theme="vs"
+                      language="json"
+                      value={JSON.stringify(record, null, 2)}
+                    />
+                  </Form.Item>
+                </Form>
               ),
               okText: '关闭',
               onOk() {},

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

@@ -32,6 +32,11 @@ const EditProperty = (props: Props) => {
         title: data?.name || '自定义属性',
         required: true,
         'x-decorator': 'FormItem',
+        'x-decorator-props': {
+          gridSpan: 2,
+          labelAlign: 'left',
+          layout: 'vertical',
+        },
         'x-component': 'Input',
       },
     },

+ 38 - 10
src/pages/device/Instance/index.tsx

@@ -258,17 +258,16 @@ const Instance = () => {
       filterMultiple: true,
     },
     {
-      title: intl.formatMessage({
-        id: 'pages.device.instance.registrationTime',
-        defaultMessage: '注册时间',
-      }),
-      dataIndex: 'registryTime',
+      title: '创建时间',
+      dataIndex: 'createTime',
       width: '200px',
       valueType: 'dateTime',
       render: (_: any, row) => {
         return row.registryTime ? moment(row.registryTime).format('YYYY-MM-DD HH:mm:ss') : '';
       },
+      defaultSortOrder: 'descend',
       sorter: true,
+      sortDirections: ['ascend', 'descend'],
     },
     {
       title: intl.formatMessage({
@@ -656,17 +655,46 @@ const Instance = () => {
         params={searchParams}
         options={{ fullScreen: true }}
         columnEmptyText={''}
-        request={(params) =>
-          service.query({
+        // request={(params,sort) =>
+        //   service.query({
+        //     ...params,
+        //     sorts: [
+        //       {
+        //         name: 'createTime',
+        //         order: 'desc',
+        //       },
+        //     ],
+        //   })
+        // }
+        request={async (params, sort) => {
+          let sorts;
+          if (sort.createTime) {
+            sorts = sort.createTime === 'descend' ? 'desc' : 'asc';
+          } else {
+            sorts = 'desc';
+          }
+          console.log(sorts);
+          const res = await service.query({
             ...params,
             sorts: [
               {
                 name: 'createTime',
-                order: 'desc',
+                order: sorts,
               },
             ],
-          })
-        }
+          });
+          return {
+            code: res.message,
+            result: {
+              data: res.result.data,
+              pageIndex: res.result.pageIndex,
+              pageSize: res.result.pageSize,
+              total: res.result.total,
+            },
+            status: res.status,
+          };
+          // return service.queryLog(InstanceModel.detail.id!, params);
+        }}
         rowKey="id"
         search={false}
         tableAlertRender={({ selectedRowKeys }) => <div>已选择 {selectedRowKeys.length} 项</div>}

+ 12 - 6
src/pages/device/components/Metadata/Base/index.tsx

@@ -142,12 +142,18 @@ const BaseMetadata = observer((props: Props) => {
 
   const handleSearch = async (name: string) => {
     if (name) {
-      const result = await DB.getDB()
-        .table(`${type}`)
-        .where('id')
-        .startsWithAnyOfIgnoreCase(name)
-        .toArray();
-      setData(result.sort((a, b) => b?.sortsIndex - a?.sortsIndex));
+      // const result = await DB.getDB()
+      //   .table(`${type}`)
+      //   .where('name')
+      //   .startsWithAnyOfIgnoreCase(name)
+      //   .toArray();
+      // setData(result.sort((a, b) => b?.sortsIndex - a?.sortsIndex));
+      const result = await DB.getDB().table(`${type}`).toArray();
+      const arr = result
+        .filter((item) => item.name.indexOf(name) > -1)
+        .sort((a, b) => b?.sortsIndex - a?.sortsIndex);
+      // console.log(result, arr)
+      setData(arr);
     } else {
       await initData();
     }

+ 20 - 16
src/pages/home/Api/index.tsx

@@ -12,12 +12,16 @@ export default () => {
   const [clientId, setClientId] = useState('');
   const [secureKey, setSecureKey] = useState('');
   const [sdkDetail, setSdkDetail] = useState<any>({});
+  const [loading, setLoading] = useState<boolean>(false);
 
   const getDetail = async (id: string) => {
     const resp = await service.getDetail(id);
     if (resp.status === 200) {
       setClientId(resp.result?.id);
-      setSecureKey(resp.result?.secureKey);
+      setSecureKey(resp.result?.apiServer?.secureKey);
+      setLoading(true);
+    } else {
+      setLoading(true);
     }
   };
 
@@ -46,20 +50,12 @@ export default () => {
     getSDKDetail();
     api.userDetail().then((res) => {
       if (res.status === 200) {
-        api
-          .apiDetail({
-            terms: [
-              {
-                column: 'userId',
-                value: res.result.id,
-              },
-            ],
-          })
-          .then((response) => {
-            if (response.status === 200) {
-              getDetail(response.result.data[0].id);
-            }
-          });
+        api.apiDetail(res.result.id).then((response) => {
+          if (response.status === 200) {
+            console.log(response.result);
+            getDetail(response.result.id);
+          }
+        });
       }
     });
   }, []);
@@ -118,7 +114,15 @@ export default () => {
       </Col>
       <Col span={24}>
         <Card title={'API文档'}>
-          <ApiPage type={'authorize'} showDebugger={true} isShowGranted={true} showHome={true} />
+          {loading && (
+            <ApiPage
+              type={'authorize'}
+              showDebugger={true}
+              isShowGranted={true}
+              showHome={true}
+              code={clientId}
+            />
+          )}
         </Card>
       </Col>
     </Row>

+ 10 - 0
src/pages/home/device/index.tsx

@@ -17,7 +17,9 @@ const Device = () => {
   const rulePermission = PermissionButton.usePermission('rule-engine/Instance').permission;
 
   const [productCount, setProductCount] = useState<number>(0);
+  const [productMessage, setProductMessage] = useState<string>();
   const [deviceCount, setDeviceCount] = useState<number>(0);
+  const [deviceMessage, setDeviceMessage] = useState<string>();
 
   const [productVisible, setProductVisible] = useState<boolean>(false);
   const [deviceVisible, setDeviceVisible] = useState<boolean>(false);
@@ -27,6 +29,9 @@ const Device = () => {
     if (resp.status === 200) {
       setProductCount(resp.result);
     }
+    if (resp.status === 403) {
+      setProductMessage('暂无产品权限');
+    }
   };
 
   const getDeviceCount = async () => {
@@ -34,6 +39,9 @@ const Device = () => {
     if (resp.status === 200) {
       setDeviceCount(resp.result);
     }
+    if (resp.status === 403) {
+      setDeviceMessage('暂无设备权限');
+    }
   };
 
   useEffect(() => {
@@ -95,11 +103,13 @@ const Device = () => {
               name: '产品数量',
               value: productCount,
               children: require('/public/images/home/top-2.png'),
+              permission: productMessage,
             },
             {
               name: '设备数量',
               value: deviceCount,
               children: '',
+              permission: deviceMessage,
             },
           ]}
           title="设备统计"

+ 1 - 1
src/pages/home/index.tsx

@@ -57,7 +57,7 @@ const Home = () => {
           const isApiUser = res.result.dimensions.find(
             (item: any) => item.type === 'api-client' || item.type.id === 'api-client',
           );
-          console.log(isApiUser);
+          // console.log(isApiUser);
           setApiUser(isApiUser);
           service.queryViews().then((resp) => {
             setLoading(false);

+ 9 - 4
src/pages/home/service.ts

@@ -33,10 +33,15 @@ class Service {
     request(`/${SystemConst.API_BASE}/user/detail`, {
       method: 'GET',
     });
-  apiDetail = (data: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/_query`, {
-      method: 'POST',
-      data,
+  // apiDetail = (data: any) =>
+  //   request(`/${SystemConst.API_BASE}/api-client/_query`, {
+  //     method: 'POST',
+  //     data,
+  //   });
+  apiDetail = (id: any) =>
+    request(`/${SystemConst.API_BASE}/application/${id}`, {
+      method: 'GET',
+      // data,
     });
   // settingDetail = (data?: any) =>
   //   request(`/${SystemConst.API_BASE}/system/config/scopes`, {

+ 3 - 0
src/pages/iot-card/Home/index.less

@@ -0,0 +1,3 @@
+.bottomImg {
+  padding-bottom: 30%;
+}

+ 3 - 1
src/pages/iot-card/Home/index.tsx

@@ -10,6 +10,7 @@ import Echarts from '@/components/DashBoard/echarts';
 import { EChartsOption } from 'echarts';
 import moment from 'moment';
 import Service from './service';
+import './index.less';
 
 export const service = new Service('');
 
@@ -210,8 +211,9 @@ export default () => {
             }
           />
         </Col>
-        <Col span={24}>
+        <Col span={24} style={{ backgroundColor: 'white', minHeight: 580 }}>
           <Body
+            className="bottomImg"
             title={'平台架构图'}
             english={'PLATFORM ARCHITECTURE DIAGRAM'}
             url={require('/public/images/iot-card/iotcard-home.png')}

+ 5 - 0
src/pages/iot-card/Recharge/index.less

@@ -0,0 +1,5 @@
+.record {
+  .ant-empty-normal {
+    margin: 12% 0;
+  }
+}

+ 1 - 0
src/pages/iot-card/Recharge/index.tsx

@@ -10,6 +10,7 @@ import { useRef, useState } from 'react';
 import Service from '../CardManagement/service';
 import Detail from './detail';
 import TopUp from './topUp';
+import './index.less';
 
 export const service = new Service('network/card');
 

+ 1 - 1
src/pages/link/AccessConfig/Detail/Cloud/index.tsx

@@ -81,7 +81,7 @@ const Cloud = (props: Props) => {
             provider={props.provider}
             data={props.data}
             config={{
-              config,
+              ...config,
               protocol: protocolCurrent,
             }}
             prev={prev}

+ 3 - 2
src/pages/link/DataCollect/DataGathering/index.less

@@ -3,8 +3,9 @@
   min-height: calc(100vh - 180px);
   .left {
     width: 300px;
-    margin-right: 20px;
-    padding: 10px;
+    margin-top: 24px;
+    margin-right: 12px;
+    padding-right: 10px;
     border-right: 1px solid #eee;
   }
   .right {

+ 1 - 1
src/pages/link/DataCollect/DataGathering/index.tsx

@@ -46,7 +46,7 @@ export default observer(() => {
 
   return (
     <PageContainer>
-      <Card bordered={false}>
+      <Card bordered={false} bodyStyle={{ paddingTop: 0 }}>
         <div className={styles.container}>
           <div className={styles.left}>
             <ChannelTree

+ 2 - 1
src/pages/link/DataCollect/components/Channel/index.tsx

@@ -268,7 +268,8 @@ export default observer((props: Props) => {
               justifyContent: 'flex-end',
               position: 'absolute',
               width: '100%',
-              bottom: 0,
+              bottom: 10,
+              right: '2%',
             }}
           >
             <Pagination

+ 3 - 1
src/pages/link/DataCollect/components/Device/index.tsx

@@ -219,6 +219,7 @@ export default observer((props: Props) => {
         loading={loading}
         style={{ minHeight, position: 'relative' }}
         className={'data-collect-collector'}
+        bodyStyle={{ paddingTop: !props.type ? 4 : 24 }}
       >
         <div>
           <div style={{ paddingBottom: 48, height: '100%' }}>
@@ -361,7 +362,8 @@ export default observer((props: Props) => {
                     justifyContent: 'flex-end',
                     position: 'absolute',
                     width: '100%',
-                    bottom: 0,
+                    bottom: 10,
+                    right: '2%',
                   }}
                 >
                   <Pagination

+ 3 - 0
src/pages/link/DataCollect/components/Point/Save/modbus.tsx

@@ -355,6 +355,9 @@ export default (props: Props) => {
             'x-decorator': 'FormItem',
             'x-decorator-props': {
               gridSpan: 2,
+              style: {
+                marginBottom: 8,
+              },
             },
             default: 3000,
             'x-component-props': {

+ 3 - 0
src/pages/link/DataCollect/components/Point/Save/opc-ua.tsx

@@ -166,6 +166,9 @@ export default (props: Props) => {
             'x-decorator': 'FormItem',
             'x-decorator-props': {
               gridSpan: 2,
+              style: {
+                marginBottom: 8,
+              },
             },
             default: 3000,
             'x-component-props': {

+ 3 - 1
src/pages/link/DataCollect/components/Point/index.tsx

@@ -212,6 +212,7 @@ const PointCard = observer((props: PointCardProps) => {
         bordered={false}
         className={'data-collect-point'}
         style={{ position: 'relative', minHeight }}
+        bodyStyle={{ paddingTop: !props.type ? 4 : 24 }}
       >
         <div>
           <div style={{ height: '100%', paddingBottom: 48 }}>
@@ -321,7 +322,8 @@ const PointCard = observer((props: PointCardProps) => {
                     justifyContent: 'flex-end',
                     position: 'absolute',
                     width: '100%',
-                    bottom: 0,
+                    bottom: 10,
+                    right: '2%',
                   }}
                 >
                   <Pagination

+ 6 - 0
src/pages/link/DataCollect/components/Tree/index.less

@@ -1,3 +1,9 @@
+.data-collect-tree {
+  :global(.ant-tree-node-selected) {
+    background-color: rgba(47, 84, 235, 0.06) !important;
+  }
+}
+
 .treeTitle {
   display: flex;
   align-items: center;

+ 2 - 0
src/pages/link/DataCollect/components/Tree/index.tsx

@@ -139,7 +139,9 @@ export default observer((props: Props) => {
         {TreeModel.dataSource.length ? (
           <Tree
             style={{ overflow: 'hidden' }}
+            className={styles['data-collect-tree']}
             showIcon
+            height={500}
             selectedKeys={TreeModel.selectedKeys}
             switcherIcon={<DownOutlined />}
           >

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

@@ -15,7 +15,7 @@ import {
 } from '@formily/antd';
 import type { ISchema } from '@formily/json-schema';
 import { useEffect, useMemo, useRef, useState } from 'react';
-import type { Field, FieldDataSource } from '@formily/core';
+import { Field, FieldDataSource, registerValidateRules } from '@formily/core';
 import { onFormInit } from '@formily/core';
 import { createForm, onFieldReact, onFieldValueChange } from '@formily/core';
 import { Card, Col, Row } from 'antd';
@@ -261,6 +261,16 @@ const Save = observer(() => {
         value: item.id,
       })),
     );
+
+  registerValidateRules({
+    IpOrDomain(value) {
+      if (!value) return '';
+      const regIp = /((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/;
+      const regDomain = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/;
+      return regIp.test(value) || regDomain.test(value) ? '' : '请输入IP或者域名';
+    },
+  });
+
   const clusterConfig: ISchema = {
     type: 'void',
     'x-component': 'FormGrid',
@@ -374,7 +384,11 @@ const Save = observer(() => {
         required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Input',
-        'x-validator': ['ipv4'],
+        'x-validator': [
+          {
+            IpOrDomain: true,
+          },
+        ],
         'x-component-props': {
           placeholder: '请输入公网地址',
         },
@@ -447,7 +461,11 @@ const Save = observer(() => {
               placeholder: '请输入远程地址',
             },
             required: true,
-            'x-validator': ['ipv4'],
+            'x-validator': [
+              {
+                IpOrDomain: true,
+              },
+            ],
             'x-decorator': 'FormItem',
             'x-component': 'Input',
           },
@@ -580,7 +598,8 @@ const Save = observer(() => {
           dependencies: ['type'],
           fulfill: {
             state: {
-              title: '{{$deps[0] === "TCP_SERVER" ? "开启TLS" : "开启DTLS"}}',
+              title:
+                '{{!["TCP_SERVER", "UDP", "COAP_SERVER"].includes($deps[0]) ? "开启TLS" : "开启DTLS"}}',
             },
           },
         },

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

@@ -18,7 +18,7 @@ import {
 } from 'antd';
 import SipComponent from '@/components/SipComponent';
 import SipSelectComponent from '@/components/SipSelectComponent';
-import { testIP } from '@/utils/util';
+// import { testIP } from '@/utils/util';
 import { useEffect, useState } from 'react';
 import { service } from '../index';
 import { useLocation } from 'umi';
@@ -31,13 +31,14 @@ const Save = () => {
   const id = location?.query?.id || '';
   const [list, setList] = useState<any[]>([]);
   const [transport, setTransport] = useState<'UDP' | 'TCP'>('UDP');
+  const regDomain = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/;
 
   const checkSIP = (_: any, value: { host: string; port: number }) => {
     if (!value) {
       return Promise.resolve();
     } else if (!value.host) {
       return Promise.reject(new Error('请输入IP 地址'));
-    } else if (value?.host && !testIP(value.host)) {
+    } else if (value?.host && !regDomain.test(value.host)) {
       return Promise.reject(new Error('请输入正确的IP地址'));
     } else if (!value?.port) {
       return Promise.reject(new Error('请输入端口'));

+ 5 - 5
src/pages/media/DashBoard/index.tsx

@@ -141,11 +141,11 @@ export default () => {
           type: 'value',
           // minInterval: 1,
         },
-        // grid: {
-        //   left: '4%',
-        //   right: '2%',
-        //   top: '2%',
-        // },
+        grid: {
+          left: 0,
+          right: 0,
+          top: '2%',
+        },
         color: ['#2F54EB'],
         series: [
           {

+ 15 - 10
src/pages/media/Device/Save/ProviderSelect.tsx

@@ -118,19 +118,24 @@ export default (props: ProviderProps) => {
                     <div className={styles.desc}>{item.description || ''}</div>
                   </div>
                   <div className={styles.container}>
-                    <div className={styles.server}>
-                      <div className={styles.subTitle}>{item?.channelInfo?.name || ''}</div>
-                      <div style={{ width: '100%' }}>
-                        {item.channelInfo?.addresses.map((i: any, index: number) => (
-                          <p key={i.address + `_address${index}`}>
-                            <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
-                          </p>
-                        ))}
+                    {props.type === 'gb28181-2016' && (
+                      <div className={styles.server}>
+                        <div className={styles.subTitle}>{item?.channelInfo?.name || ''}</div>
+                        <div style={{ width: '100%' }}>
+                          {item.channelInfo?.addresses.map((i: any, index: number) => (
+                            <p key={i.address + `_address${index}`}>
+                              <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
+                            </p>
+                          ))}
+                        </div>
                       </div>
-                    </div>
+                    )}
+
                     <div className={styles.procotol}>
                       <div className={styles.subTitle}>{item?.protocolDetail?.name || ''}</div>
-                      <p>{item.protocolDetail?.description || ''}</p>
+                      <p style={props.type === 'gb28181-2016' ? { width: 250 } : {}}>
+                        {item.protocolDetail?.description || ''}
+                      </p>
                     </div>
                   </div>
                 </div>

+ 8 - 7
src/pages/media/Stream/Detail/index.tsx

@@ -5,7 +5,7 @@ import { service, StreamModel } from '@/pages/media/Stream';
 import { useParams } from 'umi';
 import { QuestionCircleOutlined } from '@ant-design/icons';
 import SipComponent from '@/components/SipComponent';
-import { onlyMessage, testIP } from '@/utils/util';
+import { onlyMessage } from '@/utils/util';
 import useLocation from '@/hooks/route/useLocation';
 
 interface RTPComponentProps {
@@ -167,14 +167,15 @@ const Detail = () => {
     }
   }, [params.id]);
 
+  const regDomain = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/;
   const checkSIP = (_: any, value: { host: string; port: number }) => {
     if (!value) {
       return Promise.resolve();
     }
     if (!value || !value.host) {
       return Promise.reject(new Error('请输入API HOST'));
-    } else if (value?.host && !testIP(value.host)) {
-      return Promise.reject(new Error('请输入正确的IP地址'));
+    } else if (value?.host && !regDomain.test(value.host)) {
+      return Promise.reject(new Error('请输入正确的IP地址或者域名'));
     } else if (!value?.port) {
       return Promise.reject(new Error('请输入端口'));
     } else if ((value?.port && Number(value.port) < 1) || Number(value.port) > 65535) {
@@ -201,11 +202,11 @@ const Detail = () => {
     }
     if (!value || !value.rtpIp) {
       return Promise.reject(new Error('请输入RTP IP'));
-    } else if (value?.rtpIp && !testIP(value.rtpIp)) {
-      return Promise.reject(new Error('请输入正确的IP地址'));
+    } else if (value?.rtpIp && !regDomain.test(value.rtpIp)) {
+      return Promise.reject(new Error('请输入正确的IP地址或者域名'));
     } else if (!value.dynamicRtpPort) {
-      if (value.rtpIp && !testIP(value.rtpIp)) {
-        return Promise.reject(new Error('请输入正确的IP地址'));
+      if (value.rtpIp && !regDomain.test(value.rtpIp)) {
+        return Promise.reject(new Error('请输入正确的IP地址或者域名'));
       }
       if (!value?.rtpPort) {
         return Promise.reject(new Error('请输入端口'));

+ 1 - 1
src/pages/rule-engine/Alarm/Configuration/Save/Base/index.tsx

@@ -117,8 +117,8 @@ export default () => {
   });
 
   const handleSave = async () => {
-    setLoading(true);
     const data: any = await form.submit();
+    setLoading(true);
     const resp: any = await service.update({ ...data });
     setLoading(false);
     if (resp.status === 200) {

+ 1 - 1
src/pages/rule-engine/Alarm/Configuration/Save/Scene/Save/index.tsx

@@ -153,7 +153,7 @@ export default (props: Props) => {
                     {
                       column: 'triggerType',
                       termType: 'eq',
-                      value: props.type,
+                      value: props.type === 'other' ? undefined : 'device',
                     },
                   ],
                   type: 'and',

+ 11 - 0
src/pages/rule-engine/Alarm/Configuration/Save/Scene/index.less

@@ -0,0 +1,11 @@
+.alarm-scene-table {
+  :global {
+    .ant-card-body {
+      padding: 0;
+    }
+
+    .ant-pro-table-list-toolbar-container {
+      padding-top: 8px;
+    }
+  }
+}

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

@@ -11,6 +11,7 @@ import Save from './Save';
 import { service } from '@/pages/rule-engine/Alarm/Configuration';
 import { onlyMessage } from '@/utils/util';
 import { Store } from 'jetlinks-store';
+import styles from './index.less';
 
 export default () => {
   const intl = useIntl();
@@ -83,7 +84,7 @@ export default () => {
   ];
 
   return (
-    <>
+    <div className={styles['alarm-scene-table']}>
       <ProTableCard<SceneItem>
         columns={columns}
         actionRef={actionRef}
@@ -189,6 +190,6 @@ export default () => {
           }}
         />
       )}
-    </>
+    </div>
   );
 };

+ 43 - 31
src/pages/rule-engine/Alarm/Configuration/index.tsx

@@ -17,7 +17,7 @@ import ProTableCard from '@/components/ProTableCard';
 import Service from '@/pages/rule-engine/Alarm/Configuration/service';
 import AlarmConfig from '@/components/ProTableCard/CardItems/AlarmConfig';
 import { Store } from 'jetlinks-store';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { getMenuPathByCode } from '@/utils/menu';
 import { useHistory } from 'umi';
 import { onlyMessage } from '@/utils/util';
 import encodeQuery from '@/utils/encodeQuery';
@@ -104,33 +104,35 @@ const Configuration = () => {
       title: '关联场景联动',
       dataIndex: 'sceneId',
       width: 250,
-      render: (text: any, record: any) => (
-        <PermissionButton
-          type={'link'}
-          isPermission={!!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])}
-          style={{ padding: 0, height: 'auto' }}
-          tooltip={{
-            title: !!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])
-              ? text
-              : '没有权限,请联系管理员',
-          }}
-          onClick={() => {
-            const url = getMenuPathByCode('rule-engine/Scene/Save');
-            history.push(`${url}?id=${record.sceneId}`);
-          }}
-        >
-          <span
-            style={{
-              width: '200px',
-              overflow: 'hidden',
-              textAlign: 'left',
-              textOverflow: 'ellipsis',
-            }}
-          >
-            {record?.sceneName}
-          </span>
-        </PermissionButton>
-      ),
+      render: (text: any, record: any) =>
+        (record?.scene || []).map((item: any) => item?.name).join(',') || '',
+      // (
+      // <PermissionButton
+      //   type={'link'}
+      //   isPermission={!!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])}
+      //   style={{ padding: 0, height: 'auto' }}
+      //   tooltip={{
+      //     title: !!getMenuPathByCode(MENUS_CODE['rule-engine/Scene'])
+      //       ? text
+      //       : '没有权限,请联系管理员',
+      //   }}
+      //   onClick={() => {
+      //     const url = getMenuPathByCode('rule-engine/Scene/Save');
+      //     history.push(`${url}?id=${record.sceneId}`);
+      //   }}
+      // >
+      //   <span
+      //     style={{
+      //       width: '200px',
+      //       overflow: 'hidden',
+      //       textAlign: 'left',
+      //       textOverflow: 'ellipsis',
+      //     }}
+      //   >
+      //     {(record?.scene || []).map((item: any) => item?.name).join(',')}
+      //   </span>
+      // </PermissionButton>
+      // ),
       valueType: 'select',
       request: async () => {
         const res: any = await service.getScene(
@@ -194,7 +196,12 @@ const Configuration = () => {
               disabled: record.state?.value === 'disabled',
               title: '确认手动触发?',
               onConfirm: async () => {
-                await service._execute(record.sceneId);
+                const scene = (record?.scene || [])
+                  .filter((item: any) => item?.triggerType === 'manual')
+                  .map((i) => {
+                    return { id: i?.id };
+                  });
+                await service._execute(scene);
                 onlyMessage(
                   intl.formatMessage({
                     id: 'pages.data.option.success',
@@ -312,7 +319,7 @@ const Configuration = () => {
         columns={columns}
         columnEmptyText={''}
         request={(params) =>
-          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+          service.queryList({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
         }
         gridColumn={3}
         cardRender={(record) => (
@@ -332,7 +339,12 @@ const Configuration = () => {
                     title: '确认手动触发?',
                     disabled: record.state?.value === 'disabled',
                     onConfirm: async () => {
-                      await service._execute(record.sceneId);
+                      const scene = (record?.scene || [])
+                        .filter((item: any) => item?.triggerType === 'manual')
+                        .map((i: any) => {
+                          return { id: i?.id };
+                        });
+                      await service._execute(scene);
                       onlyMessage(
                         intl.formatMessage({
                           id: 'pages.data.option.success',

+ 8 - 3
src/pages/rule-engine/Alarm/Configuration/service.ts

@@ -3,6 +3,11 @@ import { request } from 'umi';
 import SystemConst from '@/utils/const';
 
 class Service extends BaseService<ConfigItem> {
+  public queryList = (data: any) =>
+    request(`/${SystemConst.API_BASE}/alarm/config/detail/_query`, {
+      method: 'POST',
+      data,
+    });
   public getTargetTypes = () =>
     request(`/${SystemConst.API_BASE}/alarm/config/target-type/supports`, {
       method: 'GET',
@@ -28,10 +33,10 @@ class Service extends BaseService<ConfigItem> {
     request(`/${SystemConst.API_BASE}/alarm/config/${id}/_disable`, {
       method: 'POST',
     });
-  public _execute = (id: string) =>
-    request(`/${SystemConst.API_BASE}/scene/${id}/_execute`, {
+  public _execute = (data: any) =>
+    request(`/${SystemConst.API_BASE}/scene/batch/_execute`, {
       method: 'POST',
-      data: {},
+      data,
     });
 
   public getAlarmCountById = (id: string) =>

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

@@ -4,6 +4,7 @@ type ConfigurationItem = {
   level: number;
   sceneName: string;
   sceneId: string;
+  scene?: any[];
   state: { text: string; value: string };
   description: string;
   id: string;

+ 1 - 1
src/pages/rule-engine/DashBoard/index.less

@@ -108,7 +108,7 @@
 
 .alarmRank {
   position: relative;
-  width: 300px;
+  width: 30%;
   padding-left: 48px;
 }
 

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

@@ -148,9 +148,9 @@ const Basis = () => {
                   >
                     <UploadImage
                       size={1}
-                      types={['image/x-icon', 'image/jpeg', 'image/png']}
+                      types={['image/x-icon']}
                       backgroundSize={'inherit'}
-                      errorMessage={'请上传.ico.jpg.png.jfif.pjp.pjpeg.jpeg格式的图片'}
+                      errorMessage={'请上传ico格式的图片'}
                     />
                   </Form.Item>
                 </Col>

+ 2 - 5
src/pages/system/DataSource/Save/index.tsx

@@ -96,6 +96,7 @@ const Save = (props: Props) => {
               },
             ],
             required: true,
+            'x-disabled': !!props.data.id,
             enum: Store.get('datasource-type'),
           },
           'shareConfig.url': {
@@ -360,11 +361,7 @@ const Save = (props: Props) => {
         props.close();
       }}
       onOk={() => {
-        if ((props.data.id && props.data.typeId === 'rdb') || !props.data.id) {
-          handleSave();
-        } else {
-          onlyMessage('该类型数据库不可以编辑', 'warning');
-        }
+        handleSave();
       }}
     >
       <Form form={form} layout="vertical">

+ 7 - 2
src/pages/system/DataSource/index.tsx

@@ -139,13 +139,18 @@ const DataSource = observer(() => {
           type="link"
           isPermission={userPermission.manage}
           key="manage"
-          disabled={record.state?.value !== 'enabled'}
+          disabled={record.state?.value !== 'enabled' || record?.typeId === 'rabbitmq'}
           onClick={() => {
             const url = getMenuPathByCode(MENUS_CODE[`system/DataSource/Management`]);
             history.push(`${url}?id=${record.id}`);
           }}
           tooltip={{
-            title: record.state?.value !== 'enabled' ? '请先启用数据源' : '管理',
+            title:
+              record?.typeId === 'rabbitmq'
+                ? '暂不支持管理功能'
+                : record.state?.value !== 'enabled'
+                ? '请先启用数据源'
+                : '管理',
           }}
         >
           <DatabaseOutlined />

+ 5 - 2
src/pages/system/Platforms/Api/base.tsx

@@ -38,6 +38,7 @@ interface ApiPageProps {
   isOpenGranted?: boolean;
   type?: 'all' | 'empowerment' | 'authorize';
   showHome?: boolean;
+  code?: string;
 }
 
 export default observer((props: ApiPageProps) => {
@@ -70,7 +71,7 @@ export default observer((props: ApiPageProps) => {
    */
   const getApiGrant = useCallback(() => {
     const param = new URLSearchParams(location.search);
-    const code = param.get('code');
+    const code = props.code ? props.code : param.get('code');
 
     if (props.isOpenGranted === false) {
       service.apiOperations().then((resp: any) => {
@@ -79,18 +80,20 @@ export default observer((props: ApiPageProps) => {
         }
       });
     } else {
+      console.log(props.code, 1111111);
       service.getApiGranted(code!).then((resp: any) => {
         if (resp.status === 200) {
           setGrantKeys(resp.result);
         }
       });
     }
-  }, [location, props.isOpenGranted]);
+  }, [location, props.isOpenGranted, props.code]);
 
   useEffect(() => {
     initModel();
     getOperations();
     getApiGrant();
+    console.log(props.code);
   }, []);
 
   return (

+ 1 - 0
src/pages/system/Platforms/Api/swagger-ui/base.tsx

@@ -246,6 +246,7 @@ export default observer(() => {
             }}
             value={ApiModel.swagger.url}
             readOnly
+            disabled
           />
         </Input.Group>
       </div>

+ 36 - 3
src/pages/system/Platforms/Api/swagger-ui/debugging.tsx

@@ -2,7 +2,7 @@ import { TitleComponent } from '@/components';
 import ReactJson from 'react-json-view';
 import { request } from 'umi';
 import MonacoEditor from 'react-monaco-editor';
-import { Button, Input } from 'antd';
+import { Button, Input, Popconfirm } from 'antd';
 import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import { createSchemaField, FormProvider, observer } from '@formily/react';
 import { ApiModel } from '@/pages/system/Platforms/Api/base';
@@ -11,6 +11,7 @@ import { ArrayTable, Editable, FormItem, Input as FormilyInput } from '@formily/
 import type { ISchema } from '@formily/json-schema';
 import SystemConst from '@/utils/const';
 import classNames from 'classnames';
+import { DeleteOutlined } from '@ant-design/icons';
 
 export default observer(() => {
   const [result, setResult] = useState({});
@@ -18,7 +19,36 @@ export default observer(() => {
 
   const editor: any = useRef(null);
 
+  const RemoveButton = () => {
+    const index = ArrayTable.useIndex!();
+    const array = ArrayTable.useArray!();
+    return (
+      <Popconfirm
+        title="确定删除"
+        onConfirm={() => {
+          array.field?.remove?.(index);
+          array.props?.onRemove?.(index);
+        }}
+      >
+        <DeleteOutlined />
+      </Popconfirm>
+    );
+  };
+
+  const isBody = () => {
+    const obj = Object.keys(ApiModel.debugger?.body || {});
+    const url = ApiModel.swagger.url.indexOf('{' || '}');
+    if (ApiModel.debugger.body && obj.length !== 0) {
+      return true;
+    } else if (url !== -1) {
+      return true;
+    } else {
+      return false;
+    }
+  };
+
   useEffect(() => {
+    console.log('ApiModel.debugger.body', ApiModel.debugger.body);
     if (ApiModel.debugger.body && editor.current) {
       const { editor: MEditor } = editor.current;
       MEditor.setValue(JSON.stringify(ApiModel.debugger.body));
@@ -36,6 +66,7 @@ export default observer(() => {
       Editable,
       Input: FormilyInput,
       ArrayTable,
+      RemoveButton,
     },
   });
 
@@ -100,6 +131,7 @@ export default observer(() => {
   }, [body]);
 
   useEffect(() => {
+    // console.log('ApiModel.debugger',ApiModel.debugger)
     if (form && ApiModel.debugger && ApiModel.debugger.params) {
       const arr = ApiModel.debugger.params.map((item: any) => {
         return {
@@ -168,7 +200,7 @@ export default observer(() => {
                   properties: {
                     remove: {
                       type: 'void',
-                      'x-component': 'ArrayTable.Remove',
+                      'x-component': 'RemoveButton',
                     },
                   },
                 },
@@ -200,6 +232,7 @@ export default observer(() => {
               width: `calc(100% - ${ApiModel.swagger.method !== 'delete' ? '140px' : '150px'})`,
             }}
             value={ApiModel.swagger.url}
+            disabled
           />
           <Button type="primary" onClick={onSearch}>
             发送
@@ -214,7 +247,7 @@ export default observer(() => {
               <SchemaField schema={schema} />
             </FormProvider>
           )}
-          {ApiModel.debugger.body && (
+          {isBody() && (
             <MonacoEditor
               height={200}
               language={'json'}

+ 2 - 0
src/pages/system/Platforms/Api/swagger-ui/index.tsx

@@ -13,6 +13,8 @@ export default (props: SwaggerProps) => {
       <Button
         onClick={() => {
           ApiModel.showTable = true;
+          ApiModel.swagger = {};
+          ApiModel.debugger = {};
         }}
         className={'platforms-api-swagger-back'}
       >

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

@@ -1,6 +1,11 @@
 import { useIntl } from 'umi';
 import type { Field } from '@formily/core';
-import { createForm, onFieldInputValueChange, onFieldValueChange } from '@formily/core';
+import {
+  createForm,
+  onFieldInputValueChange,
+  onFieldValueChange,
+  registerValidateRules,
+} from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import React from 'react';
 import * as ICONS from '@ant-design/icons';
@@ -114,6 +119,14 @@ const Save = (props: Props) => {
     },
   });
 
+  registerValidateRules({
+    validateId(value) {
+      if (!value) return '';
+      const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
+      return reg.exec(value) ? '' : '标识只能由数字、字母、下划线、中划线组成';
+    },
+  });
+
   const schema: ISchema = {
     type: 'object',
     properties: {
@@ -170,6 +183,10 @@ const Save = (props: Props) => {
                 required: true,
                 message: '请输入标识',
               },
+              {
+                validateId: true,
+                message: '标识只能由数字、26个英文字母或者下划线组成',
+              },
             ],
             name: 'relation',
             required: true,

+ 11 - 1
src/pages/system/Relationship/index.tsx

@@ -45,9 +45,19 @@ const Relationship = () => {
       },
     },
     {
-      dataIndex: 'targetTypeName',
+      dataIndex: 'targetType',
       title: '被关联方',
       ellipsis: true,
+      valueType: 'select',
+      render: (_, row) => {
+        return row.targetTypeName;
+      },
+      valueEnum: {
+        user: {
+          text: '用户',
+          status: 'user',
+        },
+      },
     },
     {
       dataIndex: 'description',

+ 7 - 1
src/pages/system/Role/Detail/Permission/index.tsx

@@ -97,7 +97,13 @@ const Permission = () => {
               <Form.Item
                 label="名称"
                 name="name"
-                rules={[{ required: true, message: '请输入名称!' }]}
+                rules={[
+                  { required: true, message: '请输入名称!' },
+                  {
+                    max: 64,
+                    message: '最多可输入64个字符',
+                  },
+                ]}
               >
                 <Input />
               </Form.Item>

+ 9 - 4
src/pages/system/User/Save/index.tsx

@@ -338,8 +338,10 @@ const Save = (props: Props) => {
               gridSpan: 1,
               addonAfter: (
                 <PermissionButton
-                  type="link"
-                  style={{ padding: 0 }}
+                  // type="link"
+                  type="primary"
+                  ghost
+                  style={{ padding: '0 8px' }}
                   isPermission={rolePermission.add}
                   onClick={() => {
                     const tab: any = window.open(`${origin}/#/system/role?save=true`);
@@ -383,8 +385,11 @@ const Save = (props: Props) => {
               gridSpan: 1,
               addonAfter: (
                 <PermissionButton
-                  type="link"
-                  style={{ padding: 0 }}
+                  // type="link"
+                  // style={{ padding: 0 }}
+                  type="primary"
+                  ghost
+                  style={{ padding: '0 8px' }}
                   isPermission={deptPermission.add}
                   onClick={() => {
                     const tab: any = window.open(`${origin}/#/system/department?save=true`);