visuddhinanda il y a 2 ans
Parent
commit
af26e812d3

+ 40 - 0
dashboard/src/components/api/Transfer.ts

@@ -0,0 +1,40 @@
+import { IStudio } from "../auth/StudioName";
+import { IUser } from "../auth/User";
+import { IChannel } from "../channel/Channel";
+import { TResType } from "../discussion/DiscussionListCard";
+
+export type ITransferStatus = "transferred" | "accept" | "refuse" | "cancel";
+export interface ITransferRequest {
+  res_type?: TResType;
+  res_id?: string;
+  new_owner?: string;
+  status?: ITransferStatus;
+}
+export interface ITransferResponseData {
+  id: string;
+  origin_owner: IStudio;
+  res_type: TResType;
+  res_id: string;
+  channel?: IChannel;
+  transferor: IUser;
+  new_owner: IStudio;
+  status: ITransferStatus;
+  editor?: IUser | null;
+  created_at: string;
+  updated_at: string;
+}
+export interface ITransferResponse {
+  ok: boolean;
+  message: string;
+  data: ITransferResponseData;
+}
+export interface ITransferResponseList {
+  ok: boolean;
+  message: string;
+  data: {
+    rows: ITransferResponseData[];
+    count: number;
+    out: number;
+    in: number;
+  };
+}

+ 95 - 0
dashboard/src/components/transfer/TransferCreate.tsx

@@ -0,0 +1,95 @@
+import { ModalForm, ProForm } from "@ant-design/pro-components";
+import { Alert, Form, message, notification } from "antd";
+import { TResType } from "./TransferList";
+import { post } from "../../request";
+import { ITransferRequest, ITransferResponse } from "../api/Transfer";
+import { useIntl } from "react-intl";
+import UserSelect from "../template/UserSelect";
+import { useEffect, useState } from "react";
+
+interface IWidget {
+  studioName?: string;
+  resType: TResType;
+  resId?: string;
+  resName?: string;
+  open?: boolean;
+  onOpenChange?: Function;
+  onCreate?: Function;
+}
+const TransferCreateWidget = ({
+  studioName,
+  resType,
+  resId,
+  resName,
+  open = false,
+  onOpenChange,
+  onCreate,
+}: IWidget) => {
+  const intl = useIntl();
+  const [form] = Form.useForm<{ studio: string }>();
+  const [modalVisit, setModalVisit] = useState(open);
+  useEffect(() => setModalVisit(open), [open]);
+  const strTransfer = intl.formatMessage({
+    id: `columns.studio.transfer.title`,
+  });
+  return (
+    <ModalForm<{
+      studio: string;
+    }>
+      open={modalVisit}
+      onOpenChange={(visible) => {
+        if (typeof onOpenChange !== "undefined") {
+          onOpenChange(visible);
+        }
+      }}
+      title={intl.formatMessage({
+        id: `columns.studio.transfer.title`,
+      })}
+      form={form}
+      autoFocusFirstInput
+      modalProps={{
+        destroyOnClose: true,
+        onCancel: () => console.log("run"),
+      }}
+      submitTimeout={2000}
+      onFinish={async (values) => {
+        console.log(values);
+        if (typeof resId === "undefined") {
+          return;
+        }
+        const data = {
+          res_type: resType,
+          res_id: resId,
+          new_owner: values.studio,
+        };
+        const res = await post<ITransferRequest, ITransferResponse>(
+          `/v2/transfer`,
+          data
+        );
+        if (res.ok) {
+          if (typeof onCreate === "undefined") {
+            notification.open({
+              message: strTransfer,
+              description: `${resType} ${resName} 已经转出。请等待对方确认。可以在转移管理中查看状态或取消。`,
+              duration: 0,
+            });
+          } else {
+            onCreate();
+          }
+        } else {
+          message.error(res.message, 10);
+        }
+        return true;
+      }}
+    >
+      <Alert
+        message={`将${resName} ${strTransfer}下面的用户。操作后需要等待对方确认后,资源才会被转移。可以在${strTransfer}查看和取消`}
+      />
+      <ProForm.Group>
+        <UserSelect name="studio" multiple={false} />
+      </ProForm.Group>
+    </ModalForm>
+  );
+};
+
+export default TransferCreateWidget;

+ 249 - 0
dashboard/src/components/transfer/TransferList.tsx

@@ -0,0 +1,249 @@
+import { useRef, useState } from "react";
+import { Button, Space, Tag, Typography, message, notification } from "antd";
+
+import { get, put } from "../../request";
+import { ActionType, ProList } from "@ant-design/pro-components";
+import { renderBadge } from "../channel/ChannelTable";
+import User, { IUser } from "../auth/User";
+import { IChannel } from "../channel/Channel";
+import { IStudio } from "../auth/StudioName";
+import UserName from "../auth/UserName";
+import TimeShow from "../general/TimeShow";
+import {
+  ITransferRequest,
+  ITransferResponse,
+  ITransferResponseList,
+  ITransferStatus,
+} from "../api/Transfer";
+import { useIntl } from "react-intl";
+import { BaseType } from "antd/lib/typography/Base";
+
+const { Text } = Typography;
+
+export type TResType = "article" | "channel" | "chapter" | "sentence" | "wbw";
+
+interface ITransfer {
+  id: string;
+  origin_owner: IStudio;
+  res_type: TResType;
+  res_id: string;
+  channel?: IChannel;
+  transferor: IUser;
+  new_owner: IStudio;
+  status: ITransferStatus;
+  editor?: IUser | null;
+  created_at: string;
+  updated_at: string;
+}
+interface IWidget {
+  studioName?: string;
+}
+const TransferListWidget = ({ studioName }: IWidget) => {
+  const ref = useRef<ActionType>();
+  const [activeKey, setActiveKey] = useState<React.Key | undefined>("in");
+  const [activeNumber, setActiveNumber] = useState<number>(0);
+  const [closeNumber, setCloseNumber] = useState<number>(0);
+  const intl = useIntl();
+
+  const openNotification = (description: string) => {
+    const args = {
+      message: intl.formatMessage({
+        id: `columns.studio.transfer.title`,
+      }),
+      description: description,
+      duration: 0,
+    };
+    notification.open(args);
+  };
+
+  const setStatus = (status: ITransferStatus, id: string) => {
+    const data: ITransferRequest = {
+      status: status,
+    };
+    put<ITransferRequest, ITransferResponse>(`/v2/transfer/${id}`, data).then(
+      (json) => {
+        if (json.ok) {
+          ref.current?.reload();
+          openNotification(
+            `已经` + intl.formatMessage({ id: `forms.status.${status}.label` })
+          );
+        } else {
+          message.error(json.message);
+        }
+      }
+    );
+  };
+  return (
+    <>
+      <ProList<ITransfer>
+        rowKey="id"
+        actionRef={ref}
+        metas={{
+          avatar: {
+            render(dom, entity, index, action, schema) {
+              return (
+                <>
+                  <User {...entity.transferor} showName={false} />
+                </>
+              );
+            },
+          },
+          title: {
+            render(dom, entity, index, action, schema) {
+              return (
+                <>
+                  {entity.origin_owner.studioName}/{entity.channel?.name}
+                </>
+              );
+            },
+          },
+          subTitle: {
+            render(dom, entity, index, action, schema) {
+              return <Tag>{entity.res_type}</Tag>;
+            },
+          },
+          description: {
+            search: false,
+            render(dom, entity, index, action, schema) {
+              return (
+                <Space>
+                  <UserName {...entity.transferor} />
+                  {"transfer at"}
+                  <TimeShow createdAt={entity.created_at} />
+                </Space>
+              );
+            },
+          },
+          content: {
+            render(dom, entity, index, action, schema) {
+              let style: BaseType | undefined;
+              switch (entity.status) {
+                case "accept":
+                  style = "success";
+                  break;
+                case "refuse":
+                  style = "warning";
+                  break;
+                case "cancel":
+                  style = "danger";
+                  break;
+                default:
+                  style = undefined;
+                  break;
+              }
+              return (
+                <Text type={style}>
+                  {intl.formatMessage({
+                    id: `forms.status.${entity.status}.label`,
+                  })}
+                </Text>
+              );
+            },
+          },
+          actions: {
+            render: (text, row, index, action) => [
+              activeKey === "in" ? (
+                <>
+                  <Button
+                    type="text"
+                    disabled={row.status !== "transferred"}
+                    onClick={() => setStatus("accept", row.id)}
+                  >
+                    {intl.formatMessage({
+                      id: `buttons.accept`,
+                    })}
+                  </Button>
+                  <Button
+                    disabled={row.status !== "transferred"}
+                    danger
+                    type="text"
+                    onClick={() => setStatus("refuse", row.id)}
+                  >
+                    {intl.formatMessage({
+                      id: `buttons.refuse`,
+                    })}
+                  </Button>
+                </>
+              ) : (
+                <>
+                  <Button
+                    type="text"
+                    disabled={row.status !== "transferred"}
+                    onClick={() => setStatus("cancel", row.id)}
+                  >
+                    {intl.formatMessage({
+                      id: `buttons.cancel`,
+                    })}
+                  </Button>
+                </>
+              ),
+            ],
+          },
+        }}
+        request={async (params = {}, sorter, filter) => {
+          let url = `/v2/transfer?view=studio&name=${studioName}&view2=${activeKey}`;
+          const offset =
+            ((params.current ? params.current : 1) - 1) *
+            (params.pageSize ? params.pageSize : 20);
+          url += `&limit=${params.pageSize}&offset=${offset}`;
+          url += params.keyword ? "&search=" + params.keyword : "";
+          console.log("url", url);
+          const res = await get<ITransferResponseList>(url);
+          const items: ITransfer[] = res.data.rows.map((item, id) => {
+            return item;
+          });
+
+          setActiveNumber(res.data.in);
+          setCloseNumber(res.data.out);
+
+          return {
+            total: res.data.count,
+            succcess: true,
+            data: items,
+          };
+        }}
+        bordered
+        pagination={{
+          showQuickJumper: true,
+          showSizeChanger: true,
+          pageSize: 10,
+        }}
+        search={false}
+        options={{
+          search: false,
+        }}
+        toolbar={{
+          menu: {
+            activeKey,
+            items: [
+              {
+                key: "in",
+                label: (
+                  <span>
+                    转入
+                    {renderBadge(activeNumber, activeKey === "in")}
+                  </span>
+                ),
+              },
+              {
+                key: "out",
+                label: (
+                  <span>
+                    转出
+                    {renderBadge(closeNumber, activeKey === "out")}
+                  </span>
+                ),
+              },
+            ],
+            onChange(key) {
+              setActiveKey(key);
+              ref.current?.reload();
+            },
+          },
+        }}
+      />
+    </>
+  );
+};
+
+export default TransferListWidget;

+ 22 - 0
dashboard/src/pages/studio/transfer/index.tsx

@@ -0,0 +1,22 @@
+import { Outlet } from "react-router-dom";
+import { Layout } from "antd";
+
+import LeftSider from "../../../components/studio/LeftSider";
+import { styleStudioContent } from "../style";
+
+const { Content } = Layout;
+
+const Widget = () => {
+  return (
+    <Layout>
+      <Layout>
+        <LeftSider selectedKeys="transfer" />
+        <Content style={styleStudioContent}>
+          <Outlet />
+        </Content>
+      </Layout>
+    </Layout>
+  );
+};
+
+export default Widget;

+ 13 - 0
dashboard/src/pages/studio/transfer/list.tsx

@@ -0,0 +1,13 @@
+import { useParams } from "react-router-dom";
+import TransferList from "../../../components/transfer/TransferList";
+
+const Widget = () => {
+  const { studioname } = useParams();
+  return (
+    <>
+      <TransferList studioName={studioname} />
+    </>
+  );
+};
+
+export default Widget;