visuddhinanda 3 лет назад
Родитель
Сommit
eeb48f4a5d

+ 14 - 0
dashboard/src/components/group/Group.tsx

@@ -0,0 +1,14 @@
+import { Space } from "antd";
+
+export interface IGroup {
+  id: string;
+  name: string;
+}
+interface IWidget {
+  group?: IGroup;
+}
+const Widget = ({ group }: IWidget) => {
+  return <Space>{group?.name}</Space>;
+};
+
+export default Widget;

+ 226 - 0
dashboard/src/components/share/Collaborator.tsx

@@ -0,0 +1,226 @@
+import { useIntl } from "react-intl";
+import { useEffect, useRef, useState } from "react";
+import { ActionType, ProList } from "@ant-design/pro-components";
+import { Tag, Button, Popconfirm, Space, Badge, message, Dropdown } from "antd";
+import { UserOutlined, TeamOutlined } from "@ant-design/icons";
+
+import { delete_, get, put } from "../../request";
+
+import {
+  IShareDeleteResponse,
+  IShareListResponse,
+  IShareRequest,
+  IShareResponse,
+  IShareUpdateRequest,
+} from "../api/Share";
+import { IUser } from "../auth/User";
+import { TRole } from "../api/Auth";
+
+import Group, { IGroup } from "../group/Group";
+import UserName from "../auth/UserName";
+
+interface ICollaborator {
+  sn?: number;
+  id?: string;
+  resId: string;
+  resType: string;
+  power?: number;
+  user?: IUser;
+  group?: IGroup;
+  role?: TRole;
+}
+interface IWidget {
+  resId?: string;
+  load?: boolean;
+  onReload?: Function;
+}
+const Widget = ({ resId, load = false, onReload }: IWidget) => {
+  const intl = useIntl(); //i18n
+  const [canDelete, setCanDelete] = useState(false);
+  const [memberCount, setMemberCount] = useState<number>();
+
+  useEffect(() => {
+    if (load) {
+      ref.current?.reload();
+      if (typeof onReload !== "undefined") {
+        onReload();
+      }
+    }
+  }, [load, onReload]);
+  const ref = useRef<ActionType>();
+  const roleList: TRole[] = ["manager", "editor", "reader"];
+
+  return (
+    <ProList<ICollaborator>
+      rowKey="id"
+      actionRef={ref}
+      headerTitle={
+        <Space>
+          {intl.formatMessage({ id: "labels.collaborators" })}
+          <Badge color="geekblue" count={memberCount} />
+        </Space>
+      }
+      showActions="hover"
+      request={async (params = {}, sorter, filter) => {
+        // TODO
+        console.log(params, sorter, filter);
+
+        let url = `/v2/share?view=res&id=${resId}`;
+        const offset =
+          ((params.current ? params.current : 1) - 1) *
+          (params.pageSize ? params.pageSize : 20);
+        url += `&limit=${params.pageSize}&offset=${offset}`;
+        if (typeof params.keyword !== "undefined") {
+          url += "&search=" + (params.keyword ? params.keyword : "");
+        }
+        const res = await get<IShareListResponse>(url);
+        if (res.ok) {
+          console.log(res.data);
+          setMemberCount(res.data.count);
+          switch (res.data.role) {
+            case "owner":
+              setCanDelete(true);
+              break;
+            case "manager":
+              setCanDelete(true);
+              break;
+          }
+          const items: ICollaborator[] = res.data.rows.map((item, id) => {
+            let member: ICollaborator = {
+              sn: id + 1,
+              id: item.id,
+              resId: item.res_id,
+              resType: item.res_type,
+              power: item.power,
+              user: item.user,
+              group: item.group,
+              role: item.role,
+            };
+
+            return member;
+          });
+          console.log(items);
+          return {
+            total: res.data.count,
+            succcess: true,
+            data: items,
+          };
+        } else {
+          console.error(res.message);
+          return {
+            total: 0,
+            succcess: false,
+            data: [],
+          };
+        }
+      }}
+      pagination={{
+        showQuickJumper: true,
+        showSizeChanger: true,
+      }}
+      metas={{
+        title: {
+          render: (text, row, index, action) => {
+            return row.user ? (
+              <UserName {...row.user} key={index} />
+            ) : (
+              <Group group={row.group} key={index} />
+            );
+          },
+        },
+        avatar: {
+          render: (text, row, index, action) => {
+            return row.user ? (
+              <UserOutlined key={index} />
+            ) : (
+              <TeamOutlined key={index} />
+            );
+          },
+        },
+        subTitle: {
+          render: (text, row, index, action) => {
+            let right = "";
+            switch (row.power) {
+              case 10:
+                right = intl.formatMessage({ id: "auth.role.reader" });
+                break;
+              case 20:
+                right = intl.formatMessage({ id: "auth.role.editor" });
+                break;
+              case 30:
+                right = intl.formatMessage({ id: "auth.role.manager" });
+                break;
+              default:
+                break;
+            }
+            return (
+              <Dropdown
+                key={index}
+                trigger={["click"]}
+                menu={{
+                  items: roleList.map((item) => {
+                    return {
+                      key: item,
+                      label: intl.formatMessage({ id: "auth.role." + item }),
+                    };
+                  }),
+                  onClick: (e) => {
+                    put<IShareUpdateRequest, IShareResponse>(
+                      `/v2/share/${row.id}`,
+                      {
+                        role: e.key as TRole,
+                      }
+                    ).then((json) => {
+                      console.log(json);
+                      if (json.ok) {
+                        ref.current?.reload();
+                      }
+                    });
+                  },
+                }}
+              >
+                <Tag key={index}>{right}</Tag>
+              </Dropdown>
+            );
+          },
+        },
+        actions: {
+          render: (text, row, index, action) => [
+            canDelete ? (
+              <Popconfirm
+                key={index}
+                placement="bottomLeft"
+                title={intl.formatMessage({
+                  id: "forms.message.member.delete",
+                })}
+                onConfirm={(e?: React.MouseEvent<HTMLElement, MouseEvent>) => {
+                  console.log("delete", row.id);
+                  delete_<IShareDeleteResponse>("/v2/share/" + row.id)
+                    .then((json) => {
+                      if (json.ok) {
+                        message.success("delete ok");
+                        ref.current?.reload();
+                      } else {
+                        message.error(json.message);
+                      }
+                    })
+                    .catch((e) => {
+                      message.error(e);
+                    });
+                }}
+                okText={intl.formatMessage({ id: "buttons.ok" })}
+                cancelText={intl.formatMessage({ id: "buttons.cancel" })}
+              >
+                <Button size="small" type="link" danger key="link">
+                  {intl.formatMessage({ id: "buttons.remove" })}
+                </Button>
+              </Popconfirm>
+            ) : undefined,
+          ],
+        },
+      }}
+    />
+  );
+};
+
+export default Widget;

+ 122 - 0
dashboard/src/components/share/CollaboratorAdd.tsx

@@ -0,0 +1,122 @@
+import { message } from "antd";
+import { useIntl } from "react-intl";
+import {
+  ProForm,
+  ProFormDependency,
+  ProFormInstance,
+  ProFormSelect,
+} from "@ant-design/pro-components";
+
+import { post } from "../../request";
+import { TRole } from "../api/Auth";
+import { IShareRequest, IShareResponse } from "../api/Share";
+import { useRef } from "react";
+import { EResType } from "./Share";
+import UserSelect from "../template/UserSelect";
+import GroupSelect from "../template/GroupSelect";
+
+interface IWidget {
+  resId: string;
+  resType: EResType;
+  onSuccess?: Function;
+}
+const Widget = ({ resId, resType, onSuccess }: IWidget) => {
+  const roleList = ["manager", "editor", "reader"];
+  const intl = useIntl();
+  const formRef = useRef<ProFormInstance>();
+  interface IFormData {
+    userId: string[];
+    groupId: string[];
+    userType: string;
+    role: TRole;
+  }
+  return (
+    <ProForm<IFormData>
+      formRef={formRef}
+      onFinish={async (values: IFormData) => {
+        // TODO
+        console.log(values);
+        if (typeof resId !== "undefined") {
+          post<IShareRequest, IShareResponse>("/v2/share", {
+            user_id:
+              values.userType === "user" ? values.userId : values.groupId,
+            user_type: values.userType,
+            role: values.role,
+            res_id: resId,
+            res_type: resType,
+          }).then((json) => {
+            console.log("add member", json);
+            if (json.ok) {
+              if (typeof onSuccess !== "undefined") {
+                onSuccess();
+              }
+              formRef.current?.resetFields(["userId"]);
+              message.success(intl.formatMessage({ id: "flashes.success" }));
+            }
+          });
+        }
+      }}
+    >
+      <ProForm.Group>
+        <ProFormSelect
+          initialValue={"user"}
+          name="userType"
+          label={intl.formatMessage({ id: "forms.fields.role.label" })}
+          allowClear={false}
+          options={[
+            {
+              value: "user",
+              label: intl.formatMessage({ id: "auth.type.user" }),
+            },
+            {
+              value: "group",
+              label: intl.formatMessage({ id: "auth.type.group" }),
+            },
+          ]}
+          rules={[
+            {
+              required: true,
+              message: intl.formatMessage({
+                id: "forms.message.user.required",
+              }),
+            },
+          ]}
+        />
+      </ProForm.Group>
+      <ProForm.Group>
+        <ProFormDependency name={["userType"]}>
+          {({ userType }) => {
+            if (userType === "user") {
+              return <UserSelect name="userId" multiple={true} />;
+            } else {
+              return <GroupSelect name="groupId" multiple={true} />;
+            }
+          }}
+        </ProFormDependency>
+
+        <ProFormSelect
+          name="role"
+          initialValue={"reader"}
+          label={intl.formatMessage({ id: "forms.fields.role.label" })}
+          allowClear={false}
+          options={roleList.map((item) => {
+            return {
+              value: item,
+              label: intl.formatMessage({ id: "auth.role." + item }),
+            };
+          })}
+          rules={[
+            {
+              required: true,
+              message: intl.formatMessage({
+                id: "forms.message.user.required",
+              }),
+            },
+          ]}
+        />
+      </ProForm.Group>
+    </ProForm>
+  );
+};
+
+export default Widget;

+ 42 - 0
dashboard/src/components/share/ShareModal.tsx

@@ -0,0 +1,42 @@
+import { useState } from "react";
+import { Modal } from "antd";
+import Share, { EResType } from "./Share";
+
+interface IWidget {
+  resId: string;
+  resType: EResType;
+  trigger?: React.ReactNode;
+}
+const Widget = ({ resId, resType, trigger }: IWidget) => {
+  const [isModalOpen, setIsModalOpen] = useState(false);
+
+  const showModal = () => {
+    setIsModalOpen(true);
+  };
+
+  const handleOk = () => {
+    setIsModalOpen(false);
+  };
+
+  const handleCancel = () => {
+    setIsModalOpen(false);
+  };
+
+  return (
+    <>
+      <span onClick={showModal}>{trigger}</span>
+      <Modal
+        destroyOnClose={true}
+        width={700}
+        title="协作"
+        open={isModalOpen}
+        onOk={handleOk}
+        onCancel={handleCancel}
+      >
+        <Share resId={resId} resType={resType} />
+      </Modal>
+    </>
+  );
+};
+
+export default Widget;

+ 55 - 0
dashboard/src/components/template/GroupSelect.tsx

@@ -0,0 +1,55 @@
+import { ProFormSelect } from "@ant-design/pro-components";
+import { useIntl } from "react-intl";
+
+import { get } from "../../request";
+import { IGroupListResponse } from "../api/Group";
+
+interface IWidget {
+  name?: string;
+  width?: number | "md" | "sm" | "xl" | "xs" | "lg";
+  multiple?: boolean;
+  hidden?: boolean;
+}
+const Widget = ({
+  name = "user",
+  multiple = false,
+  width = "md",
+  hidden = false,
+}: IWidget) => {
+  const intl = useIntl();
+  return (
+    <ProFormSelect
+      name={name}
+      label={intl.formatMessage({ id: "group.fields.name.label" })}
+      hidden={hidden}
+      width={width}
+      showSearch
+      debounceTime={300}
+      fieldProps={{
+        mode: multiple ? "multiple" : undefined,
+      }}
+      request={async ({ keyWords }) => {
+        console.log("group keyWord", keyWords);
+        const json = await get<IGroupListResponse>(
+          `/v2/group?view=key&key=${keyWords}`
+        );
+        console.log("json", json);
+        const userList = json.data.rows.map((item) => {
+          return {
+            value: item.uid,
+            label: `${item.studio.studioName}/${item.name}`,
+          };
+        });
+
+        return userList;
+      }}
+      rules={[
+        {
+          required: true,
+        },
+      ]}
+    />
+  );
+};
+
+export default Widget;

+ 54 - 0
dashboard/src/components/template/UserSelect.tsx

@@ -0,0 +1,54 @@
+import { ProFormSelect } from "@ant-design/pro-components";
+import { useIntl } from "react-intl";
+
+import { get } from "../../request";
+import { IUserListResponse } from "../api/Auth";
+
+interface IWidget {
+  name?: string;
+  width?: number | "md" | "sm" | "xl" | "xs" | "lg";
+  multiple?: boolean;
+  hidden?: boolean;
+}
+const Widget = ({
+  name = "user",
+  multiple = false,
+  width = "md",
+  hidden = false,
+}: IWidget) => {
+  const intl = useIntl();
+  return (
+    <ProFormSelect
+      name={name}
+      label={intl.formatMessage({ id: "forms.fields.user.label" })}
+      hidden={hidden}
+      width={width}
+      showSearch
+      debounceTime={300}
+      fieldProps={{
+        mode: multiple ? "multiple" : undefined,
+      }}
+      request={async ({ keyWords }) => {
+        console.log("keyWord", keyWords);
+        const json = await get<IUserListResponse>(
+          `/v2/user?view=key&key=${keyWords}`
+        );
+        const userList = json.data.rows.map((item) => {
+          return {
+            value: item.id,
+            label: `${item.userName}-${item.nickName}`,
+          };
+        });
+        console.log("json", userList);
+        return userList;
+      }}
+      rules={[
+        {
+          required: true,
+        },
+      ]}
+    />
+  );
+};
+
+export default Widget;