Jelajahi Sumber

feat(permission): button permission

lind 3 tahun lalu
induk
melakukan
3ba5010523

+ 37 - 29
src/components/BaseCrud/index.tsx

@@ -1,9 +1,9 @@
 import { useIntl } from '@@/plugin-locale/localeExports';
-import { Button, Dropdown } from 'antd';
+import { Button } from 'antd';
 import type { ActionType, ProColumns, RequestData } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 
-import { EllipsisOutlined, 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';
@@ -47,6 +47,7 @@ export type Props<T> = {
   /** @name 用于存储搜索历史记录的标记*/
   moduleName?: string; //
   footer?: React.ReactNode;
+  disableAdd?: boolean;
 };
 
 const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
@@ -55,15 +56,15 @@ const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
   const {
     columns,
     service,
-    title,
-    menu,
+    // title,
+    // menu,
     schema,
     defaultParams,
     actionRef,
     schemaConfig,
     modelConfig,
     request,
-    toolBar,
+    // toolBar,
     pagination,
     search,
     formEffect,
@@ -78,16 +79,10 @@ const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
       <SearchComponent<T>
         field={columns}
         onSearch={async (data) => {
-          // actionRef.current?.reset?.();
           actionRef.current?.setPageInfo?.({ pageSize: 10 });
           setParam(data);
         }}
         target={moduleName}
-        // onReset={() => {
-        //   // 重置分页及搜索参数
-        //   actionRef.current?.reset?.();
-        //   setParam({});
-        // }}
       />
       <ProTable<T>
         params={param}
@@ -125,25 +120,38 @@ const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
               }
         }
         dateFormatter="string"
-        headerTitle={title}
-        defaultParams={defaultParams}
-        toolBarRender={() =>
-          toolBar || [
-            <Button onClick={CurdModel.add} key="button" icon={<PlusOutlined />} type="primary">
-              {intl.formatMessage({
-                id: 'pages.data.option.add',
-                defaultMessage: '新增',
-              })}
-            </Button>,
-            menu && (
-              <Dropdown key="menu" overlay={menu}>
-                <Button>
-                  <EllipsisOutlined />
-                </Button>
-              </Dropdown>
-            ),
-          ]
+        headerTitle={
+          <Button
+            disabled={props.disableAdd}
+            onClick={CurdModel.add}
+            key="button"
+            icon={<PlusOutlined />}
+            type="primary"
+          >
+            {intl.formatMessage({
+              id: 'pages.data.option.add',
+              defaultMessage: '新增',
+            })}
+          </Button>
         }
+        defaultParams={defaultParams}
+        // toolBarRender={() =>
+        //   toolBar || [
+        //     <Button onClick={CurdModel.add} key="button" icon={<PlusOutlined />} type="primary">
+        //       {intl.formatMessage({
+        //         id: 'pages.data.option.add',
+        //         defaultMessage: '新增',
+        //       })}
+        //     </Button>,
+        //     menu && (
+        //       <Dropdown key="menu" overlay={menu}>
+        //         <Button>
+        //           <EllipsisOutlined />
+        //         </Button>
+        //       </Dropdown>
+        //     ),
+        //   ]
+        // }
       />
       <Save
         reload={() => actionRef.current?.reload()}

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

@@ -11,6 +11,7 @@ import { model } from '@formily/reactive';
 import { observer } from '@formily/react';
 import type { Response } from '@/utils/typings';
 import SearchComponent from '@/components/SearchComponent';
+import { getButtonPermission } from '@/utils/menu';
 
 export const service = new Service('device/category');
 
@@ -63,12 +64,15 @@ const Category = observer(() => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
           key={'edit'}
           onClick={() => {
             state.visible = true;
             state.current = record;
           }}
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('device/Category', ['update', 'add'])}
         >
           <Tooltip
             title={intl.formatMessage({
@@ -78,13 +82,16 @@ const Category = observer(() => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
           key={'add-next'}
           onClick={() => {
             state.visible = true;
             state.parentId = record.id;
           }}
+          disabled={getButtonPermission('device/Category', ['update', 'add'])}
         >
           <Tooltip
             title={intl.formatMessage({
@@ -94,21 +101,25 @@ const Category = observer(() => {
           >
             <PlusOutlined />
           </Tooltip>
-        </a>,
-        <Popconfirm
-          key={'delete'}
-          onConfirm={async () => {
-            const resp = (await service.remove(record.id)) as Response<any>;
-            if (resp.status === 200) {
-              message.success('操作成功');
-            } else {
-              message.error('操作失败');
-            }
-            actionRef.current?.reload();
-          }}
-          title={'确认删除吗?'}
+        </Button>,
+        <Button
+          disabled={getButtonPermission('device/Category', ['delete'])}
+          type="link"
+          style={{ padding: 0 }}
         >
-          <a>
+          <Popconfirm
+            key={'delete'}
+            onConfirm={async () => {
+              const resp = (await service.remove(record.id)) as Response<any>;
+              if (resp.status === 200) {
+                message.success('操作成功');
+              } else {
+                message.error('操作失败');
+              }
+              actionRef.current?.reload();
+            }}
+            title={'确认删除吗?'}
+          >
             <Tooltip
               title={intl.formatMessage({
                 id: 'pages.data.option.remove',
@@ -117,8 +128,8 @@ const Category = observer(() => {
             >
               <DeleteOutlined />
             </Tooltip>
-          </a>
-        </Popconfirm>,
+          </Popconfirm>
+        </Button>,
       ],
     },
   ];
@@ -150,12 +161,9 @@ const Category = observer(() => {
         }}
         rowKey="id"
         columns={columns}
-        headerTitle={intl.formatMessage({
-          id: 'pages.device.category',
-          defaultMessage: '产品分类',
-        })}
-        toolBarRender={() => [
+        headerTitle={
           <Button
+            disabled={getButtonPermission('device/Category', ['add'])}
             onClick={() => (state.visible = true)}
             key="button"
             icon={<PlusOutlined />}
@@ -165,8 +173,8 @@ const Category = observer(() => {
               id: 'pages.data.option.add',
               defaultMessage: '新增',
             })}
-          </Button>,
-        ]}
+          </Button>
+        }
         pagination={false}
         actionRef={actionRef}
       />

+ 199 - 160
src/pages/device/Instance/index.tsx

@@ -29,7 +29,7 @@ import { ProTableCard } from '@/components';
 import SystemConst from '@/utils/const';
 import Token from '@/utils/token';
 import DeviceCard from '@/components/ProTableCard/CardItems/device';
-import { getMenuPathByParams, MENUS_CODE, getButtonPermission } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const statusMap = new Map();
 statusMap.set('在线', 'success');
@@ -69,68 +69,45 @@ const Instance = () => {
   const intl = useIntl();
 
   const tools = (record: DeviceInstance) => [
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.detail',
-        defaultMessage: '查看',
-      })}
-      key={'detail'}
-    >
-      <Button
-        type={'link'}
-        style={{ padding: 0 }}
-        onClick={() => {
-          InstanceModel.current = record;
-          const url = getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], record.id);
-          history.push(url);
-        }}
-      >
-        <EyeOutlined />
-      </Button>
-    </Tooltip>,
-    <Popconfirm
-      key={'state'}
-      title={intl.formatMessage({
-        id: `pages.data.option.${record.state.value !== 'notActive' ? 'disabled' : 'enabled'}.tips`,
-        defaultMessage: '确认禁用?',
-      })}
-      onConfirm={async () => {
-        if (record.state.value !== 'notActive') {
-          await service.undeployDevice(record.id);
-        } else {
-          await service.deployDevice(record.id);
-        }
-        message.success(
-          intl.formatMessage({
-            id: 'pages.data.option.success',
-            defaultMessage: '操作成功!',
-          }),
-        );
-        actionRef.current?.reload();
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      onClick={() => {
+        InstanceModel.current = record;
+        const url = getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], record.id);
+        history.push(url);
       }}
+      disabled={getButtonPermission('device/Instance', ['view'])}
     >
       <Tooltip
         title={intl.formatMessage({
-          id: `pages.data.option.${record.state.value !== 'notActive' ? 'disabled' : 'enabled'}`,
-          defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
+          id: 'pages.data.option.detail',
+          defaultMessage: '查看',
         })}
+        key={'detail'}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
-          {record.state.value !== 'notActive' ? <StopOutlined /> : <CheckCircleOutlined />}
-        </Button>
+        <EyeOutlined />
       </Tooltip>
-    </Popconfirm>,
-    <Popconfirm
-      title={intl.formatMessage({
-        id:
-          record.state.value === 'notActive'
-            ? 'pages.data.option.remove.tips'
-            : 'pages.device.instance.deleteTip',
-      })}
-      key={'delete'}
-      onConfirm={async () => {
-        if (record.state.value === 'notActive') {
-          await service.remove(record.id);
+    </Button>,
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('device/Product', ['action'])}
+    >
+      <Popconfirm
+        key={'state'}
+        title={intl.formatMessage({
+          id: `pages.data.option.${
+            record.state.value !== 'notActive' ? 'disabled' : 'enabled'
+          }.tips`,
+          defaultMessage: '确认禁用?',
+        })}
+        onConfirm={async () => {
+          if (record.state.value !== 'notActive') {
+            await service.undeployDevice(record.id);
+          } else {
+            await service.deployDevice(record.id);
+          }
           message.success(
             intl.formatMessage({
               id: 'pages.data.option.success',
@@ -138,22 +115,57 @@ const Instance = () => {
             }),
           );
           actionRef.current?.reload();
-        } else {
-          message.error(intl.formatMessage({ id: 'pages.device.instance.deleteTip' }));
-        }
-      }}
+        }}
+      >
+        <Tooltip
+          title={intl.formatMessage({
+            id: `pages.data.option.${record.state.value !== 'notActive' ? 'disabled' : 'enabled'}`,
+            defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
+          })}
+        >
+          {record.state.value !== 'notActive' ? <StopOutlined /> : <CheckCircleOutlined />}
+        </Tooltip>
+      </Popconfirm>{' '}
+    </Button>,
+
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('device/Instance', ['delete'])}
     >
-      <Tooltip
+      <Popconfirm
         title={intl.formatMessage({
-          id: 'pages.data.option.remove',
-          defaultMessage: '删除',
+          id:
+            record.state.value === 'notActive'
+              ? 'pages.data.option.remove.tips'
+              : 'pages.device.instance.deleteTip',
         })}
+        key={'delete'}
+        onConfirm={async () => {
+          if (record.state.value === 'notActive') {
+            await service.remove(record.id);
+            message.success(
+              intl.formatMessage({
+                id: 'pages.data.option.success',
+                defaultMessage: '操作成功!',
+              }),
+            );
+            actionRef.current?.reload();
+          } else {
+            message.error(intl.formatMessage({ id: 'pages.device.instance.deleteTip' }));
+          }
+        }}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
+        <Tooltip
+          title={intl.formatMessage({
+            id: 'pages.data.option.remove',
+            defaultMessage: '删除',
+          })}
+        >
           <DeleteOutlined />
-        </Button>
-      </Tooltip>
-    </Popconfirm>,
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
   ];
 
   const columns: ProColumns<DeviceInstance>[] = [
@@ -247,6 +259,7 @@ const Instance = () => {
     <Menu>
       <Menu.Item key="1">
         <Button
+          disabled={getButtonPermission('device/Instance', ['export'])}
           icon={<ExportOutlined />}
           type="default"
           onClick={() => {
@@ -258,6 +271,7 @@ const Instance = () => {
       </Menu.Item>
       <Menu.Item key="2">
         <Button
+          disabled={getButtonPermission('device/Instance', ['import'])}
           icon={<ImportOutlined />}
           onClick={() => {
             setImportVisible(true);
@@ -267,24 +281,30 @@ const Instance = () => {
         </Button>
       </Menu.Item>
       <Menu.Item key="4">
-        <Popconfirm
-          title={'确认激活全部设备?'}
-          onConfirm={() => {
-            setType('active');
-            const activeAPI = `/${
-              SystemConst.API_BASE
-            }/device-instance/deploy?:X_Access_Token=${Token.get()}`;
-            setApi(activeAPI);
-            setOperationVisible(true);
-          }}
+        <Button
+          disabled={getButtonPermission('device/Instance', ['active'])}
+          icon={<CheckCircleOutlined />}
+          type="primary"
+          ghost
         >
-          <Button icon={<CheckCircleOutlined />} type="primary" ghost>
+          <Popconfirm
+            title={'确认激活全部设备?'}
+            onConfirm={() => {
+              setType('active');
+              const activeAPI = `/${
+                SystemConst.API_BASE
+              }/device-instance/deploy?:X_Access_Token=${Token.get()}`;
+              setApi(activeAPI);
+              setOperationVisible(true);
+            }}
+          >
             激活全部设备
-          </Button>
-        </Popconfirm>
+          </Popconfirm>
+        </Button>
       </Menu.Item>
       <Menu.Item key="5">
         <Button
+          disabled={getButtonPermission('device/Instance', ['sync'])}
           icon={<SyncOutlined />}
           type="primary"
           onClick={() => {
@@ -301,44 +321,54 @@ const Instance = () => {
       </Menu.Item>
       {bindKeys.length > 0 && (
         <Menu.Item key="3">
-          <Popconfirm
-            title="确认删除选中设备?"
-            onConfirm={() => {
-              service.batchDeleteDevice(bindKeys).then((resp) => {
-                if (resp.status === 200) {
-                  message.success('操作成功');
-                  actionRef.current?.reset?.();
-                }
-              });
-            }}
-            okText="确认"
-            cancelText="取消"
+          <Button
+            disabled={getButtonPermission('device/Instance', ['delete'])}
+            icon={<DeleteOutlined />}
+            type="primary"
+            danger
           >
-            <Button icon={<DeleteOutlined />} type="primary" danger>
+            <Popconfirm
+              title="确认删除选中设备?"
+              onConfirm={() => {
+                service.batchDeleteDevice(bindKeys).then((resp) => {
+                  if (resp.status === 200) {
+                    message.success('操作成功');
+                    actionRef.current?.reset?.();
+                  }
+                });
+              }}
+              okText="确认"
+              cancelText="取消"
+            >
               删除选中设备
-            </Button>
-          </Popconfirm>
+            </Popconfirm>
+          </Button>
         </Menu.Item>
       )}
       {bindKeys.length > 0 && (
         <Menu.Item key="6">
-          <Popconfirm
-            title="确认禁用选中设备?"
-            onConfirm={() => {
-              service.batchUndeployDevice(bindKeys).then((resp) => {
-                if (resp.status === 200) {
-                  message.success('操作成功');
-                  actionRef.current?.reset?.();
-                }
-              });
-            }}
-            okText="确认"
-            cancelText="取消"
+          <Button
+            disabled={getButtonPermission('device/Instance', ['action'])}
+            icon={<StopOutlined />}
+            type="primary"
+            danger
           >
-            <Button icon={<StopOutlined />} type="primary" danger>
+            <Popconfirm
+              title="确认禁用选中设备?"
+              onConfirm={() => {
+                service.batchUndeployDevice(bindKeys).then((resp) => {
+                  if (resp.status === 200) {
+                    message.success('操作成功');
+                    actionRef.current?.reset?.();
+                  }
+                });
+              }}
+              okText="确认"
+              cancelText="取消"
+            >
               禁用选中设备
-            </Button>
-          </Popconfirm>
+            </Popconfirm>
+          </Button>
         </Menu.Item>
       )}
     </Menu>
@@ -350,8 +380,6 @@ const Instance = () => {
         field={columns}
         target="device-instance"
         onSearch={(data) => {
-          console.log(data);
-          // 重置分页数据
           actionRef.current?.reset?.();
           setSearchParams(data);
         }}
@@ -393,7 +421,7 @@ const Instance = () => {
               setCurrent({});
             }}
             style={{ marginRight: 12 }}
-            disabled={getButtonPermission('device/Instance', 'delete')}
+            disabled={getButtonPermission('device/Instance', 'add')}
             key="button"
             icon={<PlusOutlined />}
             type="primary"
@@ -430,6 +458,7 @@ const Instance = () => {
                   setVisible(true);
                 }}
                 key={'edit'}
+                disabled={getButtonPermission('device/Instance', ['update', 'add'])}
               >
                 <EditOutlined />
                 {intl.formatMessage({
@@ -437,50 +466,25 @@ const Instance = () => {
                   defaultMessage: '编辑',
                 })}
               </Button>,
-              <Popconfirm
-                key={'state'}
-                title={intl.formatMessage({
-                  id: `pages.data.option.${
-                    record.state.value !== 'notActive' ? 'disabled' : 'enabled'
-                  }.tips`,
-                  defaultMessage: '确认禁用?',
-                })}
-                onConfirm={async () => {
-                  if (record.state.value !== 'notActive') {
-                    await service.undeployDevice(record.id);
-                  } else {
-                    await service.deployDevice(record.id);
-                  }
-                  message.success(
-                    intl.formatMessage({
-                      id: 'pages.data.option.success',
-                      defaultMessage: '操作成功!',
-                    }),
-                  );
-                  actionRef.current?.reload();
-                }}
+              <Button
+                disabled={getButtonPermission('device/Instance', ['action'])}
+                type={'link'}
+                style={{ padding: 0 }}
               >
-                <Button type={'link'} style={{ padding: 0 }}>
-                  {record.state.value !== 'notActive' ? <StopOutlined /> : <CheckCircleOutlined />}
-                  {intl.formatMessage({
+                <Popconfirm
+                  key={'state'}
+                  title={intl.formatMessage({
                     id: `pages.data.option.${
                       record.state.value !== 'notActive' ? 'disabled' : 'enabled'
-                    }`,
-                    defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
+                    }.tips`,
+                    defaultMessage: '确认禁用?',
                   })}
-                </Button>
-              </Popconfirm>,
-              <Popconfirm
-                title={intl.formatMessage({
-                  id:
-                    record.state.value === 'notActive'
-                      ? 'pages.data.option.remove.tips'
-                      : 'pages.device.instance.deleteTip',
-                })}
-                key={'delete'}
-                onConfirm={async () => {
-                  if (record.state.value === 'notActive') {
-                    await service.remove(record.id);
+                  onConfirm={async () => {
+                    if (record.state.value !== 'notActive') {
+                      await service.undeployDevice(record.id);
+                    } else {
+                      await service.deployDevice(record.id);
+                    }
                     message.success(
                       intl.formatMessage({
                         id: 'pages.data.option.success',
@@ -488,15 +492,50 @@ const Instance = () => {
                       }),
                     );
                     actionRef.current?.reload();
-                  } else {
-                    message.error(intl.formatMessage({ id: 'pages.device.instance.deleteTip' }));
-                  }
-                }}
+                  }}
+                >
+                  {record.state.value !== 'notActive' ? <StopOutlined /> : <CheckCircleOutlined />}
+                  {intl.formatMessage({
+                    id: `pages.data.option.${
+                      record.state.value !== 'notActive' ? 'disabled' : 'enabled'
+                    }`,
+                    defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
+                  })}
+                </Popconfirm>
+              </Button>,
+
+              <Button
+                key="delete"
+                disabled={getButtonPermission('device/Instance', ['delete'])}
+                type={'link'}
+                style={{ padding: 0 }}
               >
-                <Button type={'link'} style={{ padding: 0 }}>
+                <Popconfirm
+                  title={intl.formatMessage({
+                    id:
+                      record.state.value === 'notActive'
+                        ? 'pages.data.option.remove.tips'
+                        : 'pages.device.instance.deleteTip',
+                  })}
+                  key={'delete'}
+                  onConfirm={async () => {
+                    if (record.state.value === 'notActive') {
+                      await service.remove(record.id);
+                      message.success(
+                        intl.formatMessage({
+                          id: 'pages.data.option.success',
+                          defaultMessage: '操作成功!',
+                        }),
+                      );
+                      actionRef.current?.reload();
+                    } else {
+                      message.error(intl.formatMessage({ id: 'pages.device.instance.deleteTip' }));
+                    }
+                  }}
+                >
                   <DeleteOutlined />
-                </Button>
-              </Popconfirm>,
+                </Popconfirm>
+              </Button>,
             ]}
           />
         )}

+ 146 - 111
src/pages/device/Product/index.tsx

@@ -19,7 +19,7 @@ import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
 import Save from '@/pages/device/Product/Save';
 import SearchComponent from '@/components/SearchComponent';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { ProTableCard } from '@/components';
 import ProductCard from '@/components/ProTableCard/CardItems/product';
 import { downloadObject } from '@/utils/util';
@@ -105,51 +105,57 @@ const Product = observer(() => {
   };
 
   const tools = (record: ProductItem) => [
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.detail',
-        defaultMessage: '查看',
-      })}
-      key={'detail'}
+    <Button
+      disabled={getButtonPermission('device/Product', ['view'])}
+      type={'link'}
+      onClick={() => {
+        productModel.current = record;
+        history.push(`${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], record.id)}`);
+      }}
+      style={{ padding: 0 }}
     >
-      <Button
-        type={'link'}
-        onClick={() => {
-          productModel.current = record;
-          history.push(`${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], record.id)}`);
-        }}
-        style={{ padding: 0 }}
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.detail',
+          defaultMessage: '查看',
+        })}
+        key={'detail'}
       >
         <EyeOutlined />
-      </Button>
-    </Tooltip>,
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.edit',
-        defaultMessage: '编辑',
-      })}
-      key={'edit'}
+      </Tooltip>
+    </Button>,
+    <Button
+      disabled={getButtonPermission('device/Product', ['update'])}
+      key="warning"
+      onClick={() => {
+        setCurrent(record);
+        setVisible(true);
+      }}
+      type={'link'}
+      style={{ padding: 0 }}
     >
-      <Button
-        key="warning"
-        onClick={() => {
-          setCurrent(record);
-          setVisible(true);
-        }}
-        type={'link'}
-        style={{ padding: 0 }}
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.edit',
+          defaultMessage: '编辑',
+        })}
+        key={'edit'}
       >
         <EditOutlined />
-      </Button>
-    </Tooltip>,
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.download',
-        defaultMessage: '下载',
-      })}
-      key={'download'}
+      </Tooltip>
+    </Button>,
+    <Button
+      disabled={getButtonPermission('device/Product', ['export'])}
+      type={'link'}
+      style={{ padding: 0 }}
     >
-      <Button type={'link'} style={{ padding: 0 }}>
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.download',
+          defaultMessage: '下载',
+        })}
+        key={'download'}
+      >
         <DownloadOutlined
           onClick={async () => {
             downloadObject(
@@ -162,55 +168,63 @@ const Product = observer(() => {
             message.success('操作成功');
           }}
         />
-      </Button>
-    </Tooltip>,
-    <Popconfirm
-      key={'state'}
-      title={intl.formatMessage({
-        id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
-        defaultMessage: '是否启用?',
-      })}
-      onConfirm={() => {
-        changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
-      }}
+      </Tooltip>
+    </Button>,
+    <Button
+      disabled={getButtonPermission('device/Product', ['action'])}
+      style={{ padding: 0 }}
+      type={'link'}
     >
-      <Tooltip
+      <Popconfirm
+        key={'state'}
         title={intl.formatMessage({
-          id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}`,
-          defaultMessage: record.state ? '禁用' : '启用',
+          id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
+          defaultMessage: '是否启用?',
         })}
+        onConfirm={() => {
+          changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
+        }}
       >
-        <Button style={{ padding: 0 }} type={'link'}>
+        <Tooltip
+          title={intl.formatMessage({
+            id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}`,
+            defaultMessage: record.state ? '禁用' : '启用',
+          })}
+        >
           {record.state ? <StopOutlined /> : <PlayCircleOutlined />}
-        </Button>
-      </Tooltip>
-    </Popconfirm>,
-    <Popconfirm
-      key="unBindUser"
-      title={intl.formatMessage({
-        id: record.state === 1 ? 'pages.device.productDetail.deleteTip' : 'page.table.isDelete',
-        defaultMessage: '是否删除?',
-      })}
-      onConfirm={async () => {
-        if (record.state === 0) {
-          await deleteItem(record.id);
-        } else {
-          message.error('已发布的产品不能进行删除操作');
-        }
-      }}
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
+    <Button
+      disabled={getButtonPermission('device/Product', ['delete'])}
+      type={'link'}
+      style={{ padding: 0 }}
     >
-      <Tooltip
+      <Popconfirm
+        key="unBindUser"
         title={intl.formatMessage({
-          id: 'pages.data.option.remove',
-          defaultMessage: '删除',
+          id: record.state === 1 ? 'pages.device.productDetail.deleteTip' : 'page.table.isDelete',
+          defaultMessage: '是否删除?',
         })}
-        key={'remove'}
+        onConfirm={async () => {
+          if (record.state === 0) {
+            await deleteItem(record.id);
+          } else {
+            message.error('已发布的产品不能进行删除操作');
+          }
+        }}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
+        <Tooltip
+          title={intl.formatMessage({
+            id: 'pages.data.option.remove',
+            defaultMessage: '删除',
+          })}
+          key={'remove'}
+        >
           <DeleteOutlined />
-        </Button>
-      </Tooltip>
-    </Popconfirm>,
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
   ];
 
   const columns: ProColumns<ProductItem>[] = [
@@ -302,6 +316,7 @@ const Product = observer(() => {
               setCurrent(undefined);
               setVisible(true);
             }}
+            disabled={getButtonPermission('device/Product', ['add'])}
             key="button"
             icon={<PlusOutlined />}
             type="primary"
@@ -312,6 +327,7 @@ const Product = observer(() => {
             })}
           </Button>,
           <Upload
+            disabled={getButtonPermission('device/Product', ['import'])}
             key={'import'}
             showUploadList={false}
             beforeUpload={(file) => {
@@ -344,7 +360,12 @@ const Product = observer(() => {
               return false;
             }}
           >
-            <Button style={{ marginLeft: 12 }}>导入</Button>
+            <Button
+              disabled={getButtonPermission('device/Product', ['import'])}
+              style={{ marginLeft: 12 }}
+            >
+              导入
+            </Button>
           </Upload>,
         ]}
         cardRender={(record) => (
@@ -370,6 +391,7 @@ const Product = observer(() => {
                   setCurrent(record);
                   setVisible(true);
                 }}
+                disabled={getButtonPermission('device/Product', ['update', 'add'])}
                 type={'link'}
                 style={{ padding: 0 }}
               >
@@ -379,7 +401,12 @@ const Product = observer(() => {
                   defaultMessage: '编辑',
                 })}
               </Button>,
-              <Button type={'link'} key={'download'} style={{ padding: 0 }}>
+              <Button
+                disabled={getButtonPermission('device/Product', ['export'])}
+                type={'link'}
+                key={'download'}
+                style={{ padding: 0 }}
+              >
                 <DownloadOutlined
                   onClick={async () => {
                     downloadObject(
@@ -397,45 +424,53 @@ const Product = observer(() => {
                   defaultMessage: '下载',
                 })}
               </Button>,
-              <Popconfirm
-                key={'state'}
-                title={intl.formatMessage({
-                  id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
-                  defaultMessage: '是否启用?',
-                })}
-                onConfirm={() => {
-                  changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
-                }}
+              <Button
+                style={{ padding: 0 }}
+                type={'link'}
+                disabled={getButtonPermission('device/Product', ['action'])}
               >
-                <Button style={{ padding: 0 }} type={'link'}>
+                <Popconfirm
+                  key={'state'}
+                  title={intl.formatMessage({
+                    id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
+                    defaultMessage: '是否启用?',
+                  })}
+                  onConfirm={() => {
+                    changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
+                  }}
+                >
                   {record.state ? <StopOutlined /> : <PlayCircleOutlined />}
                   {intl.formatMessage({
                     id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}`,
                     defaultMessage: record.state ? '禁用' : '启用',
                   })}
-                </Button>
-              </Popconfirm>,
-              <Popconfirm
-                key="delete"
-                title={intl.formatMessage({
-                  id:
-                    record.state === 1
-                      ? 'pages.device.productDetail.deleteTip'
-                      : 'page.table.isDelete',
-                  defaultMessage: '是否删除?',
-                })}
-                onConfirm={async () => {
-                  if (record.state === 0) {
-                    await deleteItem(record.id);
-                  } else {
-                    message.error('已发布的产品不能进行删除操作');
-                  }
-                }}
+                </Popconfirm>
+              </Button>,
+              <Button
+                type={'link'}
+                style={{ padding: 0 }}
+                disabled={getButtonPermission('device/Product', ['delete'])}
               >
-                <Button type={'link'} style={{ padding: 0 }}>
+                <Popconfirm
+                  key="delete"
+                  title={intl.formatMessage({
+                    id:
+                      record.state === 1
+                        ? 'pages.device.productDetail.deleteTip'
+                        : 'page.table.isDelete',
+                    defaultMessage: '是否删除?',
+                  })}
+                  onConfirm={async () => {
+                    if (record.state === 0) {
+                      await deleteItem(record.id);
+                    } else {
+                      message.error('已发布的产品不能进行删除操作');
+                    }
+                  }}
+                >
                   <DeleteOutlined />
-                </Button>
-              </Popconfirm>,
+                </Popconfirm>
+              </Button>,
             ]}
           />
         )}

+ 14 - 4
src/pages/link/AccessConfig/index.tsx

@@ -1,5 +1,5 @@
 import SearchComponent from '@/components/SearchComponent';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ProColumns } from '@jetlinks/pro-table';
 import { Button, Card, Col, Empty, message, Pagination, Popconfirm, Row } from 'antd';
@@ -77,9 +77,10 @@ const AccessConfig = () => {
             handleSearch(dt);
           }}
         />
-        <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-end' }}>
+        <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
           <Button
             type="primary"
+            disabled={getButtonPermission('link/AccessConfig', ['add'])}
             onClick={() => {
               history.push(`${getMenuPathByCode(MENUS_CODE['link/AccessConfig/Detail'])}`);
             }}
@@ -95,6 +96,7 @@ const AccessConfig = () => {
                   {...item}
                   actions={[
                     <Button
+                      disabled={getButtonPermission('link/AccessConfig', ['update'])}
                       key="edit"
                       type="link"
                       onClick={() => {
@@ -108,7 +110,11 @@ const AccessConfig = () => {
                       <EditOutlined />
                       编辑
                     </Button>,
-                    <Button key="warning" type="link">
+                    <Button
+                      key="warning"
+                      type="link"
+                      disabled={getButtonPermission('link/AccessConfig', ['action'])}
+                    >
                       <Popconfirm
                         title={`确认${item.state.value !== 'disabled' ? '禁用' : '启用'}`}
                         onConfirm={() => {
@@ -142,7 +148,11 @@ const AccessConfig = () => {
                         )}
                       </Popconfirm>
                     </Button>,
-                    <Button key="delete" type="link">
+                    <Button
+                      key="delete"
+                      type="link"
+                      disabled={getButtonPermission('link/AccessConfig', ['delete'])}
+                    >
                       <Popconfirm
                         title={'确认删除?'}
                         onConfirm={() => {

+ 38 - 18
src/pages/link/Protocol/index.tsx

@@ -13,6 +13,7 @@ import { onFormValuesChange, registerValidateRules } from '@formily/core';
 import { Store } from 'jetlinks-store';
 import { useLocation } from 'umi';
 import SystemConst from '@/utils/const';
+import { getButtonPermission } from '@/utils/menu';
 
 export const service = new Service('protocol');
 const Protocol = () => {
@@ -70,7 +71,10 @@ const Protocol = () => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('link/Protocol', ['update'])}
           key="edit"
           onClick={() => {
             CurdModel.update(record);
@@ -85,37 +89,52 @@ const Protocol = () => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
+        </Button>,
         record.state !== 1 && (
-          <a key="publish">
+          <Button
+            type="link"
+            style={{ padding: 0 }}
+            disabled={getButtonPermission('link/Protocol', ['action'])}
+            key="publish"
+          >
             <Popconfirm title="确认发布?" onConfirm={() => modifyState(record.id, 'deploy')}>
               <Tooltip title="发布">
                 <CheckCircleOutlined />
               </Tooltip>
             </Popconfirm>
-          </a>
+          </Button>
         ),
         record.state === 1 && (
-          <a key="reload">
+          <Button
+            type="link"
+            style={{ padding: 0 }}
+            disabled={getButtonPermission('link/Protocol', ['action'])}
+            key="reload"
+          >
             <Popconfirm title="确认撤销?" onConfirm={() => modifyState(record.id, 'un-deploy')}>
               <Tooltip title="撤销">
                 <StopOutlined />
               </Tooltip>
             </Popconfirm>
-          </a>
+          </Button>
         ),
-        <Tooltip
+        <Button
+          style={{ padding: 0 }}
           key="delete"
-          title={
-            record.state !== 1
-              ? intl.formatMessage({
-                  id: 'pages.data.option.remove',
-                  defaultMessage: '删除',
-                })
-              : '请先禁用该组件,再删除。'
-          }
+          type="link"
+          disabled={record.state === 1 || getButtonPermission('link/Protocol', ['delete'])}
         >
-          <Button style={{ padding: 0 }} key="delete" type="link" disabled={record.state === 1}>
+          <Tooltip
+            key="delete"
+            title={
+              record.state !== 1
+                ? intl.formatMessage({
+                    id: 'pages.data.option.remove',
+                    defaultMessage: '删除',
+                  })
+                : '请先禁用该组件,再删除。'
+            }
+          >
             <Popconfirm
               title={intl.formatMessage({
                 id: 'pages.data.option.remove.tips',
@@ -141,8 +160,8 @@ const Protocol = () => {
                 <DeleteOutlined />
               </Tooltip>
             </Popconfirm>
-          </Button>
-        </Tooltip>,
+          </Tooltip>
+        </Button>,
       ],
     },
   ];
@@ -326,6 +345,7 @@ const Protocol = () => {
         modelConfig={{ width: '550px' }}
         schema={schema}
         actionRef={actionRef}
+        disableAdd={getButtonPermission('link/Protocol', ['add'])}
         formEffect={() => {
           onFormValuesChange((form) => {
             form.setFieldState('id', (state) => {

+ 38 - 24
src/pages/link/Type/index.tsx

@@ -13,7 +13,7 @@ import { PageContainer } from '@ant-design/pro-layout';
 import type { NetworkItem } from '@/pages/link/Type/typings';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import SearchComponent from '@/components/SearchComponent';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { history } from 'umi';
 import Service from '@/pages/link/service';
 import { Store } from 'jetlinks-store';
@@ -104,7 +104,10 @@ const Network = () => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('link/Type', ['view'])}
           key="edit"
           onClick={() => {
             Store.set('current-network-data', record);
@@ -114,9 +117,14 @@ const Network = () => {
           <Tooltip title="查看">
             <EyeOutlined />
           </Tooltip>
-        </a>,
+        </Button>,
 
-        <a key="changeState">
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('link/Type', ['action'])}
+          key="changeState"
+        >
           <Popconfirm
             title={`确认${record.state.value === 'enabled' ? '禁用' : '启用'}?`}
             onConfirm={async () => {
@@ -142,19 +150,25 @@ const Network = () => {
               {record.state.value === 'enabled' ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
             </Tooltip>
           </Popconfirm>
-        </a>,
-        <Tooltip
-          key="delete"
-          title={
-            record.state.value === 'disabled'
-              ? intl.formatMessage({
-                  id: 'pages.data.option.remove',
-                  defaultMessage: '删除',
-                })
-              : '请先禁用该组件,再删除。'
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={
+            record.state.value === 'enabled' || getButtonPermission('link/Type', ['delete'])
           }
         >
-          <Button type="link" style={{ padding: 0 }} disabled={record.state.value === 'enabled'}>
+          <Tooltip
+            key="delete"
+            title={
+              record.state.value === 'disabled'
+                ? intl.formatMessage({
+                    id: 'pages.data.option.remove',
+                    defaultMessage: '删除',
+                  })
+                : '请先禁用该组件,再删除。'
+            }
+          >
             <Popconfirm
               title="确认删除?"
               onConfirm={async () => {
@@ -167,8 +181,8 @@ const Network = () => {
             >
               <DeleteOutlined />
             </Popconfirm>
-          </Button>
-        </Tooltip>,
+          </Tooltip>
+        </Button>,
       ],
     },
   ];
@@ -189,12 +203,9 @@ const Network = () => {
         params={param}
         columns={columns}
         search={false}
-        headerTitle={'网络组件'}
-        request={async (params) =>
-          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
-        }
-        toolBarRender={() => [
+        headerTitle={
           <Button
+            disabled={getButtonPermission('link/Type', ['add'])}
             onClick={() => {
               pageJump();
             }}
@@ -206,8 +217,11 @@ const Network = () => {
               id: 'pages.data.option.add',
               defaultMessage: '新增',
             })}
-          </Button>,
-        ]}
+          </Button>
+        }
+        request={async (params) =>
+          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+        }
       />
     </PageContainer>
   );

+ 145 - 122
src/pages/media/Cascade/index.tsx

@@ -16,7 +16,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import SearchComponent from '@/components/SearchComponent';
 import { ProTableCard } from '@/components';
 import CascadeCard from '@/components/ProTableCard/CardItems/cascade';
-import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { useHistory } from 'umi';
 import Service from './service';
 import Publish from './Publish';
@@ -33,94 +33,103 @@ const Cascade = () => {
   const [current, setCurrent] = useState<Partial<CascadeItem>>();
 
   const tools = (record: CascadeItem) => [
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.edit',
-        defaultMessage: '编辑',
-      })}
-      key={'edit'}
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('media/Cascade', ['update', 'view'])}
+      onClick={() => {
+        const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Save`]);
+        history.push(url + `?id=${record.id}`);
+      }}
     >
-      <Button
-        type={'link'}
-        style={{ padding: 0 }}
-        onClick={() => {
-          const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Save`]);
-          history.push(url + `?id=${record.id}`);
-        }}
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.edit',
+          defaultMessage: '编辑',
+        })}
+        key={'edit'}
       >
         <EditOutlined />
-      </Button>
-    </Tooltip>,
-    <Tooltip title={'选择通道'} key={'channel'}>
-      <Button
-        type={'link'}
-        style={{ padding: 0 }}
-        onClick={() => {
-          const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Channel`]);
-          history.push(url + `?id=${record.id}`);
-        }}
-      >
-        <LinkOutlined />
-      </Button>
-    </Tooltip>,
-    <Popconfirm
-      key={'share'}
-      title="确认共享!"
-      onConfirm={() => {
-        setCurrent(record);
-        setVisible(true);
+      </Tooltip>
+    </Button>,
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      onClick={() => {
+        const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Channel`]);
+        history.push(url + `?id=${record.id}`);
       }}
     >
-      <Tooltip title={'共享'}>
-        <Button type={'link'}>
-          <ShareAltOutlined />
-        </Button>
+      <Tooltip title={'选择通道'} key={'channel'}>
+        <LinkOutlined />
       </Tooltip>
-    </Popconfirm>,
-    <Popconfirm
-      key={'able'}
-      title={record.status.value === 'disabled' ? '确认启用' : '确认禁用'}
-      onConfirm={async () => {
-        let resp: any = undefined;
-        if (record.status.value === 'disabled') {
-          resp = await service.enabled(record.id);
-        } else {
-          resp = await service.disabled(record.id);
-        }
-        if (resp?.status === 200) {
-          message.success('操作成功!');
-          actionRef.current?.reset?.();
-        }
-      }}
+    </Button>,
+    <Button type={'link'}>
+      <Popconfirm
+        key={'share'}
+        title="确认共享!"
+        onConfirm={() => {
+          setCurrent(record);
+          setVisible(true);
+        }}
+      >
+        <Tooltip title={'共享'}>
+          <ShareAltOutlined />
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('media/Cascade', ['action'])}
     >
-      <Button type={'link'} style={{ padding: 0 }}>
+      <Popconfirm
+        key={'able'}
+        title={record.status.value === 'disabled' ? '确认启用' : '确认禁用'}
+        onConfirm={async () => {
+          let resp: any = undefined;
+          if (record.status.value === 'disabled') {
+            resp = await service.enabled(record.id);
+          } else {
+            resp = await service.disabled(record.id);
+          }
+          if (resp?.status === 200) {
+            message.success('操作成功!');
+            actionRef.current?.reset?.();
+          }
+        }}
+      >
         <Tooltip title={record.status.value === 'disabled' ? '启用' : '禁用'}>
           {record.status.value === 'disabled' ? <CheckCircleOutlined /> : <StopOutlined />}
         </Tooltip>
-      </Button>
-    </Popconfirm>,
-    <Popconfirm
-      title={'确认删除'}
-      key={'delete'}
-      onConfirm={async () => {
-        const resp: any = await service.remove(record.id);
-        if (resp.status === 200) {
-          message.success('操作成功!');
-          actionRef.current?.reset?.();
-        }
-      }}
+      </Popconfirm>
+    </Button>,
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('media/Cascade', ['delete'])}
     >
-      <Tooltip
-        title={intl.formatMessage({
-          id: 'pages.data.option.remove',
-          defaultMessage: '删除',
-        })}
+      <Popconfirm
+        title={'确认删除'}
+        key={'delete'}
+        onConfirm={async () => {
+          const resp: any = await service.remove(record.id);
+          if (resp.status === 200) {
+            message.success('操作成功!');
+            actionRef.current?.reset?.();
+          }
+        }}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
+        <Tooltip
+          title={intl.formatMessage({
+            id: 'pages.data.option.remove',
+            defaultMessage: '删除',
+          })}
+        >
           <DeleteOutlined />
-        </Button>
-      </Tooltip>
-    </Popconfirm>,
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
   ];
 
   const columns: ProColumns<CascadeItem>[] = [
@@ -200,7 +209,10 @@ const Cascade = () => {
       align: 'center',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('media/Cascade', ['view', 'update'])}
           key={'edit'}
           onClick={() => {
             const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Save`]);
@@ -215,8 +227,10 @@ const Cascade = () => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
           key={'channel'}
           onClick={() => {
             const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Channel`]);
@@ -226,55 +240,63 @@ const Cascade = () => {
           <Tooltip title={'选择通道'}>
             <LinkOutlined />
           </Tooltip>
-        </a>,
-        <Popconfirm
-          key={'share'}
-          onConfirm={() => {
-            setVisible(true);
-            setCurrent(record);
-          }}
-          title={'确认共享'}
-        >
-          <a>
+        </Button>,
+        <Button type="link" style={{ padding: 0 }}>
+          <Popconfirm
+            key={'share'}
+            onConfirm={() => {
+              setVisible(true);
+              setCurrent(record);
+            }}
+            title={'确认共享'}
+          >
             <Tooltip title={'共享'}>
               <ShareAltOutlined />
             </Tooltip>
-          </a>
-        </Popconfirm>,
-        <Popconfirm
-          key={'able'}
-          title={record.status.value === 'disabled' ? '确认启用' : '确认禁用'}
-          onConfirm={async () => {
-            let resp: any = undefined;
-            if (record.status.value === 'disabled') {
-              resp = await service.enabled(record.id);
-            } else {
-              resp = await service.disabled(record.id);
-            }
-            if (resp?.status === 200) {
-              message.success('操作成功!');
-              actionRef.current?.reset?.();
-            }
-          }}
+          </Popconfirm>
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('media/Cascade', ['action'])}
         >
-          <a>
+          <Popconfirm
+            key={'able'}
+            title={record.status.value === 'disabled' ? '确认启用' : '确认禁用'}
+            onConfirm={async () => {
+              let resp: any = undefined;
+              if (record.status.value === 'disabled') {
+                resp = await service.enabled(record.id);
+              } else {
+                resp = await service.disabled(record.id);
+              }
+              if (resp?.status === 200) {
+                message.success('操作成功!');
+                actionRef.current?.reset?.();
+              }
+            }}
+          >
             <Tooltip title={record.status.value === 'disabled' ? '启用' : '禁用'}>
               {record.status.value === 'disabled' ? <CheckCircleOutlined /> : <StopOutlined />}
             </Tooltip>
-          </a>
-        </Popconfirm>,
-        <Popconfirm
-          title={'确认删除'}
-          key={'delete'}
-          onConfirm={async () => {
-            const resp: any = await service.remove(record.id);
-            if (resp.status === 200) {
-              message.success('操作成功!');
-              actionRef.current?.reset?.();
-            }
-          }}
+          </Popconfirm>
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('media/Cascade', ['delete'])}
         >
-          <a>
+          <Popconfirm
+            title={'确认删除'}
+            key={'delete'}
+            onConfirm={async () => {
+              const resp: any = await service.remove(record.id);
+              if (resp.status === 200) {
+                message.success('操作成功!');
+                actionRef.current?.reset?.();
+              }
+            }}
+          >
             <Tooltip
               title={intl.formatMessage({
                 id: 'pages.data.option.remove',
@@ -283,8 +305,8 @@ const Cascade = () => {
             >
               <DeleteOutlined />
             </Tooltip>
-          </a>
-        </Popconfirm>,
+          </Popconfirm>
+        </Button>,
       ],
     },
   ];
@@ -327,6 +349,7 @@ const Cascade = () => {
               const url = getMenuPathByCode(MENUS_CODE[`media/Cascade/Save`]);
               history.push(url);
             }}
+            disabled={getButtonPermission('media/Cascade', ['add'])}
             style={{ marginRight: 12 }}
             key="button"
             icon={<PlusOutlined />}

+ 28 - 18
src/pages/media/Stream/index.tsx

@@ -5,7 +5,7 @@ import type { ProColumns } from '@jetlinks/pro-table';
 import { Button, Card, Col, Empty, message, Pagination, Popconfirm, Row, Space } from 'antd';
 import { useEffect, useState } from 'react';
 import Service from '@/pages/media/Stream/service';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { useHistory } from 'umi';
 import styles from './index.less';
 import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
@@ -76,6 +76,7 @@ const Stream = () => {
         {dataSource.data.length > 0 ? (
           <>
             <Button
+              disabled={getButtonPermission('media/Stream', ['add'])}
               type="primary"
               onClick={() => {
                 history.push(`${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'])}`);
@@ -94,7 +95,10 @@ const Stream = () => {
                         <div className={styles.title}>{item?.name}</div>
                         <div className={styles.actions}>
                           <Space>
-                            <span
+                            <Button
+                              type="link"
+                              style={{ padding: 0 }}
+                              disabled={getButtonPermission('media/Stream', ['update'])}
                               className={styles.action}
                               onClick={() => {
                                 history.push(
@@ -108,23 +112,29 @@ const Stream = () => {
                             >
                               <EditOutlined style={{ color: '#000000' }} />
                               <span>编辑</span>
-                            </span>
-                            <Popconfirm
-                              title={'确认删除?'}
-                              onConfirm={() => {
-                                service.remove(item.id).then((resp: any) => {
-                                  if (resp.status === 200) {
-                                    message.success('操作成功!');
-                                    handleSearch({ pageSize: 10, terms: [] });
-                                  }
-                                });
-                              }}
+                            </Button>
+                            <Button
+                              type="link"
+                              style={{ padding: 0 }}
+                              disabled={getButtonPermission('media/Stream', ['delete'])}
                             >
-                              <span className={styles.action}>
-                                <DeleteOutlined style={{ color: '#E50012' }} />
-                                <span>删除</span>
-                              </span>
-                            </Popconfirm>
+                              <Popconfirm
+                                title={'确认删除?'}
+                                onConfirm={() => {
+                                  service.remove(item.id).then((resp: any) => {
+                                    if (resp.status === 200) {
+                                      message.success('操作成功!');
+                                      handleSearch({ pageSize: 10, terms: [] });
+                                    }
+                                  });
+                                }}
+                              >
+                                <span className={styles.action}>
+                                  <DeleteOutlined style={{ color: '#E50012' }} />
+                                  <span>删除</span>
+                                </span>
+                              </Popconfirm>
+                            </Button>
                           </Space>
                         </div>
                       </div>

+ 160 - 131
src/pages/rule-engine/Instance/index.tsx

@@ -19,6 +19,7 @@ import RuleInstanceCard from '@/components/ProTableCard/CardItems/ruleInstance';
 import Save from '@/pages/rule-engine/Instance/Save';
 import SystemConst from '@/utils/const';
 import { StatusColorEnum } from '@/components/BadgeStatus';
+import { getButtonPermission } from '@/utils/menu';
 
 export const service = new Service('rule-engine/instance');
 
@@ -30,79 +31,60 @@ const Instance = () => {
   const [searchParams, setSearchParams] = useState<any>({});
 
   const tools = (record: InstanceItem) => [
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.edit',
-        defaultMessage: '编辑',
-      })}
-      key={'edit'}
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      onClick={() => {
+        setCurrent(record);
+        setVisible(true);
+      }}
+      disabled={getButtonPermission('rule-engine/Instance', ['update'])}
     >
-      <Button
-        type={'link'}
-        style={{ padding: 0 }}
-        onClick={() => {
-          setCurrent(record);
-          setVisible(true);
-        }}
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.edit',
+          defaultMessage: '编辑',
+        })}
+        key={'edit'}
       >
         <EditOutlined />
-      </Button>
-    </Tooltip>,
-    <Tooltip
-      title={intl.formatMessage({
-        id: 'pages.data.option.detail',
-        defaultMessage: '查看',
-      })}
-      key={'detail'}
-    >
-      <Button
-        type={'link'}
-        style={{ padding: 0 }}
-        onClick={() => {
-          window.open(`/${SystemConst.API_BASE}/rule-editor/index.html#flow/${record.id}`);
-        }}
-      >
-        <EyeOutlined />
-      </Button>
-    </Tooltip>,
-    <Popconfirm
-      key={'state'}
-      title={intl.formatMessage({
-        id: `pages.data.option.${record.state.value !== 'stopped' ? 'disabled' : 'enabled'}.tips`,
-        defaultMessage: '确认禁用?',
-      })}
-      onConfirm={async () => {
-        if (record.state.value !== 'stopped') {
-          await service.stopRule(record.id);
-        } else {
-          await service.startRule(record.id);
-        }
-        message.success(
-          intl.formatMessage({
-            id: 'pages.data.option.success',
-            defaultMessage: '操作成功!',
-          }),
-        );
-        actionRef.current?.reload();
+      </Tooltip>
+    </Button>,
+    <Button
+      disabled={getButtonPermission('rule-engine/Instance', ['view'])}
+      type={'link'}
+      style={{ padding: 0 }}
+      onClick={() => {
+        window.open(`/${SystemConst.API_BASE}/rule-editor/index.html#flow/${record.id}`);
       }}
     >
       <Tooltip
         title={intl.formatMessage({
-          id: `pages.data.option.${record.state.value !== 'stopped' ? 'disabled' : 'enabled'}`,
-          defaultMessage: record.state.value !== 'stopped' ? '禁用' : '启用',
+          id: 'pages.data.option.detail',
+          defaultMessage: '查看',
         })}
+        key={'detail'}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
-          {record.state.value !== 'stopped' ? <StopOutlined /> : <CheckCircleOutlined />}
-        </Button>
+        <EyeOutlined />
       </Tooltip>
-    </Popconfirm>,
-    <Popconfirm
-      title={record.state.value === 'stopped' ? '确认删除' : '未停止不能删除'}
-      key={'delete'}
-      onConfirm={async () => {
-        if (record.state.value === 'stopped') {
-          await service.remove(record.id);
+    </Button>,
+    <Button
+      disabled={getButtonPermission('rule-engine/Instance', ['action'])}
+      type={'link'}
+      style={{ padding: 0 }}
+    >
+      <Popconfirm
+        key={'state'}
+        title={intl.formatMessage({
+          id: `pages.data.option.${record.state.value !== 'stopped' ? 'disabled' : 'enabled'}.tips`,
+          defaultMessage: '确认禁用?',
+        })}
+        onConfirm={async () => {
+          if (record.state.value !== 'stopped') {
+            await service.stopRule(record.id);
+          } else {
+            await service.startRule(record.id);
+          }
           message.success(
             intl.formatMessage({
               id: 'pages.data.option.success',
@@ -110,22 +92,52 @@ const Instance = () => {
             }),
           );
           actionRef.current?.reload();
-        } else {
-          message.error('未停止不能删除');
-        }
-      }}
+        }}
+      >
+        <Tooltip
+          title={intl.formatMessage({
+            id: `pages.data.option.${record.state.value !== 'stopped' ? 'disabled' : 'enabled'}`,
+            defaultMessage: record.state.value !== 'stopped' ? '禁用' : '启用',
+          })}
+        >
+          {record.state.value !== 'stopped' ? <StopOutlined /> : <CheckCircleOutlined />}
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
+
+    <Button
+      type={'link'}
+      style={{ padding: 0 }}
+      disabled={getButtonPermission('rule-engine/Instance', ['delete'])}
     >
-      <Tooltip
-        title={intl.formatMessage({
-          id: 'pages.data.option.remove',
-          defaultMessage: '删除',
-        })}
+      <Popconfirm
+        title={record.state.value === 'stopped' ? '确认删除' : '未停止不能删除'}
+        key={'delete'}
+        onConfirm={async () => {
+          if (record.state.value === 'stopped') {
+            await service.remove(record.id);
+            message.success(
+              intl.formatMessage({
+                id: 'pages.data.option.success',
+                defaultMessage: '操作成功!',
+              }),
+            );
+            actionRef.current?.reload();
+          } else {
+            message.error('未停止不能删除');
+          }
+        }}
       >
-        <Button type={'link'} style={{ padding: 0 }}>
+        <Tooltip
+          title={intl.formatMessage({
+            id: 'pages.data.option.remove',
+            defaultMessage: '删除',
+          })}
+        >
           <DeleteOutlined />
-        </Button>
-      </Tooltip>
-    </Popconfirm>,
+        </Tooltip>
+      </Popconfirm>
+    </Button>,
   ];
 
   const columns: ProColumns<InstanceItem>[] = [
@@ -181,7 +193,10 @@ const Instance = () => {
       align: 'center',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('rule-engine/Instance', ['update'])}
           key={'edit'}
           onClick={() => {
             setCurrent(record);
@@ -196,9 +211,12 @@ const Instance = () => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a
-          key={'see'}
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('rule-engine/Instance', ['view'])}
+          key={'view'}
           onClick={() => {
             window.open(`/${SystemConst.API_BASE}/rule-editor/index.html#flow/${record.id}`);
           }}
@@ -211,47 +229,26 @@ const Instance = () => {
           >
             <EyeOutlined />
           </Tooltip>
-        </a>,
-        <Popconfirm
-          key={'state'}
-          title={intl.formatMessage({
-            id: `pages.data.option.${
-              record.state.value !== 'stopped' ? 'disabled' : 'enabled'
-            }.tips`,
-            defaultMessage: '确认禁用?',
-          })}
-          onConfirm={async () => {
-            if (record.state.value !== 'stopped') {
-              await service.stopRule(record.id);
-            } else {
-              await service.startRule(record.id);
-            }
-            message.success(
-              intl.formatMessage({
-                id: 'pages.data.option.success',
-                defaultMessage: '操作成功!',
-              }),
-            );
-            actionRef.current?.reload();
-          }}
+        </Button>,
+        <Button
+          type={'link'}
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('rule-engine/Instance', ['action'])}
         >
-          <Tooltip
+          <Popconfirm
+            key={'state'}
             title={intl.formatMessage({
-              id: `pages.data.option.${record.state.value !== 'stopped' ? 'disabled' : 'enabled'}`,
-              defaultMessage: record.state.value !== 'stopped' ? '禁用' : '启用',
+              id: `pages.data.option.${
+                record.state.value !== 'stopped' ? 'disabled' : 'enabled'
+              }.tips`,
+              defaultMessage: '确认禁用?',
             })}
-          >
-            <Button type={'link'} style={{ padding: 0 }}>
-              {record.state.value !== 'stopped' ? <StopOutlined /> : <CheckCircleOutlined />}
-            </Button>
-          </Tooltip>
-        </Popconfirm>,
-        <Popconfirm
-          title={record.state.value === 'stopped' ? '确认删除' : '未停止不能删除'}
-          key={'delete'}
-          onConfirm={async () => {
-            if (record.state.value === 'stopped') {
-              await service.remove(record.id);
+            onConfirm={async () => {
+              if (record.state.value !== 'stopped') {
+                await service.stopRule(record.id);
+              } else {
+                await service.startRule(record.id);
+              }
               message.success(
                 intl.formatMessage({
                   id: 'pages.data.option.success',
@@ -259,22 +256,53 @@ const Instance = () => {
                 }),
               );
               actionRef.current?.reload();
-            } else {
-              message.error('未停止不能删除');
-            }
-          }}
+            }}
+          >
+            <Tooltip
+              title={intl.formatMessage({
+                id: `pages.data.option.${
+                  record.state.value !== 'stopped' ? 'disabled' : 'enabled'
+                }`,
+                defaultMessage: record.state.value !== 'stopped' ? '禁用' : '启用',
+              })}
+            >
+              {record.state.value !== 'stopped' ? <StopOutlined /> : <CheckCircleOutlined />}
+            </Tooltip>
+          </Popconfirm>
+        </Button>,
+        <Button
+          disabled={getButtonPermission('rule-engine/Instance', ['delete'])}
+          type={'link'}
+          style={{ padding: 0 }}
         >
-          <Tooltip
-            title={intl.formatMessage({
-              id: 'pages.data.option.remove',
-              defaultMessage: '删除',
-            })}
+          <Popconfirm
+            title={record.state.value === 'stopped' ? '确认删除' : '未停止不能删除'}
+            key={'delete'}
+            onConfirm={async () => {
+              if (record.state.value === 'stopped') {
+                await service.remove(record.id);
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              } else {
+                message.error('未停止不能删除');
+              }
+            }}
           >
-            <Button type={'link'} style={{ padding: 0 }}>
+            <Tooltip
+              title={intl.formatMessage({
+                id: 'pages.data.option.remove',
+                defaultMessage: '删除',
+              })}
+            >
               <DeleteOutlined />
-            </Button>
-          </Tooltip>
-        </Popconfirm>,
+            </Tooltip>
+          </Popconfirm>
+        </Button>,
       ],
     },
   ];
@@ -316,6 +344,7 @@ const Instance = () => {
               setVisible(true);
               setCurrent({});
             }}
+            disabled={getButtonPermission('rule-engine/Instance', ['add'])}
             style={{ marginRight: 12 }}
             key="button"
             icon={<PlusOutlined />}

+ 47 - 24
src/pages/system/Department/index.tsx

@@ -4,7 +4,7 @@ import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import * as React from 'react';
 import { useEffect, useRef, useState } from 'react';
-import { Link, useIntl, useLocation } from 'umi';
+import { history, useIntl, useLocation } from 'umi';
 import { Button, message, Popconfirm, Tooltip } from 'antd';
 import {
   DeleteOutlined,
@@ -21,7 +21,7 @@ import { observer } from '@formily/react';
 import { model } from '@formily/reactive';
 import Save from './save';
 import SearchComponent from '@/components/SearchComponent';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('organization');
 
@@ -85,7 +85,10 @@ export default observer(() => {
       valueType: 'option',
       width: 240,
       render: (text, record) => [
-        <a
+        <Button
+          style={{ padding: 0 }}
+          type="link"
+          disabled={getButtonPermission('system/Department', ['add', 'update'])}
           key="editable"
           onClick={() => {
             State.current = record;
@@ -100,8 +103,11 @@ export default observer(() => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a
+        </Button>,
+        <Button
+          style={{ padding: 0 }}
+          type="link"
+          disabled={getButtonPermission('system/Department', ['add'])}
           key="editable"
           onClick={() => {
             State.current = {
@@ -118,13 +124,20 @@ export default observer(() => {
           >
             <PlusCircleOutlined />
           </Tooltip>
-        </a>,
-        <Link
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
           key="assets"
-          to={`${getMenuPathByParams(
-            MENUS_CODE['system/Department/Detail'],
-            record.id,
-          )}?type=assets`}
+          disabled={getButtonPermission('system/Department', ['add', 'view', 'update'])}
+          onClick={() => {
+            history.push(
+              `${getMenuPathByParams(
+                MENUS_CODE['system/Department/Detail'],
+                record.id,
+              )}?type=assets`,
+            );
+          }}
         >
           <Tooltip
             title={intl.formatMessage({
@@ -134,10 +147,17 @@ export default observer(() => {
           >
             <MedicineBoxOutlined />
           </Tooltip>
-        </Link>,
-        <Link
+        </Button>,
+        <Button
+          type="link"
           key="user"
-          to={`${getMenuPathByParams(MENUS_CODE['system/Department/Detail'], record.id)}?type=user`}
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('system/Department', ['add', 'view', 'update'])}
+          onClick={() =>
+            history.push(
+              `${getMenuPathByParams(MENUS_CODE['system/Department/Detail'], record.id)}?type=user`,
+            )
+          }
         >
           <Tooltip
             title={intl.formatMessage({
@@ -147,7 +167,7 @@ export default observer(() => {
           >
             <TeamOutlined />
           </Tooltip>
-        </Link>,
+        </Button>,
         <Popconfirm
           key="unBindUser"
           title={intl.formatMessage({
@@ -157,6 +177,7 @@ export default observer(() => {
           onConfirm={() => {
             deleteItem(record.id);
           }}
+          disabled={getButtonPermission('system/Department', ['delete'])}
         >
           <Tooltip
             title={intl.formatMessage({
@@ -164,9 +185,14 @@ export default observer(() => {
               defaultMessage: '删除',
             })}
           >
-            <a key="delete">
+            <Button
+              style={{ padding: 0 }}
+              type="link"
+              disabled={getButtonPermission('system/Department', ['delete'])}
+              key="delete"
+            >
               <DeleteOutlined />
-            </a>
+            </Button>
           </Tooltip>
         </Popconfirm>,
       ],
@@ -293,8 +319,9 @@ export default observer(() => {
         pagination={false}
         search={false}
         params={param}
-        toolBarRender={() => [
+        headerTitle={
           <Button
+            disabled={getButtonPermission('system/Department', ['add'])}
             onClick={() => (State.visible = true)}
             key="button"
             icon={<PlusOutlined />}
@@ -304,12 +331,8 @@ export default observer(() => {
               id: 'pages.data.option.add',
               defaultMessage: '新增',
             })}
-          </Button>,
-        ]}
-        headerTitle={intl.formatMessage({
-          id: 'pages.system.department',
-          defaultMessage: '部门列表',
-        })}
+          </Button>
+        }
       />
       <Save<DepartmentItem>
         title={

+ 22 - 14
src/pages/system/Menu/index.tsx

@@ -18,7 +18,7 @@ import SearchComponent from '@/components/SearchComponent';
 import Service from './service';
 import type { MenuItem } from './typing';
 import moment from 'moment';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('menu');
 
@@ -130,11 +130,14 @@ export default observer(() => {
       valueType: 'option',
       width: 240,
       render: (_, record) => [
-        <a
+        <Button
+          type="link"
           key="view"
+          style={{ padding: 0 }}
           onClick={() => {
             pageJump(record.id, record.parentId || '');
           }}
+          disabled={getButtonPermission('system/Menu', ['view'])}
         >
           <Tooltip
             title={intl.formatMessage({
@@ -144,9 +147,12 @@ export default observer(() => {
           >
             <SearchOutlined />
           </Tooltip>
-        </a>,
-        <a
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
           key="editable"
+          disabled={getButtonPermission('system/Menu', ['add'])}
           onClick={() => {
             State.current = {
               parentId: record.id,
@@ -163,7 +169,7 @@ export default observer(() => {
           >
             <PlusCircleOutlined />
           </Tooltip>
-        </a>,
+        </Button>,
         <Popconfirm
           key="unBindUser"
           title={intl.formatMessage({
@@ -180,9 +186,14 @@ export default observer(() => {
               defaultMessage: '删除',
             })}
           >
-            <a key="delete">
+            <Button
+              type="link"
+              style={{ padding: 0 }}
+              disabled={getButtonPermission('system/Menu', ['delete'])}
+              key="delete"
+            >
               <DeleteOutlined />
-            </a>
+            </Button>
           </Tooltip>
         </Popconfirm>,
       ],
@@ -253,8 +264,9 @@ export default observer(() => {
             status: response.status,
           };
         }}
-        toolBarRender={() => [
+        headerTitle={
           <Button
+            disabled={getButtonPermission('system/Menu', ['add'])}
             onClick={() => {
               pageJump();
             }}
@@ -266,12 +278,8 @@ export default observer(() => {
               id: 'pages.data.option.add',
               defaultMessage: '新增',
             })}
-          </Button>,
-        ]}
-        headerTitle={intl.formatMessage({
-          id: 'pages.system.menu',
-          defaultMessage: '菜单列表',
-        })}
+          </Button>
+        }
       />
       {/*<Modal*/}
       {/*  title={intl.formatMessage({*/}

+ 55 - 44
src/pages/system/Permission/index.tsx

@@ -7,7 +7,7 @@ import {
   PlayCircleOutlined,
   PlusOutlined,
 } from '@ant-design/icons';
-import { Badge, Button, Dropdown, Menu, message, Popconfirm, Tooltip, Upload } from 'antd';
+import { Badge, Button, Dropdown, Menu, message, Popconfirm, Space, Tooltip, Upload } from 'antd';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { useIntl } from '@@/plugin-locale/localeExports';
@@ -19,6 +19,7 @@ import Save from './Save';
 import SystemConst from '@/utils/const';
 import { downloadObject } from '@/utils/util';
 import Token from '@/utils/token';
+import { getButtonPermission } from '@/utils/menu';
 
 export const service = new Service('permission');
 const Permission: React.FC = observer(() => {
@@ -62,11 +63,12 @@ const Permission: React.FC = observer(() => {
             };
           }}
         >
-          <Button>导入</Button>
+          <Button disabled={getButtonPermission('system/Permission', ['import'])}>导入</Button>
         </Upload>
       </Menu.Item>
       <Menu.Item key="export">
         <Popconfirm
+          disabled={getButtonPermission('system/Permission', ['export'])}
           title={'确认导出?'}
           onConfirm={() => {
             service.getPermission({ ...param, paging: false }).subscribe((resp) => {
@@ -79,7 +81,7 @@ const Permission: React.FC = observer(() => {
             });
           }}
         >
-          <Button>导出</Button>
+          <Button disabled={getButtonPermission('system/Permission', ['export'])}>导出</Button>
         </Popconfirm>
       </Menu.Item>
     </Menu>
@@ -142,7 +144,10 @@ const Permission: React.FC = observer(() => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <a
+        <Button
+          type="link"
+          disabled={getButtonPermission('system/Permission', ['add'])}
+          style={{ padding: 0 }}
           key="editable"
           onClick={() => {
             edit(record);
@@ -156,8 +161,13 @@ const Permission: React.FC = observer(() => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a key="view">
+        </Button>,
+        <Button
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('system/Permission', ['action'])}
+          type="link"
+          key="view"
+        >
           <Popconfirm
             title={intl.formatMessage({
               id: 'pages.data.option.disabled.tips',
@@ -187,19 +197,23 @@ const Permission: React.FC = observer(() => {
               {record.status ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
             </Tooltip>
           </Popconfirm>
-        </a>,
-        <Tooltip
-          key="delete"
-          title={
-            record.status === 0
-              ? intl.formatMessage({
-                  id: 'pages.data.option.remove',
-                  defaultMessage: '删除',
-                })
-              : '请先禁用该权限,再删除。'
-          }
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={record.status === 1 || getButtonPermission('system/Permission', ['delete'])}
         >
-          <Button type="link" style={{ padding: 0 }} disabled={record.status === 1}>
+          <Tooltip
+            key="delete"
+            title={
+              record.status === 0
+                ? intl.formatMessage({
+                    id: 'pages.data.option.remove',
+                    defaultMessage: '删除',
+                  })
+                : '请先禁用该权限,再删除。'
+            }
+          >
             <Popconfirm
               title={intl.formatMessage({
                 id: 'pages.data.option.remove.tips',
@@ -218,8 +232,8 @@ const Permission: React.FC = observer(() => {
             >
               <DeleteOutlined />
             </Popconfirm>
-          </Button>
-        </Tooltip>,
+          </Tooltip>
+        </Button>,
       ],
     },
   ];
@@ -234,39 +248,36 @@ const Permission: React.FC = observer(() => {
           actionRef.current?.reset?.();
           setParam(data);
         }}
-        // onReset={() => {
-        //   // 重置分页及搜索参数
-        //   actionRef.current?.reset?.();
-        //   setParam({});
-        // }}
       />
       <ProTable<PermissionItem>
         actionRef={actionRef}
         params={param}
         columns={columns}
         search={false}
-        headerTitle={'权限列表'}
+        headerTitle={
+          <Space>
+            <Button
+              onClick={() => {
+                setMode('add');
+              }}
+              disabled={getButtonPermission('system/Permission', ['add'])}
+              key="button"
+              icon={<PlusOutlined />}
+              type="primary"
+            >
+              {intl.formatMessage({
+                id: 'pages.data.option.add',
+                defaultMessage: '新增',
+              })}
+            </Button>
+            <Dropdown key={'more'} overlay={menu} placement="bottom">
+              <Button>批量操作</Button>
+            </Dropdown>
+          </Space>
+        }
         request={async (params) =>
           service.query({ ...params, sorts: [{ name: 'id', order: 'asc' }] })
         }
-        toolBarRender={() => [
-          <Button
-            onClick={() => {
-              setMode('add');
-            }}
-            key="button"
-            icon={<PlusOutlined />}
-            type="primary"
-          >
-            {intl.formatMessage({
-              id: 'pages.data.option.add',
-              defaultMessage: '新增',
-            })}
-          </Button>,
-          <Dropdown key={'more'} overlay={menu} placement="bottom">
-            <Button>批量操作</Button>
-          </Dropdown>,
-        ]}
       />
       <Save
         model={model}

+ 20 - 7
src/pages/system/Role/index.tsx

@@ -1,17 +1,17 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import React, { useEffect, useRef } from 'react';
 import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
-import { message, Popconfirm, Tooltip } from 'antd';
+import { Button, message, Popconfirm, Tooltip } from 'antd';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import BaseCrud from '@/components/BaseCrud';
 import Service from './service';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { observer } from '@formily/react';
-import { Link, useLocation } from 'umi';
+import { history, useLocation } from 'umi';
 import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { CurdModel } from '@/components/BaseCrud/model';
-import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('role');
 
@@ -83,7 +83,14 @@ const Role: React.FC = observer(() => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <Link to={`${getMenuPathByParams(MENUS_CODE['system/Role/Detail'], record.id)}`} key="link">
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('system/Role', ['update'])}
+          onClick={() =>
+            history.push(`${getMenuPathByParams(MENUS_CODE['system/Role/Detail'], record.id)}`)
+          }
+        >
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.edit',
@@ -93,8 +100,13 @@ const Role: React.FC = observer(() => {
           >
             <EditOutlined />
           </Tooltip>
-        </Link>,
-        <a key="delete">
+        </Button>,
+        <Button
+          type="link"
+          disabled={getButtonPermission('system/Role', ['delete'])}
+          style={{ padding: 0 }}
+          key="delete"
+        >
           <Popconfirm
             title={intl.formatMessage({
               id: 'pages.data.option.remove.tips',
@@ -120,7 +132,7 @@ const Role: React.FC = observer(() => {
               <DeleteOutlined />
             </Tooltip>
           </Popconfirm>
-        </a>,
+        </Button>,
       ],
     },
   ];
@@ -195,6 +207,7 @@ const Role: React.FC = observer(() => {
   return (
     <PageContainer>
       <BaseCrud<RoleItem>
+        disableAdd={getButtonPermission('system/Role', ['add'])}
         actionRef={actionRef}
         moduleName="role"
         columns={columns}

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

@@ -1,4 +1,4 @@
-import { message, Modal, TreeSelect as ATreeSelect } from 'antd';
+import { message, TreeSelect as ATreeSelect } from 'antd';
 import { useIntl } from 'umi';
 import type { Field } from '@formily/core';
 import { createForm } from '@formily/core';
@@ -11,6 +11,7 @@ import type { ISchema } from '@formily/json-schema';
 import { action } from '@formily/reactive';
 import type { Response } from '@/utils/typings';
 import { service } from '@/pages/system/User';
+import { Modal } from '@/components';
 
 interface Props {
   model: 'add' | 'edit' | 'query';
@@ -329,6 +330,8 @@ const Save = (props: Props) => {
       onCancel={props.close}
       onOk={save}
       width="35vw"
+      permissionCode={'system/User'}
+      permission={['add', 'edit']}
     >
       <Form form={form} layout="vertical">
         <SchemaField schema={schema} scope={{ useAsyncDataSource, getRole, getOrg }} />

+ 31 - 15
src/pages/system/User/index.tsx

@@ -15,6 +15,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { useRef, useState } from 'react';
 import Save from './Save';
 import { observer } from '@formily/react';
+import { getButtonPermission } from '@/utils/menu';
 
 export const service = new Service('user');
 
@@ -112,7 +113,13 @@ const User = observer(() => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <a key="editable" onClick={() => edit(record)}>
+        <Button
+          style={{ padding: 0 }}
+          type="link"
+          disabled={getButtonPermission('system/User', ['update', 'view'])}
+          key="editable"
+          onClick={() => edit(record)}
+        >
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.edit',
@@ -121,8 +128,13 @@ const User = observer(() => {
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a key="changeState">
+        </Button>,
+        <Button
+          style={{ padding: 0 }}
+          disabled={getButtonPermission('system/User', ['action'])}
+          type="link"
+          key="changeState"
+        >
           <Popconfirm
             title={intl.formatMessage({
               id: `pages.data.option.${record.status ? 'disabled' : 'enabled'}.tips`,
@@ -151,9 +163,13 @@ const User = observer(() => {
               {record.status ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
             </Tooltip>
           </Popconfirm>
-        </a>,
-        <Tooltip title={record.status === 0 ? '删除' : '请先禁用该用户,再删除。'} key="delete">
-          <Button type="link" style={{ padding: 0 }} disabled={record.status === 1}>
+        </Button>,
+        <Button
+          type="link"
+          style={{ padding: 0 }}
+          disabled={record.status === 1 || getButtonPermission('system/User', 'delete')}
+        >
+          <Tooltip title={record.status === 0 ? '删除' : '请先禁用该用户,再删除。'} key="delete">
             <Popconfirm
               onConfirm={async () => {
                 await service.remove(record.id);
@@ -163,8 +179,8 @@ const User = observer(() => {
             >
               <DeleteOutlined />
             </Popconfirm>
-          </Button>
-        </Tooltip>,
+          </Tooltip>
+        </Button>,
       ],
     },
   ];
@@ -187,15 +203,12 @@ const User = observer(() => {
         params={param}
         columns={columns}
         search={false}
-        headerTitle={'用户列表'}
-        request={async (params) =>
-          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
-        }
-        toolBarRender={() => [
+        headerTitle={
           <Button
             onClick={() => {
               setMode('add');
             }}
+            disabled={getButtonPermission('system/User', ['add'])}
             key="button"
             icon={<PlusOutlined />}
             type="primary"
@@ -204,8 +217,11 @@ const User = observer(() => {
               id: 'pages.data.option.add',
               defaultMessage: '新增',
             })}
-          </Button>,
-        ]}
+          </Button>
+        }
+        request={async (params) =>
+          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+        }
       />
       <Save
         model={model}

+ 9 - 1
src/utils/menu/router.ts

@@ -106,7 +106,15 @@ export enum MENUS_CODE {
 
 export type MENUS_CODE_TYPE = keyof typeof MENUS_CODE;
 
-export type BUTTON_PERMISSION = 'add' | 'delete' | 'import' | 'view' | 'export' | 'update' | string;
+export type BUTTON_PERMISSION =
+  | 'add'
+  | 'delete'
+  | 'import'
+  | 'view'
+  | 'export'
+  | 'update'
+  | 'action'
+  | string;
 
 export const getDetailNameByCode = {
   'system/Menu/Detail': '菜单详情',