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

merge(permission): role 、permission、

Lind 3 лет назад
Родитель
Сommit
a7fb7bc7a8

+ 6 - 4
config/proxy.ts

@@ -9,10 +9,12 @@
 export default {
   dev: {
     '/jetlinks': {
-      target: 'http://192.168.33.222:8844/',
-      ws: 'ws://192.168.33.222:8844/',
-      // target: 'http://192.168.32.44:8844/',
-      // ws: 'ws://192.168.32.44:8844/',
+      // target: 'http://192.168.33.222:8844/',
+      // ws: 'ws://192.168.33.222:8844/',
+      target: 'http://120.79.18.123:8844/',
+      ws: 'ws://120.79.18.123:8844/',
+      // target: 'http://192.168.66.5:8844/',
+      // ws: 'ws://192.168.66.5:8844/',
       // ws: 'ws://demo.jetlinks.cn/jetlinks',
       // target: 'http://demo.jetlinks.cn/jetlinks',
       changeOrigin: true,

+ 332 - 345
config/routes.ts

@@ -15,12 +15,12 @@
       },
     ],
   },
-  // {
-  //   path: '/analysis',
-  //   name: 'analysis',
-  //   icon: 'smile',
-  //   component: './Analysis',
-  // },
+  {
+    path: '/analysis',
+    name: 'analysis',
+    icon: 'smile',
+    component: './Analysis',
+  },
   {
     path: '/system',
     name: 'system',
@@ -45,6 +45,13 @@
         component: './system/Role',
       },
       {
+        hideInMenu: true,
+        path: '/system/role/edit/:id',
+        name: 'role-edit',
+        icon: 'smile',
+        component: './system/Role/Edit',
+      },
+      {
         path: '/system/permission',
         name: 'permission',
         icon: 'smile',
@@ -75,360 +82,340 @@
         icon: 'smile',
         component: './system/DataSource',
       },
+    ],
+  },
+  {
+    path: '/device',
+    name: 'device',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/device',
+        redirect: '/device/product',
+      },
+      {
+        path: '/device/product',
+        name: 'product',
+        icon: 'smile',
+        component: './device/Product',
+      },
+      {
+        path: '/device/category',
+        name: 'category',
+        icon: 'smile',
+        component: './device/Category',
+      },
+      {
+        hideInMenu: true,
+        path: '/device/product/detail/:id',
+        name: 'product-detail',
+        icon: 'smile',
+        component: './device/Product/Detail',
+      },
       {
-        path: '/system/department',
-        name: 'department',
+        path: '/device/instance',
+        name: 'instance',
         icon: 'smile',
-        component: './system/Department',
+        component: './device/Instance',
       },
       {
         hideInMenu: true,
-        path: '/system/department/:id/assets',
-        name: 'assets',
+        path: '/device/instance/detail/:id',
+        name: 'instance-detail',
+        icon: 'smile',
+        component: './device/Instance/Detail',
+      },
+      {
+        path: '/device/command',
+        name: 'command',
         icon: 'smile',
-        component: './system/Department/Assets',
+        component: './device/Command',
+      },
+      {
+        path: '/device/firmware',
+        name: 'firmware',
+        icon: 'smile',
+        component: './device/Firmware',
       },
       {
         hideInMenu: true,
-        path: '/system/department/:id/user',
-        name: 'member',
+        path: '/device/firmware/detail/:id',
+        name: 'firmware-detail',
+        icon: 'smile',
+        component: './device/Firmware/Detail',
+      },
+      {
+        path: '/device/alarm',
+        name: 'alarm',
+        icon: 'smile',
+        component: './device/Alarm',
+      },
+      {
+        path: '/device/location',
+        name: 'location',
+        icon: 'smile',
+        component: './device/Location',
+      },
+    ],
+  },
+  {
+    path: '/link',
+    name: 'link',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/link',
+        redirect: '/link/certificate',
+      },
+      {
+        path: '/link/certificate',
+        name: 'certificate',
+        icon: 'smile',
+        component: './link/Certificate',
+      },
+      {
+        path: '/link/protocol',
+        name: 'protocol',
+        icon: 'smile',
+        component: './link/Protocol',
+      },
+      {
+        path: 'link/type',
+        name: 'type',
+        icon: 'smile',
+        component: './link/Type',
+      },
+      {
+        path: '/link/gateway',
+        name: 'gateway',
+        icon: 'smile',
+        component: './link/Gateway',
+      },
+      {
+        path: '/link/opcua',
+        name: 'opcua',
+        icon: 'smile',
+        component: './link/Opcua',
+      },
+    ],
+  },
+  {
+    path: '/notice',
+    name: 'notice',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/notice',
+        redirect: '/notice/config',
+      },
+      {
+        path: '/notice/config',
+        name: 'config',
+        icon: 'smile',
+        component: './notice/Config',
+      },
+      {
+        path: '/notice/template',
+        name: 'template',
+        icon: 'smile',
+        component: './notice/Template',
+      },
+    ],
+  },
+  {
+    path: '/rule-engine',
+    name: 'rule-engine',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/rule-engine',
+        redirect: '/rule-engine/instance',
+      },
+      {
+        path: '/rule-engine/instance',
+        name: 'instance',
+        icon: 'smile',
+        component: './rule-engine/Instance',
+      },
+      {
+        path: '/rule-engine/sqlRule',
+        name: 'sqlRule',
+        icon: 'smile',
+        component: './rule-engine/SQLRule',
+      },
+      {
+        path: '/rule-engine/scene',
+        name: 'scene',
+        icon: 'smile',
+        component: './rule-engine/Scene',
+      },
+    ],
+  },
+  {
+    path: '/visualization',
+    name: 'visualization',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/visualization',
+        redirect: '/visualization/category',
+      },
+      {
+        path: '/visualization/category',
+        name: 'category',
+        icon: 'smile',
+        component: './visualization/Category',
+      },
+      {
+        path: '/visualization/screen',
+        name: 'screen',
+        icon: 'smile',
+        component: './visualization/Screen',
+      },
+      {
+        path: '/visualization/configuration',
+        name: 'configuration',
+        icon: 'smile',
+        component: './visualization/Configuration',
+      },
+    ],
+  },
+  {
+    path: '/simulator',
+    name: 'simulator',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/simulator',
+        redirect: '/simulator/device',
+      },
+      {
+        path: '/simulator/device',
+        name: 'device',
+        icon: 'smile',
+        component: './simulator/Device',
+      },
+    ],
+  },
+  {
+    path: '/log',
+    name: 'log',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/log',
+        redirect: '/log/access',
+      },
+      {
+        path: '/log/access',
+        name: 'access',
+        icon: 'smile',
+        component: './log/Access',
+      },
+      {
+        path: '/log/system',
+        name: 'system',
+        icon: 'smile',
+        component: './log/System',
+      },
+    ],
+  },
+  {
+    path: '/cloud',
+    name: 'cloud',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/cloud',
+        redirect: '/cloud/duer',
+      },
+      {
+        path: '/cloud/dueros',
+        name: 'DuerOS',
+        icon: 'smile',
+        component: './cloud/DuerOS',
+      },
+      {
+        path: '/cloud/aliyun',
+        name: 'aliyun',
+        icon: 'smile',
+        component: './cloud/Aliyun',
+      },
+      {
+        path: '/cloud/onenet',
+        name: 'onenet',
+        icon: 'smile',
+        component: './cloud/Onenet',
+      },
+      {
+        path: '/cloud/ctwing',
+        name: 'ctwing',
+        icon: 'smile',
+        component: './cloud/Ctwing',
+      },
+    ],
+  },
+  {
+    path: '/media',
+    name: 'media',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/media',
+        redirect: '/media/config',
+      },
+      {
+        path: '/media/config',
+        name: 'config',
+        icon: 'smile',
+        component: './media/Config',
+      },
+      {
+        path: '/media/device',
+        name: 'device',
+        icon: 'smile',
+        component: './media/Device',
+      },
+      {
+        path: '/media/reveal',
+        name: 'reveal',
+        icon: 'smile',
+        component: './media/Reveal',
+      },
+      {
+        path: '/media/cascade',
+        name: 'cascade',
+        icon: 'smile',
+        component: './media/Cascade',
+      },
+    ],
+  },
+  {
+    path: '/edge',
+    name: 'edge',
+    icon: 'crown',
+    routes: [
+      {
+        path: '/edge',
+        redirect: '/edge/product',
+      },
+      {
+        path: '/edge/product',
+        name: 'product',
+        icon: 'smile',
+        component: './edge/Product',
+      },
+      {
+        path: '/edge/device',
+        name: 'device',
         icon: 'smile',
-        component: './system/Department/Member',
+        component: './edge/Device',
       },
     ],
   },
-  // {
-  //   path: '/device',
-  //   name: 'device',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/device',
-  //       redirect: '/device/product',
-  //     },
-  //     {
-  //       path: '/device/product',
-  //       name: 'product',
-  //       icon: 'smile',
-  //       component: './device/Product',
-  //     },
-  //     {
-  //       path: '/device/category',
-  //       name: 'category',
-  //       icon: 'smile',
-  //       component: './device/Category',
-  //     },
-  //     {
-  //       hideInMenu: true,
-  //       path: '/device/product/detail/:id',
-  //       name: 'product-detail',
-  //       icon: 'smile',
-  //       component: './device/Product/Detail',
-  //     },
-  //     {
-  //       path: '/device/instance',
-  //       name: 'instance',
-  //       icon: 'smile',
-  //       component: './device/Instance',
-  //     },
-  //     {
-  //       hideInMenu: true,
-  //       path: '/device/instance/detail/:id',
-  //       name: 'instance-detail',
-  //       icon: 'smile',
-  //       component: './device/Instance/Detail',
-  //     },
-  //     {
-  //       path: '/device/command',
-  //       name: 'command',
-  //       icon: 'smile',
-  //       component: './device/Command',
-  //     },
-  //     {
-  //       path: '/device/firmware',
-  //       name: 'firmware',
-  //       icon: 'smile',
-  //       component: './device/Firmware',
-  //     },
-  //     {
-  //       hideInMenu: true,
-  //       path: '/device/firmware/detail/:id',
-  //       name: 'firmware-detail',
-  //       icon: 'smile',
-  //       component: './device/Firmware/Detail',
-  //     },
-  //     {
-  //       path: '/device/alarm',
-  //       name: 'alarm',
-  //       icon: 'smile',
-  //       component: './device/Alarm',
-  //     },
-  //     {
-  //       path: '/device/location',
-  //       name: 'location',
-  //       icon: 'smile',
-  //       component: './device/Location',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/link',
-  //   name: 'link',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/link',
-  //       redirect: '/link/certificate',
-  //     },
-  //     {
-  //       path: '/link/certificate',
-  //       name: 'certificate',
-  //       icon: 'smile',
-  //       component: './link/Certificate',
-  //     },
-  //     {
-  //       path: '/link/protocol',
-  //       name: 'protocol',
-  //       icon: 'smile',
-  //       component: './link/Protocol',
-  //     },
-  //     {
-  //       path: 'link/type',
-  //       name: 'type',
-  //       icon: 'smile',
-  //       component: './link/Type',
-  //     },
-  //     {
-  //       path: '/link/gateway',
-  //       name: 'gateway',
-  //       icon: 'smile',
-  //       component: './link/Gateway',
-  //     },
-  //     {
-  //       path: '/link/opcua',
-  //       name: 'opcua',
-  //       icon: 'smile',
-  //       component: './link/Opcua',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/notice',
-  //   name: 'notice',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/notice',
-  //       redirect: '/notice/config',
-  //     },
-  //     {
-  //       path: '/notice/config',
-  //       name: 'config',
-  //       icon: 'smile',
-  //       component: './notice/Config',
-  //     },
-  //     {
-  //       path: '/notice/template',
-  //       name: 'template',
-  //       icon: 'smile',
-  //       component: './notice/Template',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/rule-engine',
-  //   name: 'rule-engine',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/rule-engine',
-  //       redirect: '/rule-engine/instance',
-  //     },
-  //     {
-  //       path: '/rule-engine/instance',
-  //       name: 'instance',
-  //       icon: 'smile',
-  //       component: './rule-engine/Instance',
-  //     },
-  //     {
-  //       path: '/rule-engine/sqlRule',
-  //       name: 'sqlRule',
-  //       icon: 'smile',
-  //       component: './rule-engine/SQLRule',
-  //     },
-  //     {
-  //       path: '/rule-engine/scene',
-  //       name: 'scene',
-  //       icon: 'smile',
-  //       component: './rule-engine/Scene',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/visualization',
-  //   name: 'visualization',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/visualization',
-  //       redirect: '/visualization/category',
-  //     },
-  //     {
-  //       path: '/visualization/category',
-  //       name: 'category',
-  //       icon: 'smile',
-  //       component: './visualization/Category',
-  //     },
-  //     {
-  //       path: '/visualization/screen',
-  //       name: 'screen',
-  //       icon: 'smile',
-  //       component: './visualization/Screen',
-  //     },
-  //     {
-  //       path: '/visualization/configuration',
-  //       name: 'configuration',
-  //       icon: 'smile',
-  //       component: './visualization/Configuration',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/simulator',
-  //   name: 'simulator',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/simulator',
-  //       redirect: '/simulator/device',
-  //     },
-  //     {
-  //       path: '/simulator/device',
-  //       name: 'device',
-  //       icon: 'smile',
-  //       component: './simulator/Device',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/log',
-  //   name: 'log',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/log',
-  //       redirect: '/log/access',
-  //     },
-  //     {
-  //       path: '/log/access',
-  //       name: 'access',
-  //       icon: 'smile',
-  //       component: './log/Access',
-  //     },
-  //     {
-  //       path: '/log/system',
-  //       name: 'system',
-  //       icon: 'smile',
-  //       component: './log/System',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/cloud',
-  //   name: 'cloud',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/cloud',
-  //       redirect: '/cloud/duer',
-  //     },
-  //     {
-  //       path: '/cloud/dueros',
-  //       name: 'DuerOS',
-  //       icon: 'smile',
-  //       component: './cloud/DuerOS',
-  //     },
-  //     {
-  //       path: '/cloud/aliyun',
-  //       name: 'aliyun',
-  //       icon: 'smile',
-  //       component: './cloud/Aliyun',
-  //     },
-  //     {
-  //       path: '/cloud/onenet',
-  //       name: 'onenet',
-  //       icon: 'smile',
-  //       component: './cloud/Onenet',
-  //     },
-  //     {
-  //       path: '/cloud/ctwing',
-  //       name: 'ctwing',
-  //       icon: 'smile',
-  //       component: './cloud/Ctwing',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/media',
-  //   name: 'media',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/media',
-  //       redirect: '/media/config',
-  //     },
-  //     {
-  //       path: '/media/config',
-  //       name: 'config',
-  //       icon: 'smile',
-  //       component: './media/Config',
-  //     },
-  //     {
-  //       path: '/media/device',
-  //       name: 'device',
-  //       icon: 'smile',
-  //       component: './media/Device',
-  //     },
-  //     {
-  //       path: '/media/reveal',
-  //       name: 'reveal',
-  //       icon: 'smile',
-  //       component: './media/Reveal',
-  //     },
-  //     {
-  //       path: '/media/cascade',
-  //       name: 'cascade',
-  //       icon: 'smile',
-  //       component: './media/Cascade',
-  //     },
-  //   ],
-  // },
-  // {
-  //   path: '/edge',
-  //   name: 'edge',
-  //   icon: 'crown',
-  //   routes: [
-  //     {
-  //       path: '/edge',
-  //       redirect: '/edge/product',
-  //     },
-  //     {
-  //       path: '/edge/product',
-  //       name: 'product',
-  //       icon: 'smile',
-  //       component: './edge/Product',
-  //     },
-  //     {
-  //       path: '/edge/device',
-  //       name: 'device',
-  //       icon: 'smile',
-  //       component: './edge/Device',
-  //     },
-  //   ],
-  // },
   {
     path: '/',
-    redirect: '/system',
+    redirect: '/analysis',
   },
   {
     component: './404',

+ 1 - 0
src/locales/en-US/menu.ts

@@ -7,6 +7,7 @@ export default {
   'menu.system': 'System',
   'menu.system.user': 'User',
   'menu.system.role': 'Role',
+  'menu.system.role-edit': 'Access Configuration',
   'menu.system.org': 'Organization',
   'menu.system.open-api': 'OpenAPI',
   'menu.system.permission': 'Permission',

+ 3 - 0
src/locales/en-US/pages.ts

@@ -86,6 +86,9 @@ export default {
   'pages.system.role': 'Role',
   'pages.system.role.id': 'ID',
   'pages.system.role.option.bindUser': 'Bind User',
+  'pages.system.role.access.baseInfo': 'Basic Information',
+  'pages.system.role.access.permission': 'Access Configuration',
+  'pages.system.role.access.userManagement': 'User Management',
   // 系统设置-角色管理-绑定用户
   'pages.bindUser.theBoundUser': 'The Bound User',
   'pages.bindUser.theBoundUser.success': 'Unbundling success',

+ 2 - 1
src/locales/zh-CN/menu.ts

@@ -5,7 +5,8 @@ export default {
   'menu.admin': '管理页',
   'menu.system': '系统设置',
   'menu.system.user': '用户管理',
-  'menu.system.role': '角色管理',
+  'menu.system.role': '角色权限',
+  'menu.system.role-edit': '权限配置',
   'menu.system.org': '机构管理',
   'menu.system.open-api': '第三方平台',
   'menu.system.permission': '权限管理',

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

@@ -87,6 +87,9 @@ export default {
   'pages.system.role': '角色管理',
   'pages.system.role.id': '标识',
   'pages.system.role.option.bindUser': '绑定用户',
+  'pages.system.role.access.baseInfo': '基本信息',
+  'pages.system.role.access.permission': '权限分配',
+  'pages.system.role.access.userManagement': '用户管理',
   // 系统设置-角色管理-绑定用户
   'pages.bindUser.theBoundUser': '已绑定用户',
   'pages.bindUser.theBoundUser.success': '解绑成功',

+ 253 - 441
src/pages/system/Permission/index.tsx

@@ -1,7 +1,12 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import React, { useEffect, useRef } from 'react';
-import { EditOutlined, CloseCircleOutlined, PlayCircleOutlined } from '@ant-design/icons';
-import { Menu, Tooltip, Popconfirm, message } from 'antd';
+import {
+  EditOutlined,
+  CloseCircleOutlined,
+  PlayCircleOutlined,
+  MinusOutlined,
+} from '@ant-design/icons';
+import { Menu, Tooltip, Popconfirm, message, Button, Upload } from 'antd';
 import type { ProColumns, ActionType } from '@jetlinks/pro-table';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import BaseCrud from '@/components/BaseCrud';
@@ -12,47 +17,108 @@ import type { ISchema } from '@formily/json-schema';
 import Service from '@/pages/system/Permission/service';
 import { model } from '@formily/reactive';
 import { observer } from '@formily/react';
-import { map } from 'rxjs/operators';
-import { toArray } from 'rxjs';
-
-const menu = (
-  <Menu>
-    <Menu.Item key="1">1st item</Menu.Item>
-    <Menu.Item key="2">2nd item</Menu.Item>
-    <Menu.Item key="3">3rd item</Menu.Item>
-  </Menu>
-);
+import moment from 'moment';
+import SystemConst from '@/utils/const';
+import Token from '@/utils/token';
 
 export const service = new Service('permission');
 
 const defaultAction = [
-  { value: 'query', label: '查询' },
-  { value: 'save', label: '保存' },
-  { value: 'delete', label: '删除' },
-  { value: 'import', label: '导入' },
-  { value: 'export', label: '导出' },
+  { action: 'query', name: '查询', describe: '查询' },
+  { action: 'save', name: '保存', describe: '保存' },
+  { action: 'delete', name: '删除', describe: '删除' },
 ];
 
+const downloadObject = (record: any, fileName: string) => {
+  // 创建隐藏的可下载链接
+  const eleLink = document.createElement('a');
+  eleLink.download = `${fileName}-${
+    record.name || moment(new Date()).format('YYYY/MM/DD HH:mm:ss')
+  }.json`;
+  eleLink.style.display = 'none';
+  // 字符内容转变成blob地址
+  const blob = new Blob([JSON.stringify(record)]);
+  eleLink.href = URL.createObjectURL(blob);
+  // 触发点击
+  document.body.appendChild(eleLink);
+  eleLink.click();
+  // 然后移除
+  document.body.removeChild(eleLink);
+};
+
 const PermissionModel = model<{
-  permissionList: { label: string; value: string }[];
+  assetsTypesList: { label: string; value: string }[];
 }>({
-  permissionList: [],
+  assetsTypesList: [],
 });
 const Permission: React.FC = observer(() => {
   useEffect(() => {
-    service
-      .getPermission()
-      .pipe(
-        map((item) => ({ label: item.name, value: item.id })),
-        toArray(),
-      )
-      .subscribe((data) => {
-        PermissionModel.permissionList = data;
-      });
+    service.getAssetTypes().subscribe((resp) => {
+      if (resp.status === 200) {
+        const list = resp.result.map((item: { name: string; id: string }) => {
+          return {
+            label: item.name,
+            value: item.id,
+          };
+        });
+        PermissionModel.assetsTypesList = list;
+      }
+    });
   }, []);
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
 
+  const menu = (
+    <Menu>
+      <Menu.Item key="import">
+        <Upload
+          action={`/${SystemConst.API_BASE}/file/static`}
+          headers={{
+            'X-Access-Token': Token.get(),
+          }}
+          showUploadList={false}
+          accept=".json"
+          beforeUpload={(file) => {
+            const reader = new FileReader();
+            reader.readAsText(file);
+            reader.onload = (result: any) => {
+              try {
+                const data = JSON.parse(result.target.result);
+                service.batchAdd(data).subscribe((resp) => {
+                  if (resp.status === 200) {
+                    message.success('导入成功');
+                    actionRef.current?.reload();
+                  }
+                });
+              } catch (error) {
+                message.error('导入失败,请重试!');
+              }
+            };
+          }}
+        >
+          <Button>导入</Button>
+        </Upload>
+      </Menu.Item>
+      <Menu.Item key="export">
+        <Popconfirm
+          title={'确认导出?'}
+          onConfirm={() => {
+            service.getPermission().subscribe((resp) => {
+              if (resp.status === 200) {
+                downloadObject(resp.result, '权限数据');
+                message.success('导出成功');
+              } else {
+                message.error('导出错误');
+              }
+            });
+          }}
+        >
+          <Button>导出</Button>
+        </Popconfirm>
+      </Menu.Item>
+    </Menu>
+  );
+
   const columns: ProColumns<PermissionItem>[] = [
     {
       dataIndex: 'index',
@@ -173,6 +239,7 @@ const Permission: React.FC = observer(() => {
             })}
             onConfirm={async () => {
               await service.update({
+                ...record,
                 id: record.id,
                 status: record.status ? 0 : 1,
               });
@@ -195,6 +262,33 @@ const Permission: React.FC = observer(() => {
             </Tooltip>
           </Popconfirm>
         </a>,
+        <a key="delete">
+          <Popconfirm
+            title={intl.formatMessage({
+              id: 'pages.data.option.remove.tips',
+              defaultMessage: '确认删除?',
+            })}
+            onConfirm={async () => {
+              await service.remove(record.id);
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            }}
+          >
+            <Tooltip
+              title={intl.formatMessage({
+                id: 'pages.data.option.remove',
+                defaultMessage: '删除',
+              })}
+            >
+              <MinusOutlined />
+            </Tooltip>
+          </Popconfirm>
+        </a>,
       ],
     },
   ];
@@ -204,453 +298,170 @@ const Permission: React.FC = observer(() => {
   const schema: ISchema = {
     type: 'object',
     properties: {
-      collapse: {
-        type: 'void',
-        'x-component': 'FormTab',
+      id: {
+        title: intl.formatMessage({
+          id: 'pages.system.permission.id',
+          defaultMessage: '标识(ID)',
+        }),
+        type: 'string',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        name: 'id',
+        required: true,
+      },
+      name: {
+        title: intl.formatMessage({
+          id: 'pages.table.name',
+          defaultMessage: '名称',
+        }),
+        type: 'string',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        name: 'name',
+        required: true,
+      },
+      status: {
+        title: '状态',
+        'x-decorator': 'FormItem',
+        'x-component': 'Switch',
+        required: true,
+        default: 1,
+        enum: [
+          { label: '1', value: 1 },
+          { label: '0', value: 0 },
+        ],
+      },
+      'properties.assetTypes': {
+        type: 'string',
+        title: '关联资产',
+        'x-decorator': 'FormItem',
+        'x-component': 'Select',
+        name: 'properties.assetTypes',
+        required: false,
+        enum: PermissionModel.assetsTypesList,
         'x-component-props': {
-          formTab: '{{formTab}}',
+          showSearch: true,
+          mode: 'multiple',
         },
-        properties: {
-          baseTab: {
-            type: 'void',
-            'x-component': 'FormTab.TabPane',
-            'x-component-props': {
-              tab: intl.formatMessage({
-                id: 'pages.system.permission.addInformation',
-                defaultMessage: '基础信息',
-              }),
+      },
+      actions: {
+        type: 'array',
+        'x-decorator': 'FormItem',
+        'x-component': 'ArrayTable',
+        default: defaultAction,
+        title: '操作类型',
+        items: {
+          type: 'object',
+          properties: {
+            column1: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': { width: 80, title: '-', align: 'center' },
+              properties: {
+                index: {
+                  type: 'void',
+                  'x-component': 'ArrayTable.Index',
+                },
+              },
             },
-            properties: {
-              id: {
+            column2: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': {
+                width: 200,
                 title: intl.formatMessage({
-                  id: 'pages.system.permission.id',
-                  defaultMessage: '标识(ID)',
+                  id: 'pages.system.permission.addConfigurationType',
+                  defaultMessage: '操作类型',
                 }),
-                type: 'string',
-                'x-decorator': 'FormItem',
-                'x-component': 'Input',
-                name: 'id',
-                required: true,
               },
-              name: {
+              properties: {
+                action: {
+                  type: 'string',
+                  'x-decorator': 'Editable',
+                  'x-component': 'Input',
+                },
+              },
+            },
+            column3: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': {
+                width: 200,
                 title: intl.formatMessage({
                   id: 'pages.table.name',
                   defaultMessage: '名称',
                 }),
-                type: 'string',
-                'x-decorator': 'FormItem',
-                'x-component': 'Input',
-                name: 'name',
-                required: true,
               },
-              'properties.type': {
-                type: 'string',
+              properties: {
+                name: {
+                  type: 'string',
+                  'x-decorator': 'Editable',
+                  'x-component': 'Input',
+                },
+              },
+            },
+            column4: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': {
+                width: 200,
                 title: intl.formatMessage({
-                  id: 'pages.searchTable.titleStatus',
-                  defaultMessage: '分类',
+                  id: 'pages.table.describe',
+                  defaultMessage: '描述',
                 }),
-                'x-decorator': 'FormItem',
-                'x-component': 'Select',
-                name: 'properties.type',
-                required: false,
               },
-            },
-          },
-          actionsTab: {
-            type: 'void',
-            'x-component': 'FormTab.TabPane',
-            'x-component-props': {
-              tab: intl.formatMessage({
-                id: 'pages.system.permission.addConfiguration',
-                defaultMessage: '操作配置',
-              }),
-            },
-            properties: {
-              actions: {
-                type: 'array',
-                'x-decorator': 'FormItem',
-                'x-component': 'ArrayTable',
-                items: {
-                  type: 'object',
-                  properties: {
-                    column1: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': { width: 80, title: '-', align: 'center' },
-                      properties: {
-                        index: {
-                          type: 'void',
-                          'x-component': 'ArrayTable.Index',
-                        },
-                      },
-                    },
-                    column2: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.system.permission.addConfigurationType',
-                          defaultMessage: '操作类型',
-                        }),
-                      },
-                      properties: {
-                        action: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Input',
-                        },
-                      },
-                    },
-                    column3: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.table.name',
-                          defaultMessage: '名称',
-                        }),
-                      },
-                      properties: {
-                        name: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Input',
-                        },
-                      },
-                    },
-                    column4: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.table.describe',
-                          defaultMessage: '描述',
-                        }),
-                      },
-                      properties: {
-                        describe: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Input',
-                        },
-                      },
-                    },
-                    column5: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        title: intl.formatMessage({
-                          id: 'pages.data.option',
-                          defaultMessage: '操作',
-                        }),
-                        dataIndex: 'operations',
-                        width: 200,
-                        fixed: 'right',
-                      },
-                      properties: {
-                        item: {
-                          type: 'void',
-                          'x-component': 'FormItem',
-                          properties: {
-                            remove: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.Remove',
-                            },
-                            moveDown: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveDown',
-                            },
-                            moveUp: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveUp',
-                            },
-                          },
-                        },
-                      },
-                    },
-                  },
-                },
-                properties: {
-                  add: {
-                    type: 'void',
-                    'x-component': 'ArrayTable.Addition',
-                    title: intl.formatMessage({
-                      id: 'pages.system.permission.add',
-                      defaultMessage: '添加条目',
-                    }),
-                  },
+              properties: {
+                describe: {
+                  type: 'string',
+                  'x-decorator': 'Editable',
+                  'x-component': 'Input',
                 },
               },
             },
-          },
-          relationTab: {
-            type: 'void',
-            'x-component': 'FormTab.TabPane',
-            'x-component-props': {
-              tab: intl.formatMessage({
-                id: 'pages.system.permission.addPermissionOperation',
-                defaultMessage: '关联权限',
-              }),
-            },
-            properties: {
-              parents: {
-                type: 'array',
-                'x-decorator': 'FormItem',
-                'x-component': 'ArrayTable',
-                'x-component-props': {},
-                items: {
-                  type: 'object',
+            column5: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': {
+                title: intl.formatMessage({
+                  id: 'pages.data.option',
+                  defaultMessage: '操作',
+                }),
+                dataIndex: 'operations',
+                width: 200,
+                fixed: 'right',
+              },
+              properties: {
+                item: {
+                  type: 'void',
+                  'x-component': 'FormItem',
                   properties: {
-                    column1: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': { width: 80, title: '-', align: 'center' },
-                      properties: {
-                        index: {
-                          type: 'void',
-                          'x-component': 'ArrayTable.Index',
-                        },
-                      },
-                    },
-                    column2: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.system.permission.addPermissionPreOperation',
-                          defaultMessage: '前置操作',
-                        }),
-                      },
-                      properties: {
-                        preActions: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Select',
-                          enum: defaultAction,
-                          'x-component-props': {
-                            mode: 'multiple',
-                            style: { minWidth: 100 },
-                          },
-                        },
-                      },
-                    },
-                    column3: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.system.permission.addPermission',
-                          defaultMessage: '关联权限',
-                        }),
-                      },
-                      properties: {
-                        permission: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Select',
-                          enum: PermissionModel.permissionList,
-                          'x-component-props': {
-                            style: { minWidth: 200, maxWidth: 300 },
-                          },
-                        },
-                      },
-                    },
-                    column4: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.system.permission.addPermissionOperation',
-                          defaultMessage: '关联操作',
-                        }),
-                      },
-                      properties: {
-                        actions: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Select',
-                          'x-component-props': {
-                            mode: 'multiple',
-                            style: { minWidth: 100 },
-                          },
-                          enum: defaultAction,
-                        },
-                      },
-                    },
-                    column5: {
+                    remove: {
                       type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        title: intl.formatMessage({
-                          id: 'pages.data.option',
-                          defaultMessage: '操作',
-                        }),
-                        dataIndex: 'operations',
-                        width: 200,
-                        fixed: 'right',
-                      },
-                      properties: {
-                        item: {
-                          type: 'void',
-                          'x-component': 'FormItem',
-                          properties: {
-                            remove: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.Remove',
-                            },
-                            moveDown: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveDown',
-                            },
-                            moveUp: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveUp',
-                            },
-                          },
-                        },
-                      },
+                      'x-component': 'ArrayTable.Remove',
                     },
                   },
                 },
-                properties: {
-                  add: {
-                    type: 'void',
-                    'x-component': 'ArrayTable.Addition',
-                    title: intl.formatMessage({
-                      id: 'pages.system.permission.add',
-                      defaultMessage: '添加条目',
-                    }),
-                  },
-                },
               },
             },
           },
-          optionalFieldsTab: {
+        },
+        properties: {
+          add: {
             type: 'void',
-            'x-component': 'FormTab.TabPane',
-            'x-component-props': {
-              tab: intl.formatMessage({
-                id: 'pages.system.permission.addDataView',
-                defaultMessage: '数据视图',
-              }),
-            },
-            properties: {
-              optionalFields: {
-                type: 'array',
-                'x-decorator': 'FormItem',
-                'x-component': 'ArrayTable',
-                items: {
-                  type: 'object',
-                  properties: {
-                    column1: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': { width: 80, title: '-', align: 'center' },
-                      properties: {
-                        index: {
-                          type: 'void',
-                          'x-component': 'ArrayTable.Index',
-                        },
-                      },
-                    },
-                    column2: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.system.permission.addDataViewField',
-                          defaultMessage: '字段',
-                        }),
-                      },
-                      properties: {
-                        name: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Input',
-                        },
-                      },
-                    },
-                    column3: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        width: 200,
-                        title: intl.formatMessage({
-                          id: 'pages.table.describe',
-                          defaultMessage: '描述',
-                        }),
-                      },
-                      properties: {
-                        describe: {
-                          type: 'string',
-                          'x-decorator': 'Editable',
-                          'x-component': 'Input',
-                        },
-                      },
-                    },
-                    column4: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Column',
-                      'x-component-props': {
-                        title: intl.formatMessage({
-                          id: 'pages.data.option',
-                          defaultMessage: '操作',
-                        }),
-                        dataIndex: 'operations',
-                        width: 200,
-                        fixed: 'right',
-                      },
-                      properties: {
-                        item: {
-                          type: 'void',
-                          'x-component': 'FormItem',
-                          properties: {
-                            remove: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.Remove',
-                            },
-                            moveDown: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveDown',
-                            },
-                            moveUp: {
-                              type: 'void',
-                              'x-component': 'ArrayTable.MoveUp',
-                            },
-                          },
-                        },
-                      },
-                    },
-                  },
-                },
-                properties: {
-                  add: {
-                    type: 'void',
-                    'x-component': 'ArrayTable.Addition',
-                    title: intl.formatMessage({
-                      id: 'pages.system.permission.add',
-                      defaultMessage: '添加条目',
-                    }),
-                  },
-                },
-              },
-            },
+            'x-component': 'ArrayTable.Addition',
+            title: intl.formatMessage({
+              id: 'pages.system.permission.add',
+              defaultMessage: '添加条目',
+            }),
           },
         },
       },
     },
   };
-
   return (
     <PageContainer>
       <BaseCrud<PermissionItem>
+        moduleName="permission"
         actionRef={actionRef}
         columns={columns}
         service={service}
@@ -664,6 +475,7 @@ const Permission: React.FC = observer(() => {
         modelConfig={{
           width: 1000,
         }}
+        search={false}
         menu={menu}
         schema={schema}
       />

+ 19 - 5
src/pages/system/Permission/service.ts

@@ -3,7 +3,7 @@ import type { PermissionItem } from '@/pages/system/Permission/typings';
 import { defer, from } from 'rxjs';
 import { request } from '@@/plugin-request/request';
 import SystemConst from '@/utils/const';
-import { filter, mergeMap } from 'rxjs/operators';
+import { map } from 'rxjs/operators';
 
 class Service extends BaseService<PermissionItem> {
   public getPermission = () =>
@@ -13,10 +13,24 @@ class Service extends BaseService<PermissionItem> {
           method: 'GET',
         }),
       ),
-    ).pipe(
-      filter((item) => item.status === 200),
-      mergeMap((item) => item.result as PermissionItem[]),
-    );
+    ).pipe(map((item) => item));
+  public getAssetTypes = () =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/asset/types`, {
+          method: 'GET',
+        }),
+      ),
+    ).pipe(map((item) => item));
+  public batchAdd = (data: any) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/dimension/_batch`, {
+          method: 'POST',
+          data,
+        }),
+      ),
+    ).pipe(map((item) => item));
 }
 
 export default Service;

+ 107 - 0
src/pages/system/Role/Edit/Info/index.tsx

@@ -0,0 +1,107 @@
+import { Form, FormButtonGroup, FormItem, Input, Submit } from '@formily/antd';
+import { createSchemaField } from '@formily/react';
+import { Card, message, Spin } from 'antd';
+import { createForm } from '@formily/core';
+import { useEffect, useState } from 'react';
+import { service } from '@/pages/system/Role';
+import { useParams, history } from 'umi';
+
+const Info = () => {
+  const [loading, setLoading] = useState<boolean>(true);
+  const [type, setType] = useState<'edit' | 'disabled'>('disabled');
+  const [data, setData] = useState<RoleItem>();
+  const params = useParams<{ id: string }>();
+  const getDetail = async (id: string) => {
+    const res = await service.detail(id);
+    if (res.status === 200) {
+      setData(res.result);
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    const { id } = params;
+    if (id) {
+      getDetail(id);
+    } else {
+      history.goBack();
+    }
+  }, [params, params.id]);
+
+  const SchemaField = createSchemaField({
+    components: {
+      Input,
+      FormItem,
+    },
+  });
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: {
+      id: data?.id,
+      name: data?.name,
+      description: data?.description,
+    },
+  });
+
+  const schema = {
+    type: 'object',
+    properties: {
+      name: {
+        type: 'string',
+        title: '名称',
+        required: true,
+        'x-disabled': type === 'disabled',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+      },
+      description: {
+        type: 'string',
+        title: '角色描述',
+        required: false,
+        'x-disabled': type === 'disabled',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input.TextArea',
+      },
+    },
+  };
+
+  const save = async () => {
+    const values: RoleItem = await form.submit();
+    const resp = await service.modify(values.id, values);
+    if (resp.status === 200) {
+      message.success('操作成功!');
+      getDetail(values.id);
+      setType('disabled');
+    }
+  };
+
+  return (
+    <Card>
+      <div style={{ width: '500px' }}>
+        <Spin spinning={loading}>
+          <Form form={form} labelCol={5} wrapperCol={16}>
+            <SchemaField schema={schema} />
+            <FormButtonGroup.FormItem>
+              <Submit
+                block
+                size="large"
+                onClick={() => {
+                  if (type === 'edit') {
+                    save();
+                  } else {
+                    setType('edit');
+                  }
+                }}
+              >
+                {type === 'disabled' ? '编辑' : '保存'}
+              </Submit>
+            </FormButtonGroup.FormItem>
+          </Form>
+        </Spin>
+      </div>
+    </Card>
+  );
+};
+
+export default Info;

+ 114 - 0
src/pages/system/Role/Edit/UserManage/BindUser.tsx

@@ -0,0 +1,114 @@
+import type { ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { message, Modal } from 'antd';
+import { useRef, useState } from 'react';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { service } from '@/pages/system/User/index';
+import encodeQuery from '@/utils/encodeQuery';
+import Service from '@/pages/system/Role/service';
+interface Props {
+  visible: boolean;
+  data: any;
+  cancel: () => void;
+}
+
+const BindUser = (props: Props) => {
+  const roleService = new Service('role');
+  const intl = useIntl();
+  const actionRef = useRef<any>();
+  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
+  const columns: ProColumns<RoleItem>[] = [
+    {
+      dataIndex: 'index',
+      valueType: 'indexBorder',
+      width: 48,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.table.name',
+        defaultMessage: '名称',
+      }),
+      dataIndex: 'name',
+      copyable: true,
+      ellipsis: true,
+      tip: intl.formatMessage({
+        id: 'pages.system.userName.tips',
+        defaultMessage: '用户名过长会自动收缩',
+      }),
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            message: '此项为必填项',
+          },
+        ],
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.system.username',
+        defaultMessage: '用户名',
+      }),
+      dataIndex: 'username',
+      filters: true,
+      onFilter: true,
+    },
+  ];
+
+  return (
+    <Modal
+      title="添加"
+      width={900}
+      visible={props.visible}
+      onCancel={() => {
+        props.cancel();
+        setSelectedRowKeys([]);
+      }}
+      onOk={() => {
+        roleService.bindUser(props.data.id, selectedRowKeys).subscribe((resp) => {
+          if (resp.status === 200) {
+            message.success('操作成功!');
+            actionRef.current?.reload();
+          }
+        });
+        setSelectedRowKeys([]);
+        props.cancel();
+      }}
+    >
+      <ProTable
+        actionRef={actionRef}
+        rowSelection={{
+          selectedRowKeys: selectedRowKeys,
+          onChange: (key) => {
+            setSelectedRowKeys(key as string[]);
+          },
+        }}
+        pagination={{
+          pageSize: 10,
+        }}
+        request={async (param: any) => {
+          const response = await service.query(
+            encodeQuery({
+              pageSize: param.pageSize,
+              pageIndex: param.current,
+              terms: {
+                'id$in-dimension$role$not': props.data.id,
+              },
+            }),
+          );
+          return {
+            result: { data: response.result.data },
+            success: true,
+            status: 200,
+            total: response.result.total,
+          } as any;
+        }}
+        columns={columns}
+        rowKey="id"
+        toolBarRender={false}
+      />
+    </Modal>
+  );
+};
+
+export default BindUser;

+ 158 - 0
src/pages/system/Role/Edit/UserManage/index.tsx

@@ -0,0 +1,158 @@
+import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
+import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import { Button, Card, message, Popconfirm, Space, Tooltip } from 'antd';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { useRef, useState } from 'react';
+import ProTable from '@jetlinks/pro-table';
+import BindUser from './BindUser';
+import { service } from '@/pages/system/User/index';
+import encodeQuery from '@/utils/encodeQuery';
+import { useParams } from 'umi';
+import Service from '@/pages/system/Role/service';
+
+const UserManage = () => {
+  const roleService = new Service('role');
+  const params = useParams<{ id: string }>();
+  const intl = useIntl();
+  const actionRef = useRef<ActionType>();
+  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
+  const [bindUserVisible, setBindUserVisible] = useState<boolean>(false);
+  const unBindUser = (id: string, ids: string[]) => {
+    roleService.unbindUser(id, ids).subscribe((resp) => {
+      if (resp.status === 200) {
+        message.success('操作成功!');
+        actionRef.current?.reload();
+      }
+    });
+  };
+  const columns: ProColumns<RoleItem>[] = [
+    {
+      dataIndex: 'index',
+      valueType: 'indexBorder',
+      width: 48,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.table.name',
+        defaultMessage: '名称',
+      }),
+      dataIndex: 'name',
+      copyable: true,
+      ellipsis: true,
+      tip: intl.formatMessage({
+        id: 'pages.system.userName.tips',
+        defaultMessage: '用户名过长会自动收缩',
+      }),
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            message: '此项为必填项',
+          },
+        ],
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.system.username',
+        defaultMessage: '用户名',
+      }),
+      dataIndex: 'username',
+      filters: true,
+      onFilter: true,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.data.option',
+        defaultMessage: '操作',
+      }),
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+      render: (text, record) => [
+        <a key="delete">
+          <Popconfirm
+            title={'确认解绑'}
+            onConfirm={() => {
+              unBindUser(params.id, [record.id]);
+            }}
+          >
+            <Tooltip title={'解绑'}>
+              <MinusOutlined />
+            </Tooltip>
+          </Popconfirm>
+        </a>,
+      ],
+    },
+  ];
+  return (
+    <Card>
+      <ProTable
+        actionRef={actionRef}
+        tableAlertOptionRender={() => (
+          <Space size={16}>
+            <a
+              onClick={() => {
+                setSelectedRowKeys([]);
+                unBindUser(params.id, [...selectedRowKeys]);
+              }}
+            >
+              批量解绑
+            </a>
+          </Space>
+        )}
+        toolBarRender={() => [
+          <Button
+            onClick={() => {
+              setBindUserVisible(true);
+            }}
+            key="button"
+            icon={<PlusOutlined />}
+            type="primary"
+          >
+            添加
+          </Button>,
+        ]}
+        rowSelection={{
+          selectedRowKeys: selectedRowKeys,
+          onChange: (key) => {
+            setSelectedRowKeys(key as string[]);
+          },
+        }}
+        pagination={{
+          pageSize: 10,
+        }}
+        request={async (param: any) => {
+          const response = await service.query(
+            encodeQuery({
+              pageSize: param.pageSize,
+              pageIndex: param.current,
+              terms: {
+                'id$in-dimension$role': params.id,
+              },
+            }),
+          );
+          return {
+            result: { data: response.result.data },
+            success: true,
+            status: 200,
+            total: response.result.total,
+          } as any;
+        }}
+        columns={columns}
+        rowKey="id"
+      />
+      <BindUser
+        visible={bindUserVisible}
+        data={{
+          id: params.id || '',
+        }}
+        cancel={() => {
+          setBindUserVisible(false);
+          actionRef.current?.reload();
+        }}
+      />
+    </Card>
+  );
+};
+export default UserManage;

+ 47 - 0
src/pages/system/Role/Edit/index.tsx

@@ -0,0 +1,47 @@
+import { observer } from '@formily/react';
+import { PageContainer } from '@ant-design/pro-layout';
+import { useState } from 'react';
+import { history } from 'umi';
+import UserManage from '@/pages/system/Role/Edit/UserManage';
+// import Permission from '@/pages/system/Role/Edit/Permission';
+import Info from '@/pages/system/Role/Edit/Info';
+import { useIntl } from '@@/plugin-locale/localeExports';
+
+const RoleEdit = observer(() => {
+  const intl = useIntl();
+  const [tab, setTab] = useState<string>('baseInfo');
+
+  const list = [
+    {
+      key: 'baseInfo',
+      tab: intl.formatMessage({
+        id: 'pages.system.role.access.baseInfo',
+        defaultMessage: '基本信息',
+      }),
+      component: <Info />,
+    },
+    // {
+    //     key: 'permission',
+    //     tab: intl.formatMessage({
+    //         id: 'pages.system.role.access.permission',
+    //         defaultMessage: '权限分配',
+    //     }),
+    //     component: <Permission />,
+    // },
+    {
+      key: 'userManagement',
+      tab: intl.formatMessage({
+        id: 'pages.system.role.access.userManagement',
+        defaultMessage: '用户管理',
+      }),
+      component: <UserManage />,
+    },
+  ];
+
+  return (
+    <PageContainer onBack={history.goBack} tabList={list} onTabChange={setTab}>
+      {list.find((k) => k.key === tab)?.component}
+    </PageContainer>
+  );
+});
+export default RoleEdit;

+ 21 - 90
src/pages/system/Role/index.tsx

@@ -1,20 +1,19 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import React, { useEffect, useRef } from 'react';
-import { EditOutlined, KeyOutlined, MinusOutlined, UserAddOutlined } from '@ant-design/icons';
-import { Drawer, message, Modal, Popconfirm, Tooltip } from 'antd';
+import React, { useRef } from 'react';
+import { EditOutlined, MinusOutlined } from '@ant-design/icons';
+import { message, Popconfirm, Tooltip } from 'antd';
 import type { ProColumns, ActionType } from '@jetlinks/pro-table';
 import BaseCrud from '@/components/BaseCrud';
-import { CurdModel } from '@/components/BaseCrud/model';
 import BaseService from '@/utils/BaseService';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { observer } from '@formily/react';
-import autzModel from '@/components/Authorization/autz';
-import Authorization from '@/components/Authorization';
-import { BindModel } from '@/components/BindUser/model';
-import BindUser from '@/components/BindUser';
-// import { useParams } from 'umi';
+// import autzModel from '@/components/Authorization/autz';
+// import Authorization from '@/components/Authorization';
+// import { BindModel } from '@/components/BindUser/model';
+// import BindUser from '@/components/BindUser';
+import { Link } from 'umi';
 
-const service = new BaseService<RoleItem>('dimension');
+export const service = new BaseService<RoleItem>('role');
 
 const Role: React.FC = observer(() => {
   const intl = useIntl();
@@ -71,7 +70,7 @@ const Role: React.FC = observer(() => {
         id: 'pages.table.describe',
         defaultMessage: '描述',
       }),
-      dataIndex: 'describe',
+      dataIndex: 'description',
       filters: true,
       onFilter: true,
     },
@@ -84,56 +83,17 @@ const Role: React.FC = observer(() => {
       align: 'center',
       width: 200,
       render: (text, record) => [
-        <a key="editable" onClick={() => CurdModel.update(record)}>
+        <Link to={`/system/role/edit/${record.id}`} key="link">
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.edit',
               defaultMessage: '编辑',
             })}
+            key={'edit'}
           >
             <EditOutlined />
           </Tooltip>
-        </a>,
-        <a
-          key="autz"
-          onClick={() => {
-            autzModel.autzTarget.id = record.id;
-            autzModel.autzTarget.name = record.name;
-            autzModel.autzTarget.type = 'role';
-            autzModel.visible = true;
-          }}
-        >
-          <Tooltip
-            title={intl.formatMessage({
-              id: 'pages.data.option.authorize',
-              defaultMessage: '授权',
-            })}
-          >
-            <KeyOutlined />
-          </Tooltip>
-        </a>,
-
-        <a
-          key="bind"
-          onClick={() => {
-            BindModel.dimension = {
-              id: record.id,
-              name: record.name,
-              type: 'role',
-            };
-            BindModel.visible = true;
-            actionRef.current?.reload();
-          }}
-        >
-          <Tooltip
-            title={intl.formatMessage({
-              id: 'pages.system.role.option.bindUser',
-              defaultMessage: '绑定用户',
-            })}
-          >
-            <UserAddOutlined />
-          </Tooltip>
-        </a>,
+        </Link>,
         <a key="delete">
           <Popconfirm
             title={intl.formatMessage({
@@ -168,21 +128,6 @@ const Role: React.FC = observer(() => {
   const schema = {
     type: 'object',
     properties: {
-      id: {
-        title: intl.formatMessage({
-          id: 'pages.system.role.id',
-          defaultMessage: '角色标识',
-        }),
-        type: 'string',
-        'x-decorator': 'FormItem',
-        'x-component': 'Input',
-        'x-component-props': {
-          disabled: CurdModel.model === 'edit',
-        },
-        'x-decorator-props': {},
-        name: 'id',
-        required: true,
-      },
       name: {
         title: intl.formatMessage({
           id: 'pages.table.name',
@@ -196,7 +141,7 @@ const Role: React.FC = observer(() => {
         name: 'name',
         required: true,
       },
-      describe: {
+      description: {
         type: 'string',
         title: intl.formatMessage({
           id: 'pages.table.describe',
@@ -211,38 +156,24 @@ const Role: React.FC = observer(() => {
         name: 'password',
         required: false,
       },
-      typeId: {
-        type: 'string',
-        'x-visible': false,
-        'x-decorator': 'FormItem',
-        'x-component': 'Input',
-        name: 'typeId',
-        default: 'role',
-      },
     },
   };
-
-  // const params = useParams<{ tag: boolean }>({ tag: false });
-  useEffect(() => {
-    // console.log(params.tag);
-    // if (params.tag) {
-    //   CurdModel.add();
-    // }
-  });
   return (
     <PageContainer>
       <BaseCrud<RoleItem>
         actionRef={actionRef}
+        moduleName="role"
         columns={columns}
         service={service}
+        search={false}
         title={intl.formatMessage({
           id: 'pages.system.role',
           defaultMessage: '角色管理',
         })}
         schema={schema}
-        defaultParams={{ typeId: 'role' }}
+        // defaultParams={{ typeId: 'role' }}
       />
-      <Modal
+      {/* <Modal
         visible={BindModel.visible}
         closable={false}
         onCancel={() => {
@@ -252,8 +183,8 @@ const Role: React.FC = observer(() => {
         width={BindModel.bind ? '90vw' : '60vw'}
       >
         <BindUser />
-      </Modal>
-      <Drawer
+      </Modal> */}
+      {/* <Drawer
         title={intl.formatMessage({
           id: 'pages.data.option.authorize',
           defaultMessage: '授权',
@@ -270,7 +201,7 @@ const Role: React.FC = observer(() => {
           }}
           target={autzModel.autzTarget}
         />
-      </Drawer>
+      </Drawer> */}
     </PageContainer>
   );
 });

+ 37 - 0
src/pages/system/Role/service.ts

@@ -0,0 +1,37 @@
+import BaseService from '@/utils/BaseService';
+import { request } from 'umi';
+import { defer, from } from 'rxjs';
+import { map } from 'rxjs/operators';
+import SystemConst from '@/utils/const';
+
+class Service extends BaseService<RoleItem> {
+  queryMenuList = (params: any) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/menu/user-own/list`, {
+          method: 'GET',
+          params,
+        }),
+      ),
+    ).pipe(map((item) => item));
+  bindUser = (roleId: string, params: any) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/role/${roleId}/users/_bind`, {
+          method: 'POST',
+          data: params,
+        }),
+      ),
+    ).pipe(map((item) => item));
+  unbindUser = (roleId: string, params: any) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/role/${roleId}/users/_unbind`, {
+          method: 'POST',
+          data: params,
+        }),
+      ),
+    ).pipe(map((item) => item));
+}
+
+export default Service;

+ 1 - 1
src/pages/system/Role/typings.d.ts

@@ -4,5 +4,5 @@ type RoleItem = {
   name: string;
   path: string;
   sortIndex: number;
-  typeId: string;
+  description: string;
 };