104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
import { Card, Space, CardProps, Spin, Button } from 'antd';
|
|
import { theme } from 'antd/lib';
|
|
import { VideoPlayer, type VideoViewRef } from '@zhst/meta';
|
|
|
|
import React, { useState, useEffect, ReactNode, useRef } from 'react';
|
|
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
|
import './index.less'
|
|
|
|
export interface VideoPlayerCardProps {
|
|
windowKey?: string;
|
|
selectedWindowKey?: string;
|
|
showType?: 'video' | "image";
|
|
imgSrc?: string;
|
|
videoSrc?: string;
|
|
cardProps?: CardProps;
|
|
errorReasonText?: string;
|
|
isWindowLoading?: boolean;
|
|
size?: 'large' | 'small';
|
|
title?: string | ReactNode
|
|
handleCloseButtonClick?: (key?: string) => void;
|
|
handleWindowClick?: (key?: string) => void;
|
|
}
|
|
|
|
export const VideoPlayerCard: React.FC<VideoPlayerCardProps> = (props) => {
|
|
|
|
const componentName = `zhst-biz-video-player-card`;
|
|
const { showType, imgSrc, videoSrc, cardProps, isWindowLoading, errorReasonText, size, title, handleCloseButtonClick, handleWindowClick, windowKey, selectedWindowKey = '' } = props;
|
|
const [cardContent, setCardContent] = useState<JSX.Element | null>(null);
|
|
const { useToken } = theme
|
|
const { token } = useToken()
|
|
const videoRef = useRef<VideoViewRef>(null)
|
|
const selectedBorderStyle = {
|
|
border: `2px solid ${token.colorPrimary}`, boxShadow: " 0px 2px 9px 0px rgba(0,0,0,0.16)"
|
|
}
|
|
const cardStyle: React.CSSProperties = {
|
|
...(size === 'large' ? { height: 931 } : { height: 456, cursor: 'pointer' }),
|
|
...(size === 'small' && selectedWindowKey === windowKey ? selectedBorderStyle : {})
|
|
};
|
|
const videoPlayerCardStyle = size === 'small' ? { width: "calc(50% - 20px)" } : { flex: 1 }
|
|
|
|
useEffect(() => {
|
|
if (!isWindowLoading && (videoSrc || imgSrc)) {
|
|
let contentElement: JSX.Element | null = null;
|
|
if (videoSrc) {
|
|
contentElement = (
|
|
<VideoPlayer ref={videoRef} url={videoSrc} />
|
|
);
|
|
videoRef.current?.setShowCrop(true)
|
|
|
|
} else if (imgSrc) {
|
|
contentElement = (
|
|
<img
|
|
alt="首帧图"
|
|
src={imgSrc}
|
|
style={{ width: "100%", height: "100%", display: 'block' }}
|
|
/>
|
|
);
|
|
}
|
|
setCardContent(contentElement);
|
|
} else {
|
|
setCardContent(null)
|
|
}
|
|
}, [showType, imgSrc, videoSrc, isWindowLoading]);
|
|
|
|
return (
|
|
<div className={componentName} onClick={() => { handleWindowClick?.(windowKey) }} style={videoPlayerCardStyle}>
|
|
<Card
|
|
title={
|
|
<Space style={{ width: "100%", justifyContent: "space-between" }}>
|
|
<div>{title}</div>
|
|
<div className="card-close-button">
|
|
<Button type="text" onClick={() => { handleCloseButtonClick?.(windowKey) }} >
|
|
<CloseOutlined />
|
|
</Button>
|
|
</div>
|
|
</Space>}
|
|
style={{ display: "flex", flexDirection: "column", borderRadius: 4, overflow: "hidden", ...cardStyle }}
|
|
bodyStyle={{ flex: 1 }}
|
|
{...cardProps}
|
|
>
|
|
{cardContent ? (
|
|
<>
|
|
{cardContent}
|
|
</>
|
|
) : (
|
|
<div style={{ backgroundColor: '#000', height: '100%', display: 'flex', padding: '20px', boxSizing: 'border-box' }}>
|
|
{
|
|
isWindowLoading ?
|
|
<div style={{ flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} />} />
|
|
</div>
|
|
: !!errorReasonText && <span style={{ color: token.colorError }}>{errorReasonText}</span>
|
|
}
|
|
</div>
|
|
)}
|
|
{/* 其他内容 */}
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VideoPlayerCard;
|
|
|