Преглед изворни кода

Merge pull request #2026 from visuddhinanda/agile

视频组件打开时才获取url
visuddhinanda пре 2 година
родитељ
комит
c9a40fd810

+ 27 - 3
dashboard/src/components/general/Video.tsx

@@ -1,13 +1,37 @@
-import { useRef } from "react";
+import { useEffect, useRef, useState } from "react";
 import videojs from "video.js";
 import videojs from "video.js";
 import VideoPlayer from "./VideoPlayer";
 import VideoPlayer from "./VideoPlayer";
+import { IAttachmentResponse } from "../api/Attachments";
+import { get } from "../../request";
 
 
 interface IWidget {
 interface IWidget {
+  fileName?: string;
+  fileId?: string;
   src?: string;
   src?: string;
   type?: string;
   type?: string;
 }
 }
-export const VideoWidget = ({ src, type }: IWidget) => {
+const VideoWidget = ({ fileName, fileId, src, type }: IWidget) => {
   const playerRef = useRef<videojs.Player>();
   const playerRef = useRef<videojs.Player>();
+  const [url, setUrl] = useState<string>();
+
+  useEffect(() => {
+    if (fileId) {
+      const url = `/v2/attachment/${fileId}`;
+      console.info("VideoWidget api request", url);
+      get<IAttachmentResponse>(url).then((json) => {
+        console.debug("VideoWidget api response", json);
+        if (json.ok) {
+          setUrl(json.data.url);
+        }
+      });
+    }
+  }, [fileId]);
+
+  useEffect(() => {
+    if (src) {
+      setUrl(src);
+    }
+  }, [src]);
 
 
   const handlePlayerReady = (player: videojs.Player) => {
   const handlePlayerReady = (player: videojs.Player) => {
     if (playerRef.current) {
     if (playerRef.current) {
@@ -32,7 +56,7 @@ export const VideoWidget = ({ src, type }: IWidget) => {
         poster: "",
         poster: "",
         sources: [
         sources: [
           {
           {
-            src: src ? src : "",
+            src: url ? url : "",
             type: type ? type : "video/mp4",
             type: type ? type : "video/mp4",
           },
           },
         ],
         ],

+ 134 - 139
dashboard/src/components/template/Video.tsx

@@ -1,50 +1,81 @@
 import { Button, Card, Collapse, Modal, Popover, Space } from "antd";
 import { Button, Card, Collapse, Modal, Popover, Space } from "antd";
 import { Typography } from "antd";
 import { Typography } from "antd";
-import { useEffect, useState } from "react";
+import { useState } from "react";
 import { CloseOutlined } from "@ant-design/icons";
 import { CloseOutlined } from "@ant-design/icons";
 
 
-import { Link } from "react-router-dom";
 import { TDisplayStyle } from "./Article";
 import { TDisplayStyle } from "./Article";
 import Video from "../general/Video";
 import Video from "../general/Video";
 import { VideoIcon } from "../../assets/icon";
 import { VideoIcon } from "../../assets/icon";
-import { useIntl } from "react-intl";
 import { IAttachmentResponse } from "../api/Attachments";
 import { IAttachmentResponse } from "../api/Attachments";
 import { get } from "../../request";
 import { get } from "../../request";
 
 
 const { Text } = Typography;
 const { Text } = Typography;
 
 
+const getUrl = async (fileId: string) => {
+  const url = `/v2/attachment/${fileId}`;
+  console.info("url", url);
+  const res = await get<IAttachmentResponse>(url);
+  if (res.ok) {
+    return res.data.url;
+  } else {
+    return "";
+  }
+};
+
+const getLink = async ({ url, id, type, title }: IVideoCtl) => {
+  let link = url;
+  if (!link && id) {
+    const res = await getUrl(id);
+    link = res;
+  }
+  return link;
+};
+
 interface IVideoCtl {
 interface IVideoCtl {
   url?: string;
   url?: string;
   id?: string;
   id?: string;
+  type?: string;
   title?: React.ReactNode;
   title?: React.ReactNode;
   style?: TDisplayStyle;
   style?: TDisplayStyle;
   _style?: TDisplayStyle;
   _style?: TDisplayStyle;
 }
 }
 
 
-export const VideoCtl = ({
-  url,
-  id,
-  title,
-  style = "modal",
-  _style,
-}: IVideoCtl) => {
-  const intl = useIntl();
-  const [isModalOpen, setIsModalOpen] = useState(false);
-  const [fetchUrl, setFetchUrl] = useState<string>();
-  style = _style ? _style : style;
-  useEffect(() => {
-    if (id) {
-      const url = `/v2/attachment/${id}`;
-      console.info("url", url);
-      get<IAttachmentResponse>(url).then((json) => {
-        console.log(json);
-        if (json.ok) {
-          setFetchUrl(json.data.url);
-        }
-      });
-    }
-  }, [id]);
+const VideoPopover = ({ url, id, type, title }: IVideoCtl) => {
+  const [popOpen, setPopOpen] = useState(false);
+  console.debug("popOpen", popOpen);
+  return (
+    <Popover
+      title={
+        <div>
+          {title}
+          <Button
+            type="link"
+            icon={<CloseOutlined />}
+            onClick={() => {
+              setPopOpen(false);
+            }}
+          />
+        </div>
+      }
+      content={
+        <div style={{ width: 600, height: 480 }}>
+          <Video fileId={id} src={url} type={type} />
+        </div>
+      }
+      trigger={"click"}
+      placement="bottom"
+      open={popOpen}
+    >
+      <span onClick={() => setPopOpen(true)}>
+        <VideoIcon />
+        {title}
+      </span>
+    </Popover>
+  );
+};
 
 
+const VideoModal = ({ url, id, type, title }: IVideoCtl) => {
+  const [isModalOpen, setIsModalOpen] = useState(false);
   const showModal = () => {
   const showModal = () => {
     setIsModalOpen(true);
     setIsModalOpen(true);
   };
   };
@@ -57,65 +88,65 @@ export const VideoCtl = ({
     setIsModalOpen(false);
     setIsModalOpen(false);
   };
   };
 
 
-  let output = <></>;
-  let articleLink = url ? url : fetchUrl ? fetchUrl : "";
-
-  const VideoModal = () => {
-    return (
-      <>
-        <Typography.Link
-          onClick={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
-            if (event.ctrlKey || event.metaKey) {
-              window.open(articleLink, "_blank");
-            } else {
-              showModal();
-            }
-          }}
-        >
-          <Space>
-            <VideoIcon />
-            {title}
-          </Space>
-        </Typography.Link>
-        <Modal
-          width={"90%"}
-          destroyOnClose
-          style={{ maxWidth: 1000, top: 20, height: 700 }}
-          title={
-            <div
-              style={{
-                display: "flex",
-                justifyContent: "space-between",
-                marginRight: 30,
-              }}
-            >
-              <Text>{title}</Text>
-              <Text>
-                <Link to={articleLink} target="_blank">
-                  {intl.formatMessage({
-                    id: "buttons.open.in.new.tab",
-                  })}
-                </Link>
-              </Text>
-            </div>
+  return (
+    <>
+      <Typography.Link
+        onClick={async (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
+          if (event.ctrlKey || event.metaKey) {
+            const link = await getLink({ url: url, id: id });
+            window.open(link, "_blank");
+          } else {
+            showModal();
           }
           }
-          open={isModalOpen}
-          onOk={handleOk}
-          onCancel={handleCancel}
-          footer={[]}
-        >
-          <div style={{ height: 550 }}>
-            <Video src={url} />
+        }}
+      >
+        <Space>
+          <VideoIcon />
+          {title}
+        </Space>
+      </Typography.Link>
+      <Modal
+        width={800}
+        destroyOnClose
+        style={{ maxWidth: "90%", top: 20, height: 700 }}
+        title={
+          <div
+            style={{
+              display: "flex",
+              justifyContent: "space-between",
+              marginRight: 30,
+            }}
+          >
+            <Text>{title}</Text>
           </div>
           </div>
-        </Modal>
-      </>
-    );
-  };
+        }
+        open={isModalOpen}
+        onOk={handleOk}
+        onCancel={handleCancel}
+        footer={[]}
+      >
+        <div style={{ height: 550 }}>
+          <Video fileId={id} src={url} type={type} />
+        </div>
+      </Modal>
+    </>
+  );
+};
+
+export const VideoCtl = ({
+  url,
+  id,
+  type,
+  title,
+  style = "modal",
+  _style,
+}: IVideoCtl) => {
+  const curStyle = _style ? _style : style;
 
 
   const VideoCard = () => {
   const VideoCard = () => {
     return (
     return (
       <Card title={title} bodyStyle={{ width: 550, height: 420 }}>
       <Card title={title} bodyStyle={{ width: 550, height: 420 }}>
-        <Video src={url} />
+        <Video fileId={id} src={url} type={type} />
       </Card>
       </Card>
     );
     );
   };
   };
@@ -123,7 +154,7 @@ export const VideoCtl = ({
   const VideoWindow = () => {
   const VideoWindow = () => {
     return (
     return (
       <div style={{ width: 550, height: 420 }}>
       <div style={{ width: 550, height: 420 }}>
-        <Video src={url} />
+        <Video fileId={id} src={url} type={type} />
       </div>
       </div>
     );
     );
   };
   };
@@ -132,7 +163,7 @@ export const VideoCtl = ({
     return (
     return (
       <Collapse bordered={false}>
       <Collapse bordered={false}>
         <Collapse.Panel header={title} key="parent2">
         <Collapse.Panel header={title} key="parent2">
-          <Video src={url} />
+          <Video fileId={id} src={url} type={type} />
         </Collapse.Panel>
         </Collapse.Panel>
       </Collapse>
       </Collapse>
     );
     );
@@ -140,71 +171,35 @@ export const VideoCtl = ({
 
 
   const VideoLink = () => {
   const VideoLink = () => {
     return (
     return (
-      <Link to={articleLink} target="_blank">
+      <Typography.Link
+        onClick={async () => {
+          const link = await getLink({ url: url, id: id });
+          window.open(link, "_blank");
+        }}
+      >
         <Space>
         <Space>
           <VideoIcon />
           <VideoIcon />
           {title}
           {title}
         </Space>
         </Space>
-      </Link>
+      </Typography.Link>
     );
     );
   };
   };
 
 
-  const VideoPopover = () => {
-    const [popOpen, setPopOpen] = useState(false);
-
-    return (
-      <Popover
-        title={
-          <div>
-            {title}
-            <Button
-              type="link"
-              icon={<CloseOutlined />}
-              onClick={() => {
-                setPopOpen(false);
-              }}
-            />
-          </div>
-        }
-        content={
-          <div style={{ width: 600, height: 350 }}>
-            <Video src={url} />
-          </div>
-        }
-        trigger={"click"}
-        placement="bottom"
-        open={popOpen}
-      >
-        <span onClick={() => setPopOpen(true)}>
-          <VideoIcon />
-          {title}
-        </span>
-      </Popover>
-    );
-  };
-  switch (style) {
-    case "modal":
-      output = <VideoModal />;
-      break;
-    case "card":
-      output = <VideoCard />;
-      break;
-    case "window":
-      output = <VideoWindow />;
-      break;
-    case "toggle":
-      output = <VideoToggle />;
-      break;
-    case "link":
-      output = <VideoLink />;
-      break;
-    case "popover":
-      output = <VideoPopover />;
-      break;
-    default:
-      break;
-  }
-  return output;
+  return curStyle === "modal" ? (
+    <VideoModal url={url} id={id} type={type} title={title} />
+  ) : curStyle === "card" ? (
+    <VideoCard />
+  ) : curStyle === "window" ? (
+    <VideoWindow />
+  ) : curStyle === "toggle" ? (
+    <VideoToggle />
+  ) : curStyle === "link" ? (
+    <VideoLink />
+  ) : curStyle === "popover" ? (
+    <VideoPopover url={url} id={id} type={type} title={title} />
+  ) : (
+    <></>
+  );
 };
 };
 
 
 interface IWidget {
 interface IWidget {

+ 25 - 22
dashboard/src/components/template/Wbw/WbwPali.tsx

@@ -10,7 +10,7 @@ import {
 
 
 import "./wbw.css";
 import "./wbw.css";
 import WbwDetail from "./WbwDetail";
 import WbwDetail from "./WbwDetail";
-import { IWbw, TWbwDisplayMode } from "./WbwWord";
+import { IWbw, IWbwAttachment, TWbwDisplayMode } from "./WbwWord";
 import { bookMarkColor } from "./WbwDetailBookMark";
 import { bookMarkColor } from "./WbwDetailBookMark";
 import WbwVideoButton from "./WbwVideoButton";
 import WbwVideoButton from "./WbwVideoButton";
 import CommentBox from "../../discussion/DiscussionDrawer";
 import CommentBox from "../../discussion/DiscussionDrawer";
@@ -24,6 +24,29 @@ import { anchor, showWbw } from "../../../reducers/wbw";
 import { CommentOutlinedIcon } from "../../../assets/icon";
 import { CommentOutlinedIcon } from "../../../assets/icon";
 import { ParaLinkCtl } from "../ParaLink";
 import { ParaLinkCtl } from "../ParaLink";
 
 
+//生成视频播放按钮
+interface IVideoIcon {
+  attachments?: IWbwAttachment[];
+}
+const VideoIcon = ({ attachments }: IVideoIcon) => {
+  const videoList = attachments?.filter((item) =>
+    item.content_type?.includes("video")
+  );
+  return videoList ? (
+    <WbwVideoButton
+      video={videoList?.map((item) => {
+        return {
+          videoId: item.id,
+          type: item.content_type,
+          title: item.title,
+        };
+      })}
+    />
+  ) : (
+    <></>
+  );
+};
+
 const { Paragraph } = Typography;
 const { Paragraph } = Typography;
 interface IWidget {
 interface IWidget {
   data: IWbw;
   data: IWbw;
@@ -189,26 +212,6 @@ const WbwPaliWidget = ({ data, channelId, mode, display, onSave }: IWidget) => {
     ? bookMarkColor[data.bookMarkColor.value]
     ? bookMarkColor[data.bookMarkColor.value]
     : "white";
     : "white";
 
 
-  //生成视频播放按钮
-  const VideoIcon = () => {
-    const videoList = data.attachments?.filter((item) =>
-      item.content_type?.includes("video")
-    );
-    return videoList ? (
-      <WbwVideoButton
-        video={videoList?.map((item) => {
-          return {
-            videoId: item.id,
-            type: item.content_type,
-            title: item.title,
-          };
-        })}
-      />
-    ) : (
-      <></>
-    );
-  };
-
   const RelationIcon = () => {
   const RelationIcon = () => {
     return data.relation ? (
     return data.relation ? (
       <ApartmentOutlined style={{ color: "blue" }} />
       <ApartmentOutlined style={{ color: "blue" }} />
@@ -375,7 +378,7 @@ const WbwPaliWidget = ({ data, channelId, mode, display, onSave }: IWidget) => {
           </Popover>
           </Popover>
         </span>
         </span>
         <Space>
         <Space>
-          <VideoIcon />
+          <VideoIcon attachments={data.attachments} />
           <NoteIcon />
           <NoteIcon />
           <BookMarkIcon />
           <BookMarkIcon />
           <RelationIcon />
           <RelationIcon />

+ 2 - 24
dashboard/src/components/template/Wbw/WbwVideoButton.tsx

@@ -1,9 +1,4 @@
-import { VideoCameraOutlined } from "@ant-design/icons";
-import { useEffect, useState } from "react";
-import { get } from "../../../request";
-import { IAttachmentResponse } from "../../api/Attachments";
-import VideoModal from "../../general/VideoModal";
-import { VideoIcon } from "../../../assets/icon";
+import { VideoCtl } from "../Video";
 
 
 export interface IVideo {
 export interface IVideo {
   videoId: string;
   videoId: string;
@@ -15,25 +10,8 @@ interface IWidget {
   video: IVideo[];
   video: IVideo[];
 }
 }
 const WbwVideoButtonWidget = ({ video }: IWidget) => {
 const WbwVideoButtonWidget = ({ video }: IWidget) => {
-  const [url, setUrl] = useState<string>();
-  const [curr, setCurr] = useState(0);
-
-  useEffect(() => {
-    if (!video || video.length === 0) {
-      return;
-    }
-    const url = `/v2/attachment/${video[curr].videoId}`;
-    console.info("url", url);
-    get<IAttachmentResponse>(url).then((json) => {
-      console.log(json);
-      if (json.ok) {
-        setUrl(json.data.url);
-      }
-    });
-  }, [curr, video]);
-
   return video && video.length > 0 ? (
   return video && video.length > 0 ? (
-    <VideoModal src={url} type={video[0].type} trigger={<VideoIcon />} />
+    <VideoCtl id={video[0].videoId} type={video[0].type} _style="modal" />
   ) : (
   ) : (
     <></>
     <></>
   );
   );