Procházet zdrojové kódy

fix(权限): 修改设备、产品中物模型权限按钮

xieyonghong před 3 roky
rodič
revize
9cbd953ea7

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

@@ -178,7 +178,7 @@ const ArrayParam = () => {
           },
 
           description: {
-            title: '描述',
+            title: '说明',
             'x-decorator': 'FormItem',
             'x-component': 'Input.TextArea',
           },

+ 9 - 2
src/components/PermissionButton/index.tsx

@@ -8,6 +8,7 @@ interface PermissionButtonProps extends ButtonProps {
   tooltip?: TooltipProps;
   popConfirm?: PopconfirmProps;
   isPermission?: boolean;
+  noButton?: boolean;
 }
 
 /**
@@ -27,7 +28,13 @@ const PermissionButton = (props: PermissionButtonProps) => {
 
   const intl = useIntl();
 
-  const defaultButton = <Button {...buttonProps} disabled={_isPermission} />;
+  const isButton = 'noButton' in props && props.noButton;
+
+  const defaultButton = isButton ? (
+    props.children
+  ) : (
+    <Button {...buttonProps} disabled={_isPermission} />
+  );
 
   const isTooltip = tooltip ? <Tooltip {...tooltip}>{defaultButton}</Tooltip> : null;
 
@@ -38,7 +45,7 @@ const PermissionButton = (props: PermissionButtonProps) => {
         defaultMessage: '没有权限',
       })}
     >
-      {defaultButton}
+      {<Button {...buttonProps} disabled={_isPermission} />}
     </Tooltip>
   );
 

+ 2 - 0
src/hooks/index.ts

@@ -0,0 +1,2 @@
+export { default as useHistory } from './route/useHistory';
+export { default as useLocation } from './route/useLocation';

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

@@ -3,8 +3,8 @@ import { BUTTON_PERMISSION_ENUM } from '@/utils/menu/router';
 import type { MENUS_CODE_TYPE, BUTTON_PERMISSION } from '@/utils/menu/router';
 import { MENUS_BUTTONS_CACHE } from '@/utils/menu';
 
-type permissionKeyType = keyof typeof BUTTON_PERMISSION_ENUM;
-type permissionType = Record<permissionKeyType, boolean>;
+export type permissionKeyType = keyof typeof BUTTON_PERMISSION_ENUM;
+export type permissionType = Record<permissionKeyType, boolean>;
 
 const usePermissions = (
   code: MENUS_CODE_TYPE,

+ 30 - 0
src/hooks/route/useHistory.tsx

@@ -0,0 +1,30 @@
+import { useHistory } from 'umi';
+import { useEffect, useState } from 'react';
+import type { LocationDescriptor, LocationState, Path } from 'history';
+import { model } from '@formily/reactive';
+
+export const historyStateModel = model<{ state: any }>({ state: {} });
+
+const useHistories = () => {
+  const umiHistory = useHistory();
+
+  const [history, setHistory] = useState<any>();
+
+  const push = (location: Path | LocationDescriptor<LocationState>, state?: LocationState) => {
+    if (state) {
+      historyStateModel.state = state;
+    }
+    umiHistory.push(location, state);
+  };
+
+  useEffect(() => {
+    setHistory({
+      ...umiHistory,
+      push,
+    });
+  }, []);
+
+  return history;
+};
+
+export default useHistories;

+ 22 - 0
src/hooks/route/useLocation.tsx

@@ -0,0 +1,22 @@
+import { useLocation } from 'umi';
+import { useEffect, useState } from 'react';
+import { historyStateModel } from '@/hooks/route/useHistory';
+
+const useLocations = () => {
+  const umiLocation = useLocation();
+  const [location, setLocation] = useState<any>({});
+  useEffect(() => {
+    setLocation({
+      ...umiLocation,
+      state: historyStateModel.state,
+    });
+
+    return () => {
+      historyStateModel.state = undefined;
+    };
+  }, [umiLocation]);
+
+  return location;
+};
+
+export default useLocations;

+ 3 - 2
src/locales/zh-CN/pages.ts

@@ -42,7 +42,7 @@ export default {
   'pages.table.name': '名称',
   'pages.table.deviceName': '设备名称',
   'pages.table.productName': '产品名称',
-  'pages.table.describe': '描述',
+  'pages.table.describe': '说明',
   'pages.table.description': '说明',
   'pages.table.provider': '服务商',
   'pages.table.type': '类型',
@@ -149,6 +149,7 @@ export default {
   'pages.system.menu.buttons': '按钮管理',
   'pages.system.menu.root': '菜单权限',
   'page.system.menu.sort': '排序',
+  'page.system.menu.describe': '说明',
   // 系统设置-第三方平台
   'pages.system.openApi': '第三方平台',
   'pages.system.openApi.username': '用户名',
@@ -223,7 +224,7 @@ export default {
   'pages.device.productDetail.metadata.fileType': '文件类型',
   'pages.device.productDetail.metadata.source': '来源',
   'pages.device.productDetail.metadata.otherConfiguration': '其他配置',
-  'pages.device.productDetail.metadata.describe': '描述',
+  'pages.device.productDetail.metadata.describe': '说明',
   'pages.device.productDetail.metadata.inputParameter': '输入参数',
   'pages.device.productDetail.metadata.outputParameters': '输出参数',
   'pages.device.productDetail.alarm': '告警设置',

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

@@ -135,7 +135,7 @@ const Save = (props: Props) => {
         'x-component': 'Input.TextArea',
         'x-component-props': {
           rows: 3,
-          placeholder: '请输入描述',
+          placeholder: '请输入说明',
         },
         name: 'description',
       },

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

@@ -1,7 +1,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { history, useParams } from 'umi';
-import { Badge, Button, Card, Descriptions, Divider, message, Popconfirm, Tooltip } from 'antd';
+import { Badge, Button, Card, Descriptions, Divider, message } from 'antd';
 import type { ReactNode } from 'react';
 import { useEffect, useState } from 'react';
 import { observer } from '@formily/react';
@@ -21,6 +21,7 @@ import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
+import { PermissionButton } from '@/components';
 
 export const deviceStatus = new Map();
 deviceStatus.set('online', <Badge status="success" text={'在线'} />);
@@ -31,6 +32,7 @@ const InstanceDetail = observer(() => {
   const intl = useIntl();
   const [tab, setTab] = useState<string>('detail');
   const params = useParams<{ id: string }>();
+  const { permission } = PermissionButton.usePermission('device/Instance');
 
   const resetMetadata = async () => {
     const resp = await service.deleteMetadata(params.id);
@@ -70,11 +72,19 @@ const InstanceDetail = observer(() => {
           <Metadata
             type="device"
             tabAction={
-              <Popconfirm title="确认重置?" onConfirm={resetMetadata}>
-                <Tooltip title="重置后将使用产品的物模型配置">
-                  <Button>重置操作</Button>
-                </Tooltip>
-              </Popconfirm>
+              <PermissionButton
+                isPermission={permission.update}
+                popConfirm={{
+                  title: '确认重置?',
+                  onConfirm: resetMetadata,
+                }}
+                tooltip={{
+                  title: '重置后将使用产品的物模型配置',
+                }}
+                key={'reload'}
+              >
+                重置操作
+              </PermissionButton>
             }
           />
         </Card>

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

@@ -30,6 +30,7 @@ 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';
 
 export const statusMap = new Map();
 statusMap.set('在线', 'success');
@@ -54,7 +55,7 @@ export const InstanceModel = model<{
   params: new Set<string>(['test']),
 });
 export const service = new Service('device-instance');
-const Instance = (props: any) => {
+const Instance = () => {
   const actionRef = useRef<ActionType>();
   const [visible, setVisible] = useState<boolean>(false);
   const [exportVisible, setExportVisible] = useState<boolean>(false);
@@ -68,23 +69,23 @@ const Instance = (props: any) => {
   const history = useHistory<Record<string, string>>();
   const { permission } = PermissionButton.usePermission('device/Instance');
   const intl = useIntl();
+  const location = useLocation();
 
   useEffect(() => {
-    console.log(searchParams);
-    if (props.path) {
+    console.log(location);
+    if (location.state) {
       const _terms: any[] = [];
-      Object.keys(props.locationState).forEach((key) => {
+      Object.keys(location.state).forEach((key) => {
         _terms.push({
           column: key,
-          value: props.locationState[key],
+          value: location.state[key],
         });
       });
       setSearchParams({
         terms: _terms,
       });
-      props.cleanLocationState();
     }
-  }, [props.path]);
+  }, [location]);
 
   const tools = (record: DeviceInstance) => [
     <Button

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

@@ -1,10 +1,11 @@
 import { productModel, service } from '@/pages/device/Product';
-import { Button, Descriptions } from 'antd';
+import { Descriptions } from 'antd';
 import { useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { EditOutlined } from '@ant-design/icons';
 import { getDateFormat } from '@/utils/util';
 import Save from '@/pages/device/Product/Save';
+import { PermissionButton } from '@/components';
 
 // const componentMap = {
 //   string: 'Input',
@@ -16,6 +17,7 @@ const BaseInfo = () => {
   // const [metadata, setMetadata] = useState<ConfigMetadata[]>([]);
   // const [state, setState] = useState<boolean>(false);
   const [visible, setVisible] = useState(false);
+  const { permission } = PermissionButton.usePermission('device/Product');
 
   // const form = createForm({
   //   validateFirst: true,
@@ -152,7 +154,8 @@ const BaseInfo = () => {
         column={3}
         title={[
           <span key={1}>产品信息</span>,
-          <Button
+          <PermissionButton
+            isPermission={permission.update}
             key={2}
             type={'link'}
             onClick={() => {
@@ -160,7 +163,7 @@ const BaseInfo = () => {
             }}
           >
             <EditOutlined />
-          </Button>,
+          </PermissionButton>,
         ]}
         bordered
       >

+ 63 - 52
src/pages/device/Product/Detail/index.tsx

@@ -1,5 +1,5 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { history, useLocation, useParams } from 'umi';
+import { useIntl, useLocation, useParams } from 'umi';
 import {
   Badge,
   Button,
@@ -16,7 +16,7 @@ import BaseInfo from '@/pages/device/Product/Detail/BaseInfo';
 import { observer } from '@formily/react';
 import { productModel, service } from '@/pages/device/Product';
 import { useCallback, useEffect, useState } from 'react';
-import { useIntl, connect } from 'umi';
+import { useHistory } from '@/hooks';
 import Metadata from '@/pages/device/components/Metadata';
 import Access from '@/pages/device/Product/Detail/Access';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
@@ -26,6 +26,7 @@ import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import encodeQuery from '@/utils/encodeQuery';
 import MetadataMap from '@/pages/device/Instance/Detail/MetadataMap';
 import SystemConst from '@/utils/const';
+import { PermissionButton } from '@/components';
 
 export const ModelEnum = {
   base: 'base',
@@ -33,12 +34,15 @@ export const ModelEnum = {
   access: 'access',
 };
 
-const ProductDetail = observer((props: any) => {
+const ProductDetail = observer(() => {
   const intl = useIntl();
   const [mode, setMode] = useState('base');
   const param = useParams<{ id: string }>();
   const [loading, setLoading] = useState<boolean>(false);
   const location = useLocation();
+  const history = useHistory();
+
+  const { permission } = PermissionButton.usePermission('device/Product');
 
   const statusMap = {
     1: {
@@ -194,7 +198,9 @@ const ProductDetail = observer((props: any) => {
       onBack={() => history.goBack()}
       extraContent={<Space size={24} />}
       onTabChange={(key) => {
-        setMode(key);
+        if (permission.update) {
+          setMode(key);
+        }
       }}
       tabList={list}
       tabActiveKey={mode}
@@ -210,10 +216,10 @@ const ProductDetail = observer((props: any) => {
                   const params = {
                     productId: productModel.current?.id,
                   };
-                  props.push({
-                    locationState: params,
-                    path: url,
-                  });
+                  // props.push({
+                  //   locationState: params,
+                  //   path: url,
+                  // });
                   history.push(url, params);
                 }}
               >
@@ -225,40 +231,56 @@ const ProductDetail = observer((props: any) => {
       }
       title={productModel.current?.name}
       subTitle={
-        <Popconfirm
-          title={productModel.current?.state === 1 ? '确认取消发布' : '确认发布'}
-          onConfirm={() => {
-            changeDeploy(statusMap[productModel.current?.state || 0].action);
-          }}
-        >
-          <Switch
-            key={2}
-            checked={productModel.current?.state === 1}
-            checkedChildren="已发布"
-            unCheckedChildren="未发布"
-          />
-        </Popconfirm>
+        permission.update ? (
+          <Popconfirm
+            title={productModel.current?.state === 1 ? '确认取消发布' : '确认发布'}
+            onConfirm={() => {
+              changeDeploy(statusMap[productModel.current?.state || 0].action);
+            }}
+          >
+            <Switch
+              key={2}
+              checked={productModel.current?.state === 1}
+              checkedChildren="已发布"
+              unCheckedChildren="未发布"
+            />
+          </Popconfirm>
+        ) : (
+          <Tooltip
+            title={intl.formatMessage({
+              id: 'pages.data.option.noPermission',
+              defaultMessage: '没有权限',
+            })}
+          >
+            <Switch
+              key={2}
+              disabled
+              checked={productModel.current?.state === 1}
+              checkedChildren="已发布"
+              unCheckedChildren="未发布"
+            />
+          </Tooltip>
+        )
       }
       extra={[
-        <Popconfirm title={'确定应用配置?'} key="1" onConfirm={() => changeDeploy('deploy')}>
-          {productModel.current?.state === 0 ? (
-            <Tooltip title={'请先发布产品'}>
-              <Button disabled type="primary">
-                {intl.formatMessage({
-                  id: 'pages.device.productDetail.setting',
-                  defaultMessage: '应用配置',
-                })}
-              </Button>
-            </Tooltip>
-          ) : (
-            <Button key="1" type="primary">
-              {intl.formatMessage({
-                id: 'pages.device.productDetail.setting',
-                defaultMessage: '应用配置',
-              })}
-            </Button>
-          )}
-        </Popconfirm>,
+        <PermissionButton
+          key="1"
+          type={'primary'}
+          popConfirm={{
+            title: '确定应用配置?',
+            onConfirm: () => {
+              changeDeploy('deploy');
+            },
+          }}
+          tooltip={productModel.current?.state === 0 ? { title: '请先发布产品' } : undefined}
+          isPermission={permission.update}
+          disabled={productModel.current?.state === 0}
+        >
+          {intl.formatMessage({
+            id: 'pages.device.productDetail.setting',
+            defaultMessage: '应用配置',
+          })}
+        </PermissionButton>,
       ]}
     >
       <Card>
@@ -332,15 +354,4 @@ const ProductDetail = observer((props: any) => {
   );
 });
 
-const mapState = (state: any) => ({
-  state: state.state,
-  path: state.path,
-});
-
-const actionCreate = {
-  push: (payload: any) => {
-    return { type: 'location/push', payload };
-  },
-};
-
-export default connect(mapState, actionCreate)(ProductDetail);
+export default ProductDetail;

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

@@ -5,7 +5,7 @@ import { useParams } from 'umi';
 import DB from '@/db';
 import type { MetadataItem, MetadataType } from '@/pages/device/Product/typings';
 import MetadataMapping from './columns';
-import { Button, message, Popconfirm, Tooltip } from 'antd';
+import { message } from 'antd';
 import { DeleteOutlined, EditOutlined, ImportOutlined, PlusOutlined } from '@ant-design/icons';
 import Edit from './Edit';
 import { observer } from '@formily/react';
@@ -17,10 +17,13 @@ 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';
 
 interface Props {
   type: MetadataType;
   target: 'product' | 'device';
+  permission: Partial<permissionType>;
 }
 
 const BaseMetadata = observer((props: Props) => {
@@ -73,36 +76,41 @@ const BaseMetadata = observer((props: Props) => {
       align: 'center',
       width: 200,
       render: (_: unknown, record: MetadataItem) => [
-        <Tooltip key={'edit'} title={operateLimits('add', type) ? '暂不支持' : '编辑'}>
-          <Button
-            key="editable"
-            type="link"
-            disabled={operateLimits('updata', type)}
-            onClick={() => {
-              MetadataModel.edit = true;
-              MetadataModel.item = record;
-              MetadataModel.type = type;
-              MetadataModel.action = 'edit';
-            }}
-          >
-            <Tooltip title="编辑">
-              <EditOutlined />
-            </Tooltip>
-          </Button>
-        </Tooltip>,
-        ,
-        <a key="delete">
-          <Popconfirm
-            title="确认删除?"
-            onConfirm={async () => {
+        <PermissionButton
+          isPermission={props.permission.update}
+          type="link"
+          key={'edit'}
+          style={{ padding: 0 }}
+          disabled={operateLimits('updata', type)}
+          onClick={() => {
+            MetadataModel.edit = true;
+            MetadataModel.item = record;
+            MetadataModel.type = type;
+            MetadataModel.action = 'edit';
+          }}
+          tooltip={{
+            title: operateLimits('add', type) ? '暂不支持' : '编辑',
+          }}
+        >
+          <EditOutlined />
+        </PermissionButton>,
+        <PermissionButton
+          isPermission={props.permission.update}
+          type="link"
+          key={'delete'}
+          style={{ padding: 0 }}
+          popConfirm={{
+            title: '确认删除?',
+            onConfirm: async () => {
               await removeItem(record);
-            }}
-          >
-            <Tooltip title="删除">
-              <DeleteOutlined />
-            </Tooltip>
-          </Popconfirm>
-        </a>,
+            },
+          }}
+          tooltip={{
+            title: '删除',
+          }}
+        >
+          <DeleteOutlined />
+        </PermissionButton>,
       ],
     },
   ];
@@ -165,7 +173,8 @@ const BaseMetadata = observer((props: Props) => {
         }}
         toolBarRender={() => [
           props.type === 'properties' && (
-            <Button
+            <PermissionButton
+              isPermission={props.permission.update}
               onClick={() => {
                 MetadataModel.importMetadata = true;
               }}
@@ -174,27 +183,29 @@ const BaseMetadata = observer((props: Props) => {
               type="ghost"
             >
               导入属性
-            </Button>
+            </PermissionButton>
           ),
-          <Tooltip key={'add'} title={operateLimits('add', type) ? '暂不支持' : '新增'}>
-            <Button
-              onClick={() => {
-                MetadataModel.edit = true;
-                MetadataModel.item = undefined;
-                MetadataModel.type = type;
-                MetadataModel.action = 'add';
-              }}
-              disabled={operateLimits('add', type)}
-              key="button"
-              icon={<PlusOutlined />}
-              type="primary"
-            >
-              {intl.formatMessage({
-                id: 'pages.searchTable.new',
-                defaultMessage: '新建',
-              })}
-            </Button>
-          </Tooltip>,
+          <PermissionButton
+            isPermission={props.permission.update}
+            key={'add'}
+            onClick={() => {
+              MetadataModel.edit = true;
+              MetadataModel.item = undefined;
+              MetadataModel.type = type;
+              MetadataModel.action = 'add';
+            }}
+            disabled={operateLimits('add', type)}
+            icon={<PlusOutlined />}
+            type="primary"
+            tooltip={{
+              title: operateLimits('add', type) ? '暂不支持' : '新增',
+            }}
+          >
+            {intl.formatMessage({
+              id: 'pages.searchTable.new',
+              defaultMessage: '新建',
+            })}
+          </PermissionButton>,
         ]}
       />
       {MetadataModel.importMetadata && <PropertyImport type={target} />}

+ 13 - 9
src/pages/device/components/Metadata/index.tsx

@@ -1,5 +1,5 @@
 import { observer } from '@formily/react';
-import { Button, Space, Tabs } from 'antd';
+import { Space, Tabs } from 'antd';
 import BaseMetadata from './Base';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import Import from './Import';
@@ -10,6 +10,7 @@ import Service from '@/pages/device/components/Metadata/service';
 import { InfoCircleOutlined } from '@ant-design/icons';
 import styles from './index.less';
 import { InstanceModel } from '@/pages/device/Instance';
+import { PermissionButton } from '@/components';
 
 interface Props {
   tabAction?: ReactNode;
@@ -22,6 +23,9 @@ const Metadata = observer((props: Props) => {
   const intl = useIntl();
   const [visible, setVisible] = useState<boolean>(false);
   const [cat, setCat] = useState<boolean>(false);
+  const { permission } = PermissionButton.usePermission(
+    props.type === 'device' ? 'device/Instance' : 'device/Product',
+  );
   console.log(InstanceModel.detail, 'test');
   return (
     <div style={{ position: 'relative' }}>
@@ -36,19 +40,19 @@ const Metadata = observer((props: Props) => {
         tabBarExtraContent={
           <Space>
             {props?.tabAction}
-            <Button onClick={() => setVisible(true)}>
+            <PermissionButton isPermission={permission.update} onClick={() => setVisible(true)}>
               {intl.formatMessage({
                 id: 'pages.device.productDetail.metadata.quickImport',
                 defaultMessage: '快速导入',
               })}
-            </Button>
-            <Button onClick={() => setCat(true)}>
+            </PermissionButton>
+            <PermissionButton isPermission={permission.update} onClick={() => setCat(true)}>
               {intl.formatMessage({
                 id: 'pages.device.productDetail.metadata',
                 defaultMessage: '物模型',
               })}
               TSL
-            </Button>
+            </PermissionButton>
           </Space>
         }
         destroyInactiveTabPane
@@ -60,7 +64,7 @@ const Metadata = observer((props: Props) => {
           })}
           key="properties"
         >
-          <BaseMetadata target={props.type} type={'properties'} />
+          <BaseMetadata target={props.type} type={'properties'} permission={permission} />
         </Tabs.TabPane>
         <Tabs.TabPane
           tab={intl.formatMessage({
@@ -69,7 +73,7 @@ const Metadata = observer((props: Props) => {
           })}
           key="functions"
         >
-          <BaseMetadata target={props.type} type={'functions'} />
+          <BaseMetadata target={props.type} type={'functions'} permission={permission} />
         </Tabs.TabPane>
         <Tabs.TabPane
           tab={intl.formatMessage({
@@ -78,7 +82,7 @@ const Metadata = observer((props: Props) => {
           })}
           key="events"
         >
-          <BaseMetadata target={props.type} type={'events'} />
+          <BaseMetadata target={props.type} type={'events'} permission={permission} />
         </Tabs.TabPane>
         <Tabs.TabPane
           tab={intl.formatMessage({
@@ -87,7 +91,7 @@ const Metadata = observer((props: Props) => {
           })}
           key="tags"
         >
-          <BaseMetadata target={props.type} type={'tags'} />
+          <BaseMetadata target={props.type} type={'tags'} permission={permission} />
         </Tabs.TabPane>
       </Tabs>
       <Import visible={visible} close={() => setVisible(false)} />

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

@@ -473,7 +473,16 @@ const Media = (props: Props) => {
                   }
                   if (resp.status === 200) {
                     message.success('操作成功!');
-                    history.back();
+                    if (params.get('save')) {
+                      if ((window as any).onTabSaveSuccess) {
+                        if (resp.result) {
+                          (window as any).onTabSaveSuccess(resp.result);
+                          setTimeout(() => window.close(), 300);
+                        }
+                      }
+                    } else {
+                      history.back();
+                    }
                   }
                 }}
               >

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

@@ -23,7 +23,10 @@ const Detail = () => {
 
   useEffect(() => {
     setLoading(true);
-    const id = new URLSearchParams(location.search).get('id') || undefined;
+    const _params = new URLSearchParams(location.search);
+    const id = _params.get('id') || undefined;
+    const paramsType = _params.get('type');
+
     service.getProviders().then((resp) => {
       if (resp.status === 200) {
         setDataSource(resp.result);
@@ -42,6 +45,11 @@ const Detail = () => {
               setType('network');
             }
           });
+        } else if (paramsType) {
+          setType('media');
+          setProvider(resp.result.find((item: any) => item.id === paramsType));
+          setData({});
+          setVisible(false);
         } else {
           setVisible(true);
         }

+ 1 - 1
src/pages/link/Certificate/index.tsx

@@ -175,7 +175,7 @@ const Certificate = () => {
         },
       },
       description: {
-        title: '描述',
+        title: '说明',
         'x-component': 'Input.TextArea',
         'x-decorator': 'FormItem',
       },

+ 1 - 1
src/pages/link/Gateway/index.tsx

@@ -347,7 +347,7 @@ const Gateway = () => {
         'x-decorator': 'FormItem',
       },
       describe: {
-        title: '描述',
+        title: '说明',
         'x-component': 'Input.TextArea',
         'x-decorator': 'FormItem',
         'x-component-props': {

+ 117 - 50
src/pages/media/Device/Save/ProviderSelect.tsx

@@ -1,13 +1,20 @@
 import classNames from 'classnames';
-import { Badge } from 'antd';
+import { Badge, Button, Empty } from 'antd';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 import styles from '@/pages/link/AccessConfig/index.less';
 import { TableCard } from '@/components';
 import './providerSelect.less';
+import usePermissions from '@/hooks/permission';
+import { useIntl } from 'umi';
+import { providerType, service } from '@/pages/media/Device';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { useRequest } from '@@/plugin-request/request';
 
 interface ProviderProps {
   value?: string;
   options?: any[];
+  type: string;
   onChange?: (id: string) => void;
   onSelect?: (id: string, rowData: any) => void;
 }
@@ -15,64 +22,124 @@ interface ProviderProps {
 const defaultImage = require('/public/images/device-access.png');
 
 export default (props: ProviderProps) => {
+  const { permission } = usePermissions('link/AccessConfig');
+  const [options, setOptions] = useState<any[]>([]);
+  const addItemKey = useRef('');
+  const intl = useIntl();
+
+  const itemClick = useCallback(
+    (item: any) => {
+      if (props.onChange) {
+        props.onChange(item.id);
+      }
+
+      if (props.onSelect) {
+        props.onSelect(item.id, item);
+      }
+    },
+    [props],
+  );
+
+  const { run: getProviderList } = useRequest(service.queryProvider, {
+    manual: true,
+    formatResult: (res) => res.result,
+    onSuccess: (resp) => {
+      if (resp.data && resp.data.length) {
+        setOptions(resp.data);
+        if (addItemKey.current) {
+          const _item = resp.data.find((item: any) => item.id === addItemKey.current);
+          itemClick(_item);
+          addItemKey.current = '';
+        }
+      }
+    },
+  });
+
+  const jumpPage = useCallback(() => {
+    const url = getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail']);
+    const tab: any = window.open(`${origin}/#${url}?save=true&type=${props.type}`);
+    tab!.onTabSaveSuccess = (value: any) => {
+      addItemKey.current = value.id;
+      getProviderList({
+        sorts: [{ column: 'createTime', value: 'desc' }],
+        terms: [{ column: 'provider', value: props.type }],
+        paging: false,
+      });
+    };
+  }, [props.type]);
+
+  useEffect(() => {
+    setOptions(props.options || []);
+  }, [props.options]);
+
+  const emptyDescription = permission.add ? (
+    <>
+      暂无数据,请先
+      <Button type={'link'} onClick={jumpPage} style={{ padding: 0 }}>
+        添加{providerType[props.type]} 接入网关
+      </Button>
+    </>
+  ) : (
+    intl.formatMessage({
+      id: 'pages.data.option.noPermission',
+      defaultMessage: '没有权限',
+    })
+  );
+
   return (
     <div className={'provider-list'}>
-      {props.options && props.options.length
-        ? props.options.map((item) => (
-            <div
-              onClick={() => {
-                if (props.onChange) {
-                  props.onChange(item.id);
-                }
-
-                if (props.onSelect) {
-                  props.onSelect(item.id, item);
-                }
+      {options && options.length ? (
+        options.map((item) => (
+          <div
+            onClick={() => {
+              itemClick(item);
+            }}
+            style={{ padding: 16 }}
+          >
+            <TableCard
+              className={classNames({ active: item.id === props.value })}
+              showMask={false}
+              showTool={false}
+              status={item.state.value}
+              statusText={item.state.text}
+              statusNames={{
+                enabled: StatusColorEnum.processing,
+                disabled: StatusColorEnum.error,
               }}
-              style={{ padding: 16 }}
             >
-              <TableCard
-                className={classNames({ active: item.id === props.value })}
-                showMask={false}
-                showTool={false}
-                status={item.state.value}
-                statusText={item.state.text}
-                statusNames={{
-                  enabled: StatusColorEnum.processing,
-                  disabled: StatusColorEnum.error,
-                }}
-              >
-                <div className={styles.context}>
-                  <div>
-                    <img width={88} height={88} src={defaultImage} alt={''} />
+              <div className={styles.context}>
+                <div>
+                  <img width={88} height={88} src={defaultImage} alt={''} />
+                </div>
+                <div className={styles.card}>
+                  <div className={styles.header}>
+                    <div className={styles.title}>{item.name || '--'}</div>
+                    <div className={styles.desc}>{item.description || '--'}</div>
                   </div>
-                  <div className={styles.card}>
-                    <div className={styles.header}>
-                      <div className={styles.title}>{item.name || '--'}</div>
-                      <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>
-                          ))}
-                        </div>
-                      </div>
-                      <div className={styles.procotol}>
-                        <div className={styles.subTitle}>{item?.protocolDetail?.name || '--'}</div>
-                        <p>{item.protocolDetail?.description || '--'}</p>
+                  <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>
+                        ))}
                       </div>
                     </div>
+                    <div className={styles.procotol}>
+                      <div className={styles.subTitle}>{item?.protocolDetail?.name || '--'}</div>
+                      <p>{item.protocolDetail?.description || '--'}</p>
+                    </div>
                   </div>
                 </div>
-              </TableCard>
-            </div>
-          ))
-        : null}
+              </div>
+            </TableCard>
+          </div>
+        ))
+      ) : (
+        <Empty description={<span>{emptyDescription}</span>} />
+      )}
     </div>
   );
 };

+ 4 - 4
src/pages/media/Device/Save/SaveProduct.tsx

@@ -24,10 +24,9 @@ export default (props: SaveProps) => {
   useEffect(() => {
     if (visible) {
       getProviderList({
-        terms: [
-          { column: 'provider', value: props.type },
-          { column: 'state', value: 'enabled' },
-        ],
+        sorts: [{ column: 'createTime', value: 'desc' }],
+        terms: [{ column: 'provider', value: props.type }],
+        paging: false,
       });
     }
   }, [visible]);
@@ -107,6 +106,7 @@ export default (props: SaveProps) => {
         >
           <ProviderItem
             options={providerList}
+            type={props.type}
             onSelect={(_, rowData) => {
               form.setFieldsValue({
                 accessName: rowData.name,

+ 6 - 1
src/pages/media/Device/Save/index.tsx

@@ -326,7 +326,12 @@ export default (props: SaveProps) => {
             <Col span={24}>
               <Form.Item label={'说明'} name={'description'}>
                 <Input.TextArea
-                  placeholder={intlFormat('pages.form.tip.input', '请输入')}
+                  placeholder={intlFormat(
+                    'pages.form.tip.input.props',
+                    '请输入',
+                    'pages.table.describe',
+                    '说明',
+                  )}
                   rows={4}
                   style={{ width: '100%' }}
                   maxLength={200}

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

@@ -28,7 +28,7 @@ import Save from './Save';
 
 export const service = new Service('media/device');
 
-const providerType = {
+export const providerType = {
   'gb28181-2016': 'GB/T28181',
   'fixed-media': '固定地址',
 };

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

@@ -153,7 +153,7 @@ export default (props: ButtonsProps) => {
     {
       title: intl.formatMessage({
         id: 'page.system.menu.describe',
-        defaultMessage: '备注说明',
+        defaultMessage: '说明',
       }),
       dataIndex: 'description',
       // render: (_, row) => () => {
@@ -348,10 +348,10 @@ export default (props: ButtonsProps) => {
             name="description"
             label={intl.formatMessage({
               id: 'pages.table.describe',
-              defaultMessage: '描述',
+              defaultMessage: '说明',
             })}
           >
-            <Input.TextArea disabled={disabled} placeholder={'请输入描述'} />
+            <Input.TextArea disabled={disabled} placeholder={'请输入说明'} />
           </Form.Item>
         </Form>
       </Modal>

+ 2 - 2
src/pages/system/Menu/Detail/edit.tsx

@@ -36,7 +36,7 @@ export default (props: EditProps) => {
   const [show] = useState(true);
   const [accessSupport, setAccessSupport] = useState('unsupported');
   const history = useHistory();
-  const { permission } = PermissionButton.usePermission('system/Menu');
+  const { getOtherPermission } = PermissionButton.usePermission('system/Menu');
 
   const [form] = Form.useForm();
 
@@ -328,7 +328,7 @@ export default (props: EditProps) => {
                 saveData();
               }
             }}
-            isPermission={disabled ? permission.update : permission.add}
+            isPermission={getOtherPermission(['add', 'update'])}
           >
             {intl.formatMessage({
               id: `pages.data.option.${disabled ? 'edit' : 'save'}`,

+ 2 - 1
src/pages/system/Menu/index.tsx

@@ -107,7 +107,7 @@ export default observer(() => {
     {
       title: intl.formatMessage({
         id: 'page.system.menu.describe',
-        defaultMessage: '备注说明',
+        defaultMessage: '说明',
       }),
       width: 200,
       dataIndex: 'describe',
@@ -175,6 +175,7 @@ export default observer(() => {
           key="delete"
           type="link"
           style={{ padding: 0 }}
+          isPermission={permission.delete}
           popConfirm={{
             title: intl.formatMessage({
               id: 'page.system.menu.table.delete',

+ 1 - 1
src/pages/system/Role/index.tsx

@@ -175,7 +175,7 @@ const Role: React.FC = observer(() => {
         'x-component': 'Input.TextArea',
         'x-component-props': {
           checkStrength: true,
-          placeholder: '请输入描述',
+          placeholder: '请输入说明',
         },
         'x-decorator-props': {},
         name: 'password',

+ 1 - 1
src/pages/system/Tenant/index.tsx

@@ -215,7 +215,7 @@ const Tenant = observer(() => {
       },
       description: {
         type: 'string',
-        title: '备注',
+        title: '说明',
         required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Input.TextArea',