浏览代码

fix(BindUser): BindUser Component

Lind 4 年之前
父节点
当前提交
55d5564a0a

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

@@ -83,10 +83,15 @@ const Authorization = observer((props: AuthorizationProps) => {
         }, {}),
         takeLast(1),
       )
-      .subscribe((data) => {
-        form.setFieldsValue(data);
-        AuthorizationModel.checkAll = calculationSelectAll(permissionDB, data);
-        AuthorizationModel.spinning = false;
+      .subscribe({
+        next: (data) => {
+          form.setFieldsValue(data);
+          AuthorizationModel.checkAll = calculationSelectAll(permissionDB, data);
+        },
+        error: () => {},
+        complete: () => {
+          AuthorizationModel.spinning = false;
+        },
       });
   }, [target.id]);
 

+ 94 - 0
src/components/BindUser/Bound.tsx

@@ -0,0 +1,94 @@
+import { Button, Card, message, Space } from 'antd';
+import type { ActionType } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { PlusOutlined } from '@ant-design/icons';
+import { useEffect, useRef } from 'react';
+import { Store } from 'jetlinks-store';
+import SystemConst from '@/utils/const';
+import { observer } from '@formily/react';
+import { BindModel } from '@/components/BindUser/model';
+import { columns, service } from '@/components/BindUser/index';
+
+const Bound = observer(() => {
+  const actionRef = useRef<ActionType>();
+
+  useEffect(() => {
+    const listener = Store.subscribe(SystemConst.BIND_USER_STATE, () =>
+      actionRef.current?.reload(),
+    );
+    return () => listener.unsubscribe();
+  });
+
+  const handleUnBindData = () => {
+    const {
+      dimension: { id, type },
+    } = BindModel;
+    service.unBindData(BindModel.unBindUsers, type!, id!).subscribe({
+      next: async () => {
+        message.success('解绑成功');
+      },
+      error: async () => {
+        message.error('操作失败');
+      },
+      complete: () => {
+        // 通知右侧组建刷新
+        Store.set(SystemConst.BIND_USER_STATE, 'true');
+        actionRef.current?.reload();
+        BindModel.unBindUsers = [];
+      },
+    });
+  };
+
+  return (
+    <Card title="已绑定用户">
+      <ProTable
+        size="small"
+        rowKey="id"
+        rowSelection={{
+          selectedRowKeys: BindModel.unBindUsers,
+          onChange: (selectedRowKeys) => {
+            BindModel.unBindUsers = selectedRowKeys as string[];
+          },
+        }}
+        tableAlertRender={({ selectedRowKeys, onCleanSelected }) => (
+          <Space size={24}>
+            <span>
+              已选 {selectedRowKeys.length} 项
+              <a style={{ marginLeft: 8 }} onClick={onCleanSelected}>
+                取消选择
+              </a>
+            </span>
+          </Space>
+        )}
+        tableAlertOptionRender={() => (
+          <Space size={16}>
+            <a onClick={handleUnBindData}>批量解绑</a>
+          </Space>
+        )}
+        actionRef={actionRef}
+        columns={columns}
+        pagination={{
+          pageSize: 10,
+        }}
+        request={async (params) => service.query(params)}
+        defaultParams={{
+          'id$in-dimension$role': BindModel.dimension.id,
+        }}
+        toolBarRender={() => [
+          <Button
+            onClick={() => {
+              BindModel.bind = true;
+            }}
+            key="button"
+            icon={<PlusOutlined />}
+            type="primary"
+          >
+            绑定用户
+          </Button>,
+        ]}
+      />
+    </Card>
+  );
+});
+
+export default Bound;

+ 95 - 0
src/components/BindUser/Unbound.tsx

@@ -0,0 +1,95 @@
+import { CloseOutlined } from '@ant-design/icons';
+import type { ActionType } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { Card, message, Space } from 'antd';
+import { observer } from '@formily/react';
+import { Store } from 'jetlinks-store';
+import SystemConst from '@/utils/const';
+import { useEffect, useRef } from 'react';
+import { BindModel } from '@/components/BindUser/model';
+import { columns, service } from '@/components/BindUser/index';
+
+const Unbound = observer(() => {
+  const actionRef = useRef<ActionType>();
+
+  useEffect(() => {
+    const listener = Store.subscribe(SystemConst.BIND_USER_STATE, () =>
+      actionRef.current?.reload(),
+    );
+    return () => listener.unsubscribe();
+  });
+
+  const handleBindData = () => {
+    const data = BindModel.bindUsers.map(
+      (item) =>
+        ({
+          ...item,
+          dimensionId: BindModel.dimension.id,
+          dimensionTypeId: BindModel.dimension.type,
+          dimensionName: BindModel.dimension.name,
+        } as BindDataItem),
+    );
+    service.saveBindData(data).subscribe({
+      next: () => message.success('绑定成功'),
+      error: () => {},
+      complete: () => {
+        // 通知左侧组件刷新
+        Store.set(SystemConst.BIND_USER_STATE, 'true');
+        actionRef.current?.reload();
+        BindModel.bindUsers = [];
+      },
+    });
+  };
+
+  return (
+    <Card
+      title="绑定新用户"
+      extra={
+        <CloseOutlined
+          onClick={() => {
+            BindModel.bind = false;
+          }}
+        />
+      }
+    >
+      <ProTable
+        actionRef={actionRef}
+        rowKey="id"
+        rowSelection={{
+          selectedRowKeys: BindModel.bindUsers.map((item) => item.userId),
+          onChange: (selectedRowKeys, selectedRows) => {
+            BindModel.bindUsers = selectedRows.map((item) => ({
+              userId: item.id,
+              userName: item.name,
+            }));
+          },
+        }}
+        tableAlertRender={({ selectedRowKeys, onCleanSelected }) => (
+          <Space size={24}>
+            <span>
+              已选 {selectedRowKeys.length} 项
+              <a style={{ marginLeft: 8 }} onClick={onCleanSelected}>
+                取消选择
+              </a>
+            </span>
+          </Space>
+        )}
+        tableAlertOptionRender={() => (
+          <Space size={16}>
+            <a onClick={handleBindData}>批量绑定</a>
+          </Space>
+        )}
+        size="small"
+        columns={columns}
+        pagination={{
+          pageSize: 7,
+        }}
+        request={async (params) => service.query(params)}
+        defaultParams={{
+          'id$in-dimension$role$not': BindModel.dimension.id,
+        }}
+      />
+    </Card>
+  );
+});
+export default Unbound;

+ 41 - 0
src/components/BindUser/index.tsx

@@ -0,0 +1,41 @@
+import { Col, Row } from 'antd';
+import type { ProColumns } from '@jetlinks/pro-table';
+import { observer } from '@formily/react';
+import { BindModel } from '@/components/BindUser/model';
+import Bound from '@/components/BindUser/Bound';
+import Unbound from '@/components/BindUser/Unbound';
+import Service from '@/components/BindUser/service';
+
+export const service = new Service('user');
+
+export const columns: ProColumns<UserItem>[] = [
+  {
+    dataIndex: 'index',
+    valueType: 'indexBorder',
+    width: 48,
+  },
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    copyable: true,
+    ellipsis: true,
+    align: 'center',
+  },
+  {
+    title: '用户名',
+    dataIndex: 'username',
+  },
+];
+const BindUser = observer(() => {
+  return (
+    <Row gutter={[4, 4]}>
+      <Col span={BindModel.bind ? 12 : 24}>
+        <Bound />
+      </Col>
+      <Col span={BindModel.bind ? 12 : 0}>
+        <Unbound />
+      </Col>
+    </Row>
+  );
+});
+export default BindUser;

+ 19 - 0
src/components/BindUser/model.ts

@@ -0,0 +1,19 @@
+import { model } from '@formily/reactive';
+
+export const BindModel = model<{
+  visible: boolean;
+  bind: boolean;
+  bindUsers: { userId: string; userName: string }[];
+  unBindUsers: string[];
+  dimension: {
+    id?: string;
+    name?: string;
+    type?: string;
+  };
+}>({
+  visible: false,
+  bind: false,
+  bindUsers: [],
+  unBindUsers: [],
+  dimension: {},
+});

+ 41 - 0
src/components/BindUser/service.ts

@@ -0,0 +1,41 @@
+import BaseService from '@/utils/BaseService';
+import { request } from 'umi';
+import SystemConst from '@/utils/const';
+import { defer, from } from 'rxjs';
+
+class Service extends BaseService<UserItem> {
+  public getUser = (params: Record<string, unknown>) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/user/_query`, {
+          method: 'GET',
+          params,
+        }),
+      ),
+    );
+
+  public saveBindData = (data: BindDataItem[]) =>
+    defer(() =>
+      from(
+        request(`/${SystemConst.API_BASE}/dimension-user`, {
+          method: 'PATCH',
+          data,
+        }),
+      ),
+    );
+
+  public unBindData = (data: string[], dimensionType: string, dimensionId: string) =>
+    defer(() =>
+      from(
+        request(
+          `/${SystemConst.API_BASE}/dimension-user/user/${dimensionType}/${dimensionId}/_unbind`,
+          {
+            method: 'POST',
+            data,
+          },
+        ),
+      ),
+    );
+}
+
+export default Service;

+ 7 - 0
src/components/BindUser/typings.d.ts

@@ -0,0 +1,7 @@
+type BindDataItem = {
+  dimensionId: string;
+  dimensionName: string;
+  dimensionTypeId: string;
+  userId: string;
+  userName: string;
+};

+ 21 - 3
src/pages/system/Org/index.tsx

@@ -1,7 +1,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import OrganizationChart from '@dabeng/react-orgchart';
 import styles from './index.less';
-import { Menu, message } from 'antd';
+import { Drawer, Menu, message } from 'antd';
 import NodeTemplate from '@/pages/system/Org/NodeTemplate';
 import { model } from '@formily/reactive';
 import { observer } from '@formily/react';
@@ -11,6 +11,8 @@ import encodeQuery from '@/utils/encodeQuery';
 import type { ObsModel, OrgItem } from '@/pages/system/Org/typings';
 import Save from '@/pages/system/Org/Save';
 import { useIntl } from '@@/plugin-locale/localeExports';
+import autzModel from '@/components/Authorization/autz';
+import Authorization from '@/components/Authorization';
 
 const obs = model<ObsModel>({
   edit: false,
@@ -126,8 +128,9 @@ const Org = observer(() => {
             target="_blank"
             rel="noopener noreferrer"
             onClick={() => {
-              // setCurrent(nodeData);
-              // setAutzVisible(true);
+              autzModel.autzTarget.id = nodeData.id;
+              autzModel.autzTarget.name = nodeData.name;
+              autzModel.visible = true;
             }}
           >
             {intl.formatMessage({
@@ -174,6 +177,21 @@ const Org = observer(() => {
         />
       </div>
       <Save obs={obs} />
+      <Drawer
+        title="授权"
+        width="50vw"
+        visible={autzModel.visible}
+        onClose={() => {
+          autzModel.visible = false;
+        }}
+      >
+        <Authorization
+          close={() => {
+            autzModel.visible = false;
+          }}
+          target={autzModel.autzTarget}
+        />
+      </Drawer>
     </PageContainer>
   );
 });

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

@@ -1,7 +1,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import React, { useRef } from 'react';
 import { EditOutlined, KeyOutlined, MinusOutlined, UserAddOutlined } from '@ant-design/icons';
-import { Drawer, Menu, message, Popconfirm, Tooltip } from 'antd';
+import { Drawer, message, Modal, Popconfirm, Tooltip } from 'antd';
 import type { ProColumns, ActionType } from '@jetlinks/pro-table';
 import BaseCrud from '@/components/BaseCrud';
 import { CurdModel } from '@/components/BaseCrud/model';
@@ -10,14 +10,8 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { observer } from '@formily/react';
 import autzModel from '@/components/Authorization/autz';
 import Authorization from '@/components/Authorization';
-
-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 { BindModel } from '@/components/BindUser/model';
+import BindUser from '@/components/BindUser';
 
 const service = new BaseService<RoleItem>('dimension');
 
@@ -121,7 +115,12 @@ const Role: React.FC = observer(() => {
         <a
           key="bind"
           onClick={() => {
-            console.log('绑定用户');
+            BindModel.dimension = {
+              id: record.id,
+              name: record.name,
+              type: 'role',
+            };
+            BindModel.visible = true;
             actionRef.current?.reload();
           }}
         >
@@ -232,10 +231,20 @@ const Role: React.FC = observer(() => {
           id: 'pages.system.role',
           defaultMessage: '角色管理',
         })}
-        menu={menu}
         schema={schema}
         defaultParams={{ typeId: 'role' }}
       />
+      <Modal
+        visible={BindModel.visible}
+        closable={false}
+        onCancel={() => {
+          BindModel.visible = false;
+          BindModel.bind = false;
+        }}
+        width={BindModel.bind ? '90vw' : '60vw'}
+      >
+        <BindUser />
+      </Modal>
       <Drawer
         title="授权"
         width="70vw"

+ 2 - 0
src/utils/const.ts

@@ -14,6 +14,8 @@ class SystemConst {
   static BASE_CURD_MODEL = 'BASE_CURD_MODEL';
 
   static GLOBAL_WEBSOCKET = 'GLOBAL-WEBSOCKET';
+
+  static BIND_USER_STATE = 'false';
 }
 
 export default SystemConst;