Просмотр исходного кода

feat(merge): merge xyh

Next xyh
Lind 3 лет назад
Родитель
Сommit
e219935e3c
33 измененных файлов с 319 добавлено и 283 удалено
  1. 13 13
      src/components/BaseCrud/index.tsx
  2. 5 5
      src/components/Metadata/ArrayParam/index.tsx
  3. 4 4
      src/components/PermissionButton/index.tsx
  4. 9 4
      src/components/Player/index.tsx
  5. 4 4
      src/components/ProTableCard/CardItems/AccessConfig/index.tsx
  6. 4 2
      src/components/ProTableCard/index.less
  7. 6 6
      src/components/ProTableCard/index.tsx
  8. 4 4
      src/hooks/permission/index.ts
  9. 8 8
      src/pages/device/Category/Save/index.tsx
  10. 12 7
      src/pages/device/Instance/Detail/Config/index.tsx
  11. 8 8
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  12. 4 4
      src/pages/device/Instance/Detail/Diagnose/index.tsx
  13. 8 8
      src/pages/device/Instance/Detail/Info/index.tsx
  14. 3 3
      src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx
  15. 12 12
      src/pages/device/Instance/Detail/index.tsx
  16. 7 7
      src/pages/device/Instance/Save/index.tsx
  17. 24 15
      src/pages/device/Instance/index.tsx
  18. 1 1
      src/pages/device/Product/Detail/Access/index.tsx
  19. 1 3
      src/pages/device/Product/Detail/index.tsx
  20. 28 35
      src/pages/device/Product/index.tsx
  21. 14 14
      src/pages/device/components/Metadata/Base/index.tsx
  22. 8 8
      src/pages/device/components/Metadata/index.tsx
  23. 7 1
      src/pages/media/Device/Playback/index.tsx
  24. 8 14
      src/pages/notice/Config/Detail/doc/AliyunSms.tsx
  25. 8 14
      src/pages/notice/Config/Detail/doc/AliyunVoice.tsx
  26. 9 15
      src/pages/notice/Config/Detail/doc/DingTalk.tsx
  27. 10 9
      src/pages/notice/Config/Detail/doc/DingTalkRebot.tsx
  28. 9 7
      src/pages/notice/Config/Detail/doc/Email.tsx
  29. 9 15
      src/pages/notice/Config/Detail/doc/WeixinApp.tsx
  30. 9 15
      src/pages/notice/Config/Detail/doc/WeixinCorp.tsx
  31. 33 0
      src/pages/notice/Config/Detail/doc/index.less
  32. 9 9
      src/pages/system/Menu/Detail/buttons.tsx
  33. 21 9
      src/pages/system/Menu/Detail/edit.tsx

+ 13 - 13
src/components/BaseCrud/index.tsx

@@ -1,22 +1,22 @@
-import {useIntl} from '@@/plugin-locale/localeExports';
-import {Button, Tooltip} from 'antd';
-import type {ActionType, ProColumns, RequestData} from '@jetlinks/pro-table';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { Button, Tooltip } from 'antd';
+import type { ActionType, ProColumns, RequestData } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 
-import {PlusOutlined} from '@ant-design/icons';
+import { PlusOutlined } from '@ant-design/icons';
 import type BaseService from '@/utils/BaseService';
 import * as React from 'react';
-import {useRef, useState} from 'react';
+import { useRef, useState } from 'react';
 import Save from '@/components/BaseCrud/save';
-import type {ISchema} from '@formily/json-schema';
-import {CurdModel} from '@/components/BaseCrud/model';
-import type {ISchemaFieldProps} from '@formily/react/lib/types';
-import type {ModalProps} from 'antd/lib/modal/Modal';
-import type {TablePaginationConfig} from 'antd/lib/table/interface';
-import type {Form} from '@formily/core';
+import type { ISchema } from '@formily/json-schema';
+import { CurdModel } from '@/components/BaseCrud/model';
+import type { ISchemaFieldProps } from '@formily/react/lib/types';
+import type { ModalProps } from 'antd/lib/modal/Modal';
+import type { TablePaginationConfig } from 'antd/lib/table/interface';
+import type { Form } from '@formily/core';
 import SearchComponent from '@/components/SearchComponent';
-import type {ProFormInstance} from '@ant-design/pro-form';
-import type {SearchConfig} from '@ant-design/pro-form/lib/components/Submitter';
+import type { ProFormInstance } from '@ant-design/pro-form';
+import type { SearchConfig } from '@ant-design/pro-form/lib/components/Submitter';
 
 export type Option = {
   model: 'edit' | 'preview' | 'add';

+ 5 - 5
src/components/Metadata/ArrayParam/index.tsx

@@ -1,9 +1,9 @@
-import {createSchemaField} from '@formily/react';
-import {Editable, FormItem, FormLayout, Input, NumberPicker, Select} from '@formily/antd';
-import type {ISchema} from '@formily/json-schema';
+import { createSchemaField } from '@formily/react';
+import { Editable, FormItem, FormLayout, Input, NumberPicker, Select } from '@formily/antd';
+import type { ISchema } from '@formily/json-schema';
 import './index.less';
-import {DataTypeList, DateTypeList, FileTypeList} from '@/pages/device/data';
-import {Store} from 'jetlinks-store';
+import { DataTypeList, DateTypeList, FileTypeList } from '@/pages/device/data';
+import { Store } from 'jetlinks-store';
 import JsonParam from '@/components/Metadata/JsonParam';
 import EnumParam from '@/components/Metadata/EnumParam';
 import BooleanEnum from '@/components/Metadata/BooleanParam';

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

@@ -1,8 +1,8 @@
-import type {ButtonProps, PopconfirmProps, TooltipProps} from 'antd';
-import {Button, Popconfirm, Tooltip} from 'antd';
+import type { ButtonProps, PopconfirmProps, TooltipProps } from 'antd';
+import { Button, Popconfirm, Tooltip } from 'antd';
 import usePermissions from '@/hooks/permission';
-import {useCallback} from 'react';
-import {useIntl} from '@@/plugin-locale/localeExports';
+import { useCallback } from 'react';
+import { useIntl } from '@@/plugin-locale/localeExports';
 
 interface PermissionButtonProps extends ButtonProps {
   tooltip?: TooltipProps;

+ 9 - 4
src/components/Player/index.tsx

@@ -1,4 +1,4 @@
-import { useEffect, useRef } from 'react';
+import { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
 import { isFunction } from 'lodash';
 
 export type PlayerProps = {
@@ -37,13 +37,12 @@ const EventsEnum = {
   snapInside: 'onSnapInside',
   customButtons: 'onCustomButtons',
 };
-export default (props: PlayerProps) => {
+const LivePlayer = forwardRef((props: PlayerProps, ref) => {
   const player = useRef<HTMLVideoElement>(null);
 
   useEffect(() => {
     return () => {
       // 销毁播放器
-      console.log('销毁', props);
       if ('onDestroy' in props && isFunction(props.onDestroy)) {
         props.onDestroy();
       }
@@ -63,6 +62,10 @@ export default (props: PlayerProps) => {
     }
   };
 
+  useImperativeHandle(ref, () => ({
+    ...player.current,
+  }));
+
   return (
     // @ts-ignore: Unreachable code error
     <live-player
@@ -84,4 +87,6 @@ export default (props: PlayerProps) => {
       video-url={props.url || ''}
     />
   );
-};
+});
+
+export default LivePlayer;

+ 4 - 4
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
-import {StatusColorEnum} from '@/components/BadgeStatus';
-import {TableCard} from '@/components';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import { TableCard } from '@/components';
 import '@/style/common.less';
-import {Badge, Tooltip} from 'antd';
-import type {AccessItem} from '@/pages/link/AccessConfig/typings';
+import { Badge, Tooltip } from 'antd';
+import type { AccessItem } from '@/pages/link/AccessConfig/typings';
 import './index.less';
 import classNames from 'classnames';
 

+ 4 - 2
src/components/ProTableCard/index.less

@@ -112,10 +112,12 @@
         top: 0;
         left: @card-content-padding-left + 10px;
         display: block;
-        width: 120px;
-        height: 4px;
+        width: 15%;
+        min-width: 64px;
+        height: 2px;
         background-image: url('/images/rectangle.png');
         background-repeat: no-repeat;
+        background-size: 100% 100%;
         content: ' ';
       }
 

+ 6 - 6
src/components/ProTableCard/index.tsx

@@ -1,10 +1,10 @@
-import type {ProTableProps} from '@jetlinks/pro-table';
+import type { ProTableProps } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
-import type {ParamsType} from '@ant-design/pro-provider';
-import React, {useEffect, useState} from 'react';
-import {isFunction} from 'lodash';
-import {Empty, Pagination, Space} from 'antd';
-import {AppstoreOutlined, BarsOutlined} from '@ant-design/icons';
+import type { ParamsType } from '@ant-design/pro-provider';
+import React, { useEffect, useState } from 'react';
+import { isFunction } from 'lodash';
+import { Empty, Pagination, Space } from 'antd';
+import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
 import classNames from 'classnames';
 import './index.less';
 

+ 4 - 4
src/hooks/permission/index.ts

@@ -1,7 +1,7 @@
-import {useEffect, useState} from 'react';
-import type {BUTTON_PERMISSION, MENUS_CODE_TYPE} from '@/utils/menu/router';
-import {BUTTON_PERMISSION_ENUM} from '@/utils/menu/router';
-import {MENUS_BUTTONS_CACHE} from '@/utils/menu';
+import { useEffect, useState } from 'react';
+import type { BUTTON_PERMISSION, MENUS_CODE_TYPE } from '@/utils/menu/router';
+import { BUTTON_PERMISSION_ENUM } from '@/utils/menu/router';
+import { MENUS_BUTTONS_CACHE } from '@/utils/menu';
 
 export type permissionKeyType = keyof typeof BUTTON_PERMISSION_ENUM;
 export type permissionType = Record<permissionKeyType, boolean>;

+ 8 - 8
src/pages/device/Category/Save/index.tsx

@@ -13,16 +13,16 @@ import {
   Upload,
 } from '@formily/antd';
 import React from 'react';
-import {createForm} from '@formily/core';
-import {createSchemaField} from '@formily/react';
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
 import FUpload from '@/components/Upload';
 import * as ICONS from '@ant-design/icons';
-import {message, Modal} from 'antd';
-import {useIntl} from '@@/plugin-locale/localeExports';
-import type {ISchema} from '@formily/json-schema';
-import type {CategoryItem} from '@/pages/visualization/Category/typings';
-import {service, state} from '@/pages/device/Category';
-import type {Response} from '@/utils/typings';
+import { message, Modal } from 'antd';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import type { ISchema } from '@formily/json-schema';
+import type { CategoryItem } from '@/pages/visualization/Category/typings';
+import { service, state } from '@/pages/device/Category';
+import type { Response } from '@/utils/typings';
 
 interface Props {
   visible: boolean;

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

@@ -1,11 +1,16 @@
-import {Descriptions, message, Space, Tooltip} from 'antd';
-import {InstanceModel, service} from '@/pages/device/Instance';
-import {useEffect, useState} from 'react';
-import type {ConfigMetadata} from '@/pages/device/Product/typings';
-import {history, useParams} from 'umi';
-import {CheckOutlined, EditOutlined, QuestionCircleOutlined, UndoOutlined,} from '@ant-design/icons';
+import { Descriptions, message, Space, Tooltip } from 'antd';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import { useEffect, useState } from 'react';
+import type { ConfigMetadata } from '@/pages/device/Product/typings';
+import { history, useParams } from 'umi';
+import {
+  CheckOutlined,
+  EditOutlined,
+  QuestionCircleOutlined,
+  UndoOutlined,
+} from '@ant-design/icons';
 import Edit from './Edit';
-import {PermissionButton} from '@/components';
+import { PermissionButton } from '@/components';
 
 const Config = () => {
   const params = useParams<{ id: string }>();

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

@@ -1,13 +1,13 @@
 import TitleComponent from '@/components/TitleComponent';
-import {Badge, Button, Col, message, Popconfirm, Row} from 'antd';
-import {useEffect, useState} from 'react';
+import { Badge, Button, Col, message, Popconfirm, Row } from 'antd';
+import { useEffect, useState } from 'react';
 import styles from './index.less';
-import {InstanceModel, service} from '@/pages/device/Instance';
-import {getMenuPathByParams, MENUS_CODE} from '@/utils/menu';
-import type {ProductItem} from '@/pages/device/Product/typings';
-import {Store} from 'jetlinks-store';
-import {observer} from '@formily/reactive-react';
-import {DiagnoseStatusModel} from './model';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import type { ProductItem } from '@/pages/device/Product/typings';
+import { Store } from 'jetlinks-store';
+import { observer } from '@formily/reactive-react';
+import { DiagnoseStatusModel } from './model';
 
 interface Props {
   onChange: (type: string) => void;

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

@@ -1,11 +1,11 @@
-import {Badge, Card, Col, Row} from 'antd';
-import type {ReactNode} from 'react';
-import {useEffect, useState} from 'react';
+import { Badge, Card, Col, Row } from 'antd';
+import type { ReactNode } from 'react';
+import { useEffect, useState } from 'react';
 import Message from './Message';
 import Status from './Status';
 import './index.less';
 import classNames from 'classnames';
-import {Store} from 'jetlinks-store';
+import { Store } from 'jetlinks-store';
 
 interface ListProps {
   key: string;

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

@@ -1,15 +1,15 @@
-import {Card, Descriptions} from 'antd';
-import {InstanceModel} from '@/pages/device/Instance';
+import { Card, Descriptions } from 'antd';
+import { InstanceModel } from '@/pages/device/Instance';
 import moment from 'moment';
-import {observer} from '@formily/react';
-import {useIntl} from '@@/plugin-locale/localeExports';
+import { observer } from '@formily/react';
+import { useIntl } from '@@/plugin-locale/localeExports';
 import Config from '@/pages/device/Instance/Detail/Config';
 import Save from '../../Save';
-import {useState} from 'react';
-import type {DeviceInstance} from '../../typings';
-import {EditOutlined} from '@ant-design/icons';
+import { useState } from 'react';
+import type { DeviceInstance } from '../../typings';
+import { EditOutlined } from '@ant-design/icons';
 import Tags from '@/pages/device/Instance/Detail/Tags';
-import {PermissionButton} from '@/components';
+import { PermissionButton } from '@/components';
 
 const Info = observer(() => {
   const intl = useIntl();

+ 3 - 3
src/pages/device/Instance/Detail/MetadataMap/EditableTable/index.tsx

@@ -1,6 +1,6 @@
-import React, {useContext, useEffect, useState} from 'react';
-import {Form, Input, message, Pagination, Select, Table} from 'antd';
-import {service} from '@/pages/device/Instance';
+import React, { useContext, useEffect, useState } from 'react';
+import { Form, Input, message, Pagination, Select, Table } from 'antd';
+import { service } from '@/pages/device/Instance';
 import _ from 'lodash';
 
 const EditableContext: any = React.createContext(null);

+ 12 - 12
src/pages/device/Instance/Detail/index.tsx

@@ -1,10 +1,10 @@
-import {PageContainer} from '@ant-design/pro-layout';
-import {InstanceModel, service} from '@/pages/device/Instance';
-import {history, useParams} from 'umi';
-import {Badge, Card, Descriptions, Divider, message} from 'antd';
-import type {ReactNode} from 'react';
-import {useEffect, useState} from 'react';
-import {observer} from '@formily/react';
+import { PageContainer } from '@ant-design/pro-layout';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import { history, useParams } from 'umi';
+import { Badge, Card, Descriptions, Divider, message } from 'antd';
+import type { ReactNode } from 'react';
+import { useEffect, useState } from 'react';
+import { observer } from '@formily/react';
 import Log from '@/pages/device/Instance/Detail/Log';
 // import Alarm from '@/pages/device/components/Alarm';
 import Info from '@/pages/device/Instance/Detail/Info';
@@ -13,15 +13,15 @@ import Running from '@/pages/device/Instance/Detail/Running';
 import ChildDevice from '@/pages/device/Instance/Detail/ChildDevice';
 import Diagnose from '@/pages/device/Instance/Detail/Diagnose';
 import MetadataMap from '@/pages/device/Instance/Detail/MetadataMap';
-import {useIntl} from '@@/plugin-locale/localeExports';
+import { useIntl } from '@@/plugin-locale/localeExports';
 import Metadata from '../../components/Metadata';
-import type {DeviceMetadata} from '@/pages/device/Product/typings';
+import type { DeviceMetadata } from '@/pages/device/Product/typings';
 import MetadataAction from '@/pages/device/components/Metadata/DataBaseAction';
-import {Store} from 'jetlinks-store';
+import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
-import {getMenuPathByCode, getMenuPathByParams, MENUS_CODE} from '@/utils/menu';
+import { getMenuPathByCode, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
-import {PermissionButton} from '@/components';
+import { PermissionButton } from '@/components';
 
 export const deviceStatus = new Map();
 deviceStatus.set('online', <Badge status="success" text={'在线'} />);

+ 7 - 7
src/pages/device/Instance/Save/index.tsx

@@ -1,10 +1,10 @@
-import {Col, Form, Input, message, Modal, Row, Select} from 'antd';
-import {service} from '@/pages/device/Instance';
-import type {DeviceInstance} from '../typings';
-import {useEffect, useState} from 'react';
-import {useIntl} from '@@/plugin-locale/localeExports';
-import {UploadImage} from '@/components';
-import {debounce} from 'lodash';
+import { Col, Form, Input, message, Modal, Row, Select } from 'antd';
+import { service } from '@/pages/device/Instance';
+import type { DeviceInstance } from '../typings';
+import { useEffect, useState } from 'react';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { UploadImage } from '@/components';
+import { debounce } from 'lodash';
 
 interface Props {
   visible: boolean;

+ 24 - 15
src/pages/device/Instance/index.tsx

@@ -1,10 +1,10 @@
-import {PageContainer} from '@ant-design/pro-layout';
-import type {ActionType, ProColumns} from '@jetlinks/pro-table';
-import type {DeviceInstance} from '@/pages/device/Instance/typings';
+import { PageContainer } from '@ant-design/pro-layout';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import type { DeviceInstance } from '@/pages/device/Instance/typings';
 import moment from 'moment';
-import {Badge, Button, Dropdown, Menu, message, Tooltip} from 'antd';
-import {useEffect, useRef, useState} from 'react';
-import {useHistory, useIntl} from 'umi';
+import { Badge, Button, Dropdown, Menu, message, Tooltip } from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import { useHistory, useIntl } from 'umi';
 import {
   CheckCircleOutlined,
   DeleteOutlined,
@@ -16,20 +16,20 @@ import {
   StopOutlined,
   SyncOutlined,
 } from '@ant-design/icons';
-import {model} from '@formily/reactive';
+import { model } from '@formily/reactive';
 import Service from '@/pages/device/Instance/service';
-import type {MetadataItem} from '@/pages/device/Product/typings';
+import type { MetadataItem } from '@/pages/device/Product/typings';
 import Save from './Save';
 import Export from './Export';
 import Import from './Import';
 import Process from './Process';
 import SearchComponent from '@/components/SearchComponent';
-import {PermissionButton, ProTableCard} from '@/components';
+import { PermissionButton, ProTableCard } from '@/components';
 import SystemConst from '@/utils/const';
 import Token from '@/utils/token';
 import DeviceCard from '@/components/ProTableCard/CardItems/device';
-import {getMenuPathByParams, MENUS_CODE} from '@/utils/menu';
-import {useLocation} from '@/hooks';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { useLocation } from '@/hooks';
 
 export const statusMap = new Map();
 statusMap.set('在线', 'success');
@@ -67,6 +67,8 @@ const Instance = () => {
   const [bindKeys, setBindKeys] = useState<any[]>([]);
   const history = useHistory<Record<string, string>>();
   const { permission } = PermissionButton.usePermission('device/Instance');
+  const [jumpParams, setJumpParams] = useState<SearchTermsServer | undefined>(undefined);
+
   const intl = useIntl();
   const location = useLocation();
 
@@ -79,9 +81,12 @@ const Instance = () => {
           value: location.state[key],
         });
       });
-      setSearchParams({
-        terms: _terms,
-      });
+      setJumpParams([
+        {
+          terms: _terms,
+          type: 'or',
+        },
+      ]);
     }
   }, [location]);
 
@@ -194,7 +199,7 @@ const Instance = () => {
         id: 'pages.table.productName',
         defaultMessage: '产品名称',
       }),
-      dataIndex: 'productName',
+      dataIndex: 'productId',
       width: 200,
       ellipsis: true,
       valueType: 'select',
@@ -205,6 +210,7 @@ const Instance = () => {
         }
         return [];
       },
+      render: (_, row) => row.productName,
       filterMultiple: true,
     },
     {
@@ -274,6 +280,8 @@ const Instance = () => {
     },
   ];
 
+  console.log(jumpParams);
+
   const menu = (
     <Menu>
       <Menu.Item key="1">
@@ -391,6 +399,7 @@ const Instance = () => {
       <SearchComponent<DeviceInstance>
         field={columns}
         target="device-instance"
+        initParam={jumpParams}
         onSearch={(data) => {
           actionRef.current?.reset?.();
           setSearchParams(data);

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

@@ -26,7 +26,7 @@ const Access = () => {
   const [access, setAccess] = useState<any>();
   const [providers, setProviders] = useState<any[]>([]);
   const [networkList, setNetworkList] = useState<any[]>([]);
-  const { permission } = usePermissions('device/Product');
+  const { permission } = usePermissions('link/AccessConfig');
 
   const MetworkTypeMapping = new Map();
   MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');

+ 1 - 3
src/pages/device/Product/Detail/index.tsx

@@ -187,9 +187,7 @@ const ProductDetail = observer(() => {
       onBack={() => history.goBack()}
       extraContent={<Space size={24} />}
       onTabChange={(key) => {
-        if (permission.update) {
-          setMode(key);
-        }
+        setMode(key);
       }}
       tabList={list}
       tabActiveKey={mode}

+ 28 - 35
src/pages/device/Product/index.tsx

@@ -151,19 +151,18 @@ const Product = observer(() => {
           defaultMessage: '下载',
         }),
       }}
+      onClick={async () => {
+        downloadObject(
+          record,
+          intl.formatMessage({
+            id: 'pages.device.product',
+            defaultMessage: '产品',
+          }),
+        );
+        message.success('操作成功');
+      }}
     >
-      <DownloadOutlined
-        onClick={async () => {
-          downloadObject(
-            record,
-            intl.formatMessage({
-              id: 'pages.device.product',
-              defaultMessage: '产品',
-            }),
-          );
-          message.success('操作成功');
-        }}
-      />
+      <DownloadOutlined />
     </PermissionButton>,
     <PermissionButton
       popConfirm={{
@@ -199,12 +198,9 @@ const Product = observer(() => {
           defaultMessage: '是否删除?',
         }),
         onConfirm: async () => {
-          if (record.state === 0) {
-            await deleteItem(record.id);
-          } else {
-            message.error('已发布的产品不能进行删除操作');
-          }
+          await deleteItem(record.id);
         },
+        disabled: record.state === 1,
       }}
       tooltip={{
         title: intl.formatMessage({
@@ -380,13 +376,14 @@ const Product = observer(() => {
                   data.state = 0;
                   if (Array.isArray(data)) {
                     message.error('请上传json格式文件');
-                    return;
+                    return false;
                   }
                   const res = await service.update(data);
                   if (res.status === 200) {
                     message.success('操作成功');
                     actionRef.current?.reload();
                   }
+                  return true;
                 } catch {
                   message.error('请上传json格式文件');
                 }
@@ -438,19 +435,18 @@ const Product = observer(() => {
                 type={'link'}
                 key={'download'}
                 style={{ padding: 0 }}
+                onClick={async () => {
+                  downloadObject(
+                    record,
+                    intl.formatMessage({
+                      id: 'pages.device.product',
+                      defaultMessage: '产品',
+                    }),
+                  );
+                  message.success('操作成功');
+                }}
               >
-                <DownloadOutlined
-                  onClick={async () => {
-                    downloadObject(
-                      record,
-                      intl.formatMessage({
-                        id: 'pages.device.product',
-                        defaultMessage: '产品',
-                      }),
-                    );
-                    message.success('操作成功');
-                  }}
-                />
+                <DownloadOutlined />
                 {intl.formatMessage({
                   id: 'pages.data.option.download',
                   defaultMessage: '下载',
@@ -499,12 +495,9 @@ const Product = observer(() => {
                     defaultMessage: '是否删除?',
                   }),
                   onConfirm: async () => {
-                    if (record.state === 0) {
-                      await deleteItem(record.id);
-                    } else {
-                      message.error('已发布的产品不能进行删除操作');
-                    }
+                    await deleteItem(record.id);
                   },
+                  disabled: record.state === 1,
                 }}
               >
                 <DeleteOutlined />

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

@@ -1,24 +1,24 @@
-import type {ProColumns} from '@jetlinks/pro-table';
+import type { ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
-import {useCallback, useEffect, useState} from 'react';
-import {useParams} from 'umi';
+import { useCallback, useEffect, useState } from 'react';
+import { useParams } from 'umi';
 import DB from '@/db';
-import type {MetadataItem, MetadataType} from '@/pages/device/Product/typings';
+import type { MetadataItem, MetadataType } from '@/pages/device/Product/typings';
 import MetadataMapping from './columns';
-import {message} from 'antd';
-import {DeleteOutlined, EditOutlined, ImportOutlined, PlusOutlined} from '@ant-design/icons';
+import { message } from 'antd';
+import { DeleteOutlined, EditOutlined, ImportOutlined, PlusOutlined } from '@ant-design/icons';
 import Edit from './Edit';
-import {observer} from '@formily/react';
+import { observer } from '@formily/react';
 import MetadataModel from './model';
-import {Store} from 'jetlinks-store';
+import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
-import {useIntl} from '@@/plugin-locale/localeExports';
+import { useIntl } from '@@/plugin-locale/localeExports';
 import PropertyImport from '@/pages/device/Product/Detail/PropertyImport';
-import {productModel} from '@/pages/device/Product';
-import {InstanceModel} from '@/pages/device/Instance';
-import {asyncUpdateMedata, removeMetadata} from '../metadata';
-import type {permissionType} from '@/hooks/permission';
-import {PermissionButton} from '@/components';
+import { productModel } from '@/pages/device/Product';
+import { InstanceModel } from '@/pages/device/Instance';
+import { asyncUpdateMedata, removeMetadata } from '../metadata';
+import type { permissionType } from '@/hooks/permission';
+import { PermissionButton } from '@/components';
 
 interface Props {
   type: MetadataType;

+ 8 - 8
src/pages/device/components/Metadata/index.tsx

@@ -1,16 +1,16 @@
-import {observer} from '@formily/react';
-import {Space, Tabs} from 'antd';
+import { observer } from '@formily/react';
+import { Space, Tabs } from 'antd';
 import BaseMetadata from './Base';
-import {useIntl} from '@@/plugin-locale/localeExports';
+import { useIntl } from '@@/plugin-locale/localeExports';
 import Import from './Import';
-import type {ReactNode} from 'react';
-import {useState} from 'react';
+import type { ReactNode } from 'react';
+import { useState } from 'react';
 import Cat from './Cat';
 import Service from '@/pages/device/components/Metadata/service';
-import {InfoCircleOutlined} from '@ant-design/icons';
+import { InfoCircleOutlined } from '@ant-design/icons';
 import styles from './index.less';
-import {InstanceModel} from '@/pages/device/Instance';
-import {PermissionButton} from '@/components';
+import { InstanceModel } from '@/pages/device/Instance';
+import { PermissionButton } from '@/components';
 
 interface Props {
   tabAction?: ReactNode;

+ 7 - 1
src/pages/media/Device/Playback/index.tsx

@@ -1,7 +1,7 @@
 // 回放
 import { PageContainer } from '@ant-design/pro-layout';
 import LivePlayer from '@/components/Player';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
 import { Calendar, Empty, List, Select, Tooltip } from 'antd';
 import { useLocation } from 'umi';
 import Service from './service';
@@ -31,6 +31,7 @@ export default () => {
   const [cloudTime, setCloudTime] = useState<any>();
   const [playing, setPlaying] = useState(false);
   const location = useLocation();
+  const player = useRef<any>();
 
   const param = new URLSearchParams(location.search);
   const deviceId = param.get('id');
@@ -186,6 +187,7 @@ export default () => {
             url={url}
             className={'playback-media'}
             live={type === 'local'}
+            ref={player}
             onPlay={() => {
               setPlaying(true);
             }}
@@ -297,6 +299,10 @@ export default () => {
                                   );
                                 }
                               } else {
+                                console.log(player.current);
+                                if (player.current.pause) {
+                                  player.current.pause();
+                                }
                                 setPlayTime(0);
                               }
                             }}

+ 8 - 14
src/pages/notice/Config/Detail/doc/AliyunSms.tsx

@@ -1,37 +1,31 @@
 import { Image } from 'antd';
+import './index.less';
 
 const AliyunSms = () => {
   const accessKey = require('/public/images/notice/doc/config/aliyun-sms-voice/AccesskeyIDSecret.jpg');
   return (
-    <div>
-      <div
-        style={{
-          backgroundColor: '#e9eaeb',
-          height: '30px',
-          display: 'flex',
-          alignItems: 'center',
-        }}
-      >
+    <div className={'doc'}>
+      <div className={'url'}>
         阿里云管理控制台:
         <a href="https://home.console.aliyun.com">https://home.console.aliyun.com</a>
       </div>
-      <b>1. 概述</b>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div>1. RegionID</div>
+        <h2>1. RegionID</h2>
         <div>阿里云内部给每台机器设置的唯一编号。请根据购买的阿里云服务器地址进行填写。</div>
         <div>
           阿里云地域和可用区对照表地址:https://help.aliyun.com/document_detail/40654.html?spm=a2c6h.13066369.0.0.54a174710O7rWH
         </div>
       </div>
-      <b>2、AccesskeyID/Secret</b>
+      <h2>2. AccesskeyID/Secret</h2>
       <div>
         <div>用于程序通知方式调用云服务费API的用户标识和秘钥</div>
         <div>获取路径:“阿里云管理控制台”--“用户头像”--“”--“AccessKey管理”--“查看”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={accessKey} />
         </div>
       </div>

+ 8 - 14
src/pages/notice/Config/Detail/doc/AliyunVoice.tsx

@@ -1,38 +1,32 @@
 import { Image } from 'antd';
+import './index.less';
 
 const AliyunVoice = () => {
   const accessKey = require('/public/images/notice/doc/config/aliyun-sms-voice/AccesskeyIDSecret.jpg');
 
   return (
-    <div>
-      <div
-        style={{
-          backgroundColor: '#e9eaeb',
-          height: '30px',
-          display: 'flex',
-          alignItems: 'center',
-        }}
-      >
+    <div className={'doc'}>
+      <div className={'url'}>
         阿里云管理控制台:
         <a href="https://home.console.aliyun.com">https://home.console.aliyun.com</a>
       </div>
-      <b>1. 概述</b>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div>1. RegionID</div>
+        <h2>1. RegionID</h2>
         <div>
           阿里云内部给每台机器设置的唯一编号。请根据购买的阿里云服务器阿里云地域和可用区对照表地址:https://help.aliyun.com/document_detail/40654.html?spm=a2c6h.13066369.0.0.54a174710O7rWH获取路径:“微信公众平台”管理后台--“设置与开发”--“基本配置”
         </div>
       </div>
-      <b>2、AccesskeyID/Secret</b>
+      <h2>2、AccesskeyID/Secret</h2>
       <div>
         <div>用于程序通知方式调用云服务费API的用户标识和秘钥公众号开发者身份的密码</div>
         <div>获取路径:“阿里云管理控制台”--“用户头像”--“”--“AccessKey管理”--“查看”</div>
       </div>
-      <div>
+      <div className={'image'}>
         <Image width="100%" src={accessKey} />
       </div>
     </div>

+ 9 - 15
src/pages/notice/Config/Detail/doc/DingTalk.tsx

@@ -1,40 +1,34 @@
 import { Image } from 'antd';
+import './index.less';
 
 const DingTalk = () => {
   const appKey = require('/public/images/notice/doc/config/dingTalk-message/01-AppKey.jpg');
   const appSecret = require('/public/images/notice/doc/config/dingTalk-message/02-AppSecret.jpg');
   return (
-    <div>
-      <div
-        style={{
-          backgroundColor: '#e9eaeb',
-          height: '30px',
-          display: 'flex',
-          alignItems: 'center',
-        }}
-      >
+    <div className={'doc'}>
+      <div className={'url'}>
         钉钉管理后台:<a href="https://open-dev.dingtalk.com">https://open-dev.dingtalk.com</a>
       </div>
-      <b>1. 概述</b>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div>1. AppKey</div>
+        <h2>1. AppKey</h2>
         <div>
           企业内部应用的唯一身份标识。在钉钉开发者后台创建企业内部应用后,系统会自动生成一对AppKey和AppSecret。
         </div>
         <div>获取路径:“钉钉开放平台”--“应用开发”--“应用信息”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={appKey} />
         </div>
       </div>
-      <b>2. AppSecret</b>
+      <h2>2. AppSecret</h2>
       <div>
         <div>钉钉应用对应的调用密钥</div>
         <div>获取路径:“钉钉开放平台”--“应用开发”--“应用信息”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={appSecret} />
         </div>
       </div>

+ 10 - 9
src/pages/notice/Config/Detail/doc/DingTalkRebot.tsx

@@ -1,4 +1,5 @@
 import { Image } from 'antd';
+import './index.less';
 
 const DingTalkRebot = () => {
   const groupSetting = require('/public/images/notice/doc/config/dingTalk-rebot/01-group-setting.jpg');
@@ -7,25 +8,25 @@ const DingTalkRebot = () => {
 
   const webhook = require('/public/images/notice/doc/config/dingTalk-rebot/03-Webhook.jpg');
   return (
-    <div>
-      <b>1. 概述</b>
+    <div className={'doc'}>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div> 1. WebHook</div>
+        <h2> 1. WebHook</h2>
         <div>在钉钉群内每创建一个钉钉群自定义机器人都会产生唯一的WebHook地址。</div>
         <div>获取路径:“钉钉桌面客户端”--“群设置”--“智能群助手”--“机器人信息”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={rebot} />
         </div>
-        <div>1、登录钉钉桌面客户端,进入群设置</div>
-        <div>
+        <h2>1、登录钉钉桌面客户端,进入群设置</h2>
+        <div className={'image'}>
           <Image width="100%" src={groupSetting} />
         </div>
-        <div>2、点击智能群助手,查看机器人信息</div>
-        <div>
+        <h2>2、点击智能群助手,查看机器人信息</h2>
+        <div className={'image'}>
           <Image width="100%" src={webhook} />
         </div>
       </div>

+ 9 - 7
src/pages/notice/Config/Detail/doc/Email.tsx

@@ -1,21 +1,23 @@
+import './index.less';
+
 const Email = () => {
   return (
-    <div>
-      <b>1. 概述</b>
+    <div className={'doc'}>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
-      <div>1. 服务器地址</div>
+      <h1>2.通知配置说明</h1>
+      <h2>1. 服务器地址</h2>
       <div>下拉可选择国内常用的邮箱服务配置,也支持手动输入其他地址。</div>
       <div>
         系统POP协议。POP允许电子邮件客户端下载服务器上的邮件,但是您在电子邮件客户端的操作(如:移动邮件、标记已读等),这是不会反馈到服务器上。
       </div>
-      <div>2、发件人</div>
+      <h2>2. 发件人</h2>
       <div>用于发送邮件时“发件人“信息的显示</div>
-      <div>3、用户名</div>
+      <h2>3. 用户名</h2>
       <div>用该账号进行发送邮件。</div>
-      <div>4、密码</div>
+      <h2>4. 密码</h2>
       <div>用与账号身份认证,认证通过后可通过该账号进行发送邮件。</div>
     </div>
   );

+ 9 - 15
src/pages/notice/Config/Detail/doc/WeixinApp.tsx

@@ -1,39 +1,33 @@
 import { Image } from 'antd';
+import './index.less';
 
 const WeixinApp = () => {
   const appId = require('/public/images/notice/doc/config/weixin-official/01-AppID.jpg');
   const appSecret = require('/public/images/notice/doc/config/weixin-official/02-AppSecret.jpg');
 
   return (
-    <div>
-      <div
-        style={{
-          backgroundColor: '#e9eaeb',
-          height: '30px',
-          display: 'flex',
-          alignItems: 'center',
-        }}
-      >
+    <div className={'doc'}>
+      <div className={'url'}>
         微信公众平台:<a href="https://mp.weixin.qq.com/">https://mp.weixin.qq.com/</a>
       </div>
-      <b>1. 概述</b>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div>1. AppID</div>
+        <h2>1. AppID</h2>
         <div>微信服务号的唯一专属编号。</div>
         <div>获取路径:“微信公众平台”管理后台--“设置与开发”--“基本配置”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={appId} />
         </div>
       </div>
-      <b>2. AppSecret</b>
+      <h2>2. AppSecret</h2>
       <div>
         <div>公众号开发者身份的密码</div>
         <div>获取路径:“微信公众平台”管理后台--“设置与开发”--“基本配置”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={appSecret} />
         </div>
       </div>

+ 9 - 15
src/pages/notice/Config/Detail/doc/WeixinCorp.tsx

@@ -1,39 +1,33 @@
 import { Image } from 'antd';
+import './index.less';
 
 const WeixinCorp = () => {
   const corpId = require('/public/images/notice/doc/config/weixin-corp/01-corpId.jpg');
   const corpSecret = require('/public/images/notice/doc/config/weixin-corp/02-corpSecret.jpg');
   return (
-    <div>
-      <div
-        style={{
-          backgroundColor: '#e9eaeb',
-          height: '30px',
-          display: 'flex',
-          alignItems: 'center',
-        }}
-      >
+    <div className={'doc'}>
+      <div className={'url'}>
         企业微信管理后台:<a href="https://work.weixin.qq.com">https://work.weixin.qq.com</a>
       </div>
-      <b>1. 概述</b>
+      <h1>1. 概述</h1>
       <div>
         通知配置可以结合通知配置为告警消息通知提供支撑。也可以用于系统中其他自定义模块的调用。
       </div>
-      <b>2.通知配置说明</b>
+      <h1>2.通知配置说明</h1>
       <div>
-        <div>1. corpId</div>
+        <h2>1. corpId</h2>
         <div>企业号的唯一专属编号。</div>
         <div>获取路径:“企业微信”管理后台--“我的企业”--“企业ID”</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={corpId} />
         </div>
       </div>
 
-      <b>2. corpSecret</b>
+      <h2>2. corpSecret</h2>
       <div>
         <div>应用的唯一secret,一个企业微信中可以有多个corpSecret</div>
         <div>获取路径:“企业微信”--“应用与小程序”--“自建应用”中获取</div>
-        <div>
+        <div className={'image'}>
           <Image width="100%" src={corpSecret} />
         </div>
       </div>

+ 33 - 0
src/pages/notice/Config/Detail/doc/index.less

@@ -0,0 +1,33 @@
+.doc {
+  padding: 24px;
+  color: rgba(#000, 0.8);
+  font-size: 14px;
+  background-color: #fafafa;
+
+  .url {
+    padding: 8px 16px;
+    color: #2f54eb;
+    background-color: rgba(#a7bdf7, 0.2);
+  }
+
+  h1 {
+    margin: 16px 0;
+    color: rgba(#000, 0.85);
+    font-weight: bold;
+    font-size: 14px;
+
+    &:first-child {
+      margin-top: 0;
+    }
+  }
+
+  h2 {
+    margin: 6px 0;
+    color: rgba(0, 0, 0, 0.8);
+    font-size: 14px;
+  }
+
+  .image {
+    margin: 16px 0;
+  }
+}

+ 9 - 9
src/pages/system/Menu/Detail/buttons.tsx

@@ -1,14 +1,14 @@
-import {Button, Form, Input, message, Modal, Tooltip} from 'antd';
-import {useIntl} from '@@/plugin-locale/localeExports';
-import {useCallback, useEffect, useState} from 'react';
-import {service} from '@/pages/system/Menu';
-import type {ProColumns} from '@jetlinks/pro-table';
+import { Button, Form, Input, message, Modal, Tooltip } from 'antd';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { useCallback, useEffect, useState } from 'react';
+import { service } from '@/pages/system/Menu';
+import type { ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
-import {DeleteOutlined, EditOutlined, PlusOutlined, SearchOutlined} from '@ant-design/icons';
-import type {MenuButtonInfo, MenuItem} from '@/pages/system/Menu/typing';
+import { DeleteOutlined, EditOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons';
+import type { MenuButtonInfo, MenuItem } from '@/pages/system/Menu/typing';
 import Permission from '@/pages/system/Menu/components/permission';
-import {useRequest} from '@@/plugin-request/request';
-import {PermissionButton} from '@/components';
+import { useRequest } from '@@/plugin-request/request';
+import { PermissionButton } from '@/components';
 
 type ButtonsProps = {
   data: MenuItem;

+ 21 - 9
src/pages/system/Menu/Detail/edit.tsx

@@ -1,16 +1,28 @@
-import {Card, Col, Form, Input, InputNumber, message, Radio, Row, Select, Tooltip, TreeSelect,} from 'antd';
+import {
+  Card,
+  Col,
+  Form,
+  Input,
+  InputNumber,
+  message,
+  Radio,
+  Row,
+  Select,
+  Tooltip,
+  TreeSelect,
+} from 'antd';
 import Permission from '@/pages/system/Menu/components/permission';
-import {useIntl} from '@@/plugin-locale/localeExports';
-import {useEffect, useState} from 'react';
-import {service} from '@/pages/system/Menu';
-import {useHistory, useRequest} from 'umi';
-import type {MenuItem} from '@/pages/system/Menu/typing';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { useEffect, useState } from 'react';
+import { service } from '@/pages/system/Menu';
+import { useHistory, useRequest } from 'umi';
+import type { MenuItem } from '@/pages/system/Menu/typing';
 // import { debounce } from 'lodash';
 import Title from '../components/Title';
 import Icons from '../components/Icons';
-import {QuestionCircleFilled} from '@ant-design/icons';
-import {getMenuPathByParams, MENUS_CODE} from '@/utils/menu';
-import {PermissionButton} from '@/components';
+import { QuestionCircleFilled } from '@ant-design/icons';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { PermissionButton } from '@/components';
 
 type EditProps = {
   data: MenuItem;