refactor(products): VideoPlayer 改用 useVideoPlayer Hook 消除重复代码(199→123行, -38%) #1163
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState, useEffect, useCallback } from "react"
|
||||
import React, { useEffect } from "react"
|
||||
import {
|
||||
VideoCameraOutlined,
|
||||
PlayCircleOutlined,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { Button } from "@/components/ui"
|
||||
import type { ProductItem } from "../types"
|
||||
import { formatTime, formatSize } from "../utils"
|
||||
import { useVideoPlayer } from "../hooks/useVideoPlayer"
|
||||
|
||||
interface VideoPlayerProps {
|
||||
product: ProductItem
|
||||
@@ -27,52 +28,19 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
onShare,
|
||||
onViewDetail,
|
||||
}) => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const progressRef = useRef<HTMLDivElement>(null)
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [duration, setDuration] = useState(product.duration)
|
||||
const {
|
||||
videoRef,
|
||||
progressRef,
|
||||
isPlaying,
|
||||
currentTime,
|
||||
duration,
|
||||
progress,
|
||||
togglePlay,
|
||||
handleSeek,
|
||||
} = useVideoPlayer()
|
||||
|
||||
const hasVideo = !!product.videoUrl
|
||||
|
||||
/** 播放/暂停 */
|
||||
const handlePlayPause = useCallback(() => {
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
if (isPlaying) {
|
||||
video.pause()
|
||||
} else {
|
||||
video.play().catch(() => {})
|
||||
}
|
||||
setIsPlaying(!isPlaying)
|
||||
}, [isPlaying])
|
||||
|
||||
/** 视频事件监听 */
|
||||
useEffect(() => {
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
const onTime = () => setCurrentTime(video.currentTime)
|
||||
const onDur = () => setDuration(video.duration || product.duration)
|
||||
const onEnd = () => setIsPlaying(false)
|
||||
video.addEventListener("timeupdate", onTime)
|
||||
video.addEventListener("loadedmetadata", onDur)
|
||||
video.addEventListener("ended", onEnd)
|
||||
return () => {
|
||||
video.removeEventListener("timeupdate", onTime)
|
||||
video.removeEventListener("loadedmetadata", onDur)
|
||||
video.removeEventListener("ended", onEnd)
|
||||
}
|
||||
}, [product.duration])
|
||||
|
||||
/** 进度条点击 */
|
||||
const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!progressRef.current) return
|
||||
const rect = progressRef.current.getBoundingClientRect()
|
||||
const percent = (e.clientX - rect.left) / rect.width
|
||||
const newTime = percent * duration
|
||||
setCurrentTime(newTime)
|
||||
if (videoRef.current) videoRef.current.currentTime = newTime
|
||||
}
|
||||
const displayDuration = duration || product.duration
|
||||
|
||||
/** ESC 关闭 */
|
||||
useEffect(() => {
|
||||
@@ -83,8 +51,6 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
return () => window.removeEventListener("keydown", handleKey)
|
||||
}, [onClose])
|
||||
|
||||
const progress = duration > 0 ? (currentTime / duration) * 100 : 0
|
||||
|
||||
return (
|
||||
<div className="xx-player-overlay" onClick={onClose}>
|
||||
<div className="xx-player-container" onClick={(e) => e.stopPropagation()}>
|
||||
@@ -114,7 +80,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
)}
|
||||
|
||||
{/* 播放/暂停按钮 */}
|
||||
<button className="xx-player-play-btn" onClick={handlePlayPause}>
|
||||
<button className="xx-player-play-btn" onClick={togglePlay}>
|
||||
{isPlaying ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
|
||||
</button>
|
||||
|
||||
@@ -125,12 +91,12 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
|
||||
{/* 进度条 */}
|
||||
<div className="xx-player-progress-wrap">
|
||||
<div ref={progressRef} className="xx-player-progress" onClick={handleProgressClick}>
|
||||
<div ref={progressRef} className="xx-player-progress" onClick={handleSeek}>
|
||||
<div className="xx-player-progress-bar" style={{ width: `${progress}%` }} />
|
||||
</div>
|
||||
<div className="xx-player-time">
|
||||
<span>{formatTime(currentTime)}</span>
|
||||
<span>{formatTime(duration)}</span>
|
||||
<span>{formatTime(displayDuration)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ import "@/pages/products/components/VideoPlayer"
|
||||
// Hooks
|
||||
import "@/pages/products/hooks/useProductList"
|
||||
import "@/pages/products/hooks/useProductActions"
|
||||
import "@/pages/products/hooks/useVideoPlayer"
|
||||
|
||||
describe("ProductLibrary module smoke test", () => {
|
||||
it("should load all product modules", () => {
|
||||
|
||||
Reference in New Issue
Block a user