💄 update video item active

This commit is contained in:
LittleBoy 2024-12-15 13:33:01 +08:00
parent 950bc59847
commit 2525358eb9
4 changed files with 198 additions and 112 deletions

View File

@ -105,12 +105,30 @@
}
}
}
.page-live{
.live-player{
max-height: calc(100vh - var(--app-header-header) - 130px);
overflow: hidden;
iframe{
width: 100%;
height: 100%;
overflow: hidden;
}
}
}
.video-item-shadow {
box-shadow: 0 0 6px 0 var(--tw-shadow-color);
//filter: drop-shadow(0 0 6px var(--tw-shadow-color));
}
.video-list-sort-container{
height: calc(100vh - var(--app-header-header) - 300px);
min-height: 300px;
max-height: calc(100vh - var(--app-header-header) - 300px);
overflow: auto;
}
}
.live-video-list-sort-container{
min-height: 300px;
padding-right: 10px;
max-height: calc(100vh - var(--app-header-header) - 200px);
overflow: auto;
}

View File

@ -20,6 +20,7 @@ type Props = {
onEdit?: () => void;
onRemove?: () => void;
id: number;
className?: string;
}
export const VideoListItem = (
@ -27,6 +28,7 @@ export const VideoListItem = (
// index,
id, video, onPlay, onRemove, checked,
onCheckedChange, onEdit, active, editable,
className,
}: Props) => {
const {
attributes, listeners,
@ -40,7 +42,7 @@ export const VideoListItem = (
}, [checked])
return <div
className={'video-item flex items-center gap-3 mb-5'}
className={`video-item flex items-center gap-3 ${className}`}
ref={setNodeRef} style={{transform: `translateY(${transform?.y || 0}px)`,}}>
{/*{index && index > 0 && <div className="flex items-center px-2">*/}
{/* <div className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index}</div>*/}

View File

@ -4,9 +4,11 @@ import {SortableContext, arrayMove} from '@dnd-kit/sortable';
import {DndContext} from "@dnd-kit/core";
import {VideoListItem} from "@/components/video/video-list-item.tsx";
import {getList} from "@/service/api/live.ts";
import {getList, playState} from "@/service/api/live.ts";
import styles from './style.module.scss'
import {set} from "lodash";
import {showToast} from "@/components/message.ts";
export default function LiveIndex() {
const [videoData, setVideoData] = useState<LiveVideoInfo[]>([])
@ -15,14 +17,55 @@ export default function LiveIndex() {
const [editable, setEditable] = useState<boolean>(false)
const [state, setState] = useState({
activeId: -1,
activeIndex: -1,
})
const showVideoItem = (index: number) => {
// 找到对应video item 并显示在视图可见区域
const container = document.querySelector('.live-video-list-sort-container')
const item = document.querySelector(`.list-item-${index}`)
if (item && container) {
// 获取容器数据
const containerRect = container.getBoundingClientRect()
// 获取对应item的数据
const rect = item.getBoundingClientRect()
// 计算对应item需要在容器中滚动的距离
const scrollDistance = rect.top - containerRect.top
// 设置滚动高度
container.scrollTo({
top: index == 0 ? 0 : container.scrollTop + scrollDistance - 10,
behavior: 'smooth'
})
}
}
const activeToNext = () => {
const endToFirst = state.activeIndex >= videoData.length - 1
const activeIndex = endToFirst ? 0 : state.activeIndex + 1
setState(() => {
return {
activeIndex
}
})
if (endToFirst) {
showToast('即将播放第一条视频');
}
// 找到对应video item 并显示在视图可见区域
showVideoItem(activeIndex)
}
const initPlayingState = (videoList?: LiveVideoInfo[] | null) => {
const list = videoList || videoData || []
playState().then(ret => {
setState({
activeIndex: 0 //list.findIndex(v => v.id === ret.id)
})
});
}
useEffect(() => {
getList().then(res => {
setVideoData([
res.list = [
{
id: 1,
id: 11,
video_id: 1,
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要',
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
@ -32,7 +75,7 @@ export default function LiveIndex() {
order_no: '1'
},
{
id: 2,
id: 0,
video_id: 1,
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要',
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
@ -42,7 +85,7 @@ export default function LiveIndex() {
order_no: '1'
},
{
id: 3,
id: 10,
video_id: 1,
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要',
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
@ -91,7 +134,10 @@ export default function LiveIndex() {
status: 1,
order_no: '1'
}
])
]
setVideoData(res.list || [])
initPlayingState(res.list || [])
})
}, [])
const processDeleteVideo = async (_idArray: number[]) => {
@ -109,26 +155,25 @@ export default function LiveIndex() {
title: '提示',
content: '是否采纳全部编辑操作?',
onOk: () => {
message.info('编辑成功!!!');
showToast('编辑成功!!!', 'info');
}
})
}
return (<div className="container py-10 page-live">
{contextHolder}
<div className="flex">
<div className="video-player-container mr-8 flex flex-col">
<div className="text-center text-base"></div>
<div className="video-player flex justify-center flex-1 mt-5">
<div className=" rounded overflow-hidden w-[360px] h-[700px]">
<iframe src="https://fm.gachafun.com/" className="border-0 w-full h-full max-h-full"></iframe>
<div className="live-player rounded overflow-hidden w-[360px] h-[700px]">
{/*<iframe src="https://fm.gachafun.com/" className="border-0 w-full h-full max-h-full"></iframe>*/}
</div>
</div>
</div>
<div className="video-list-container flex-1 ">
<div className=" bg-white py-8 px-6 rounded py-1">
<div className="live-control flex justify-between mb-8">
<div className="video-list-container flex-1">
<div className=" bg-white py-8 px-6 rounded">
<div className="live-control flex justify-between mb-4">
{editable ? <>
<div className="flex gap-2">
<Button type="primary" onClick={handleConfirm}></Button>
@ -140,48 +185,66 @@ export default function LiveIndex() {
</> : <div>
<Button type="primary" onClick={() => setEditable(true)}></Button>
</div>}
<div className="flex gap-2">
<Button type="primary" onClick={showFirst}>showFirst</Button>
<Button type="primary" onClick={activeToNext}>Next</Button>
</div>
</div>
<DndContext onDragEnd={(e) => {
const {active, over} = e;
if (over && active.id !== over.id) {
let oldIndex = -1, newIndex = -1;
const originArr = [...videoData]
setVideoData((items) => {
oldIndex = items.findIndex(s => s.id == active.id);
newIndex = items.findIndex(s => s.id == over.id);
return arrayMove(items, oldIndex, newIndex);
});
modal.confirm({
title: '提示',
content: '是否要移动到指定位置',
onCancel: () => {
setVideoData(originArr);
},
onOk: () => {
setVideoData([...videoData])
}
})
}
}}>
<SortableContext items={videoData}>
{videoData.map((v, index) => (
<VideoListItem
video={v}
index={index + 1}
id={v.id}
active={state.activeId == v.id}
key={index}
onCheckedChange={(checked) => {
setCheckedIdArray(idArray => {
return checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
<div className="live-video-list-sort-container">
<div className="flex">
<div className="sort-number-container mr-2">
{videoData.map((v, index) => (
<div key={index} className="flex items-center px-2 h-[80px] mt-3 mb-2">
<div
className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index + 1}</div>
</div>
))}
</div>
<div className="sort-list-container flex-1">
<DndContext onDragEnd={(e) => {
const {active, over} = e;
if (over && active.id !== over.id) {
let oldIndex = -1, newIndex = -1;
const originArr = [...videoData]
setVideoData((items) => {
oldIndex = items.findIndex(s => s.id == active.id);
newIndex = items.findIndex(s => s.id == over.id);
return arrayMove(items, oldIndex, newIndex);
});
modal.confirm({
title: '提示',
content: '是否要移动到指定位置',
onCancel: () => {
setVideoData(originArr);
},
onOk: () => {
setVideoData([...videoData])
}
})
}}
onRemove={() => processDeleteVideo([v.id])}
editable={editable}
/>))}
</SortableContext>
</DndContext>
}
}}>
<SortableContext items={videoData}>
{videoData.map((v, index) => (
<VideoListItem
video={v}
index={index + 1}
id={v.id}
active={state.activeIndex == index}
key={index}
className={`list-item-${index} mt-3 mb-2`}
onCheckedChange={(checked) => {
setCheckedIdArray(idArray => {
return checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
})
}}
onRemove={() => processDeleteVideo([v.id])}
editable={editable}
/>))}
</SortableContext>
</DndContext>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import {message, Modal} from "antd";
import {Empty, message, Modal} from "antd";
import React, {useEffect, useMemo, useRef, useState} from "react";
import {DndContext} from "@dnd-kit/core";
import {arrayMove, SortableContext} from "@dnd-kit/sortable";
@ -43,7 +43,7 @@ export default function VideoIndex() {
})
}
const playVideo = (video: VideoInfo,playingIndex:number) => {
const playVideo = (video: VideoInfo, playingIndex: number) => {
setState({
playingIndex
})
@ -88,58 +88,61 @@ export default function VideoIndex() {
</div>
</div>
<div className={'flex video-list-sort-container'}>
<div className="sort-number-container mr-2">
{videoData.map((v, index) => (
<div className="flex items-center px-2 h-[80px] mb-5">
<div className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index}</div>
</div>
))}
</div>
<div className="sort-list-container">
<DndContext onDragEnd={(e) => {
const {active, over} = e;
if (over && active.id !== over.id) {
let oldIndex = -1, newIndex = -1;
const originArr = [...videoData]
setVideoData((items) => {
oldIndex = items.findIndex(s => s.id == active.id);
newIndex = items.findIndex(s => s.id == over.id);
return arrayMove(items, oldIndex, newIndex);
});
modal.confirm({
title: '提示',
content: '是否要移动到指定位置',
onCancel: () => {
setVideoData(originArr);
}
})
}
}}>
<SortableContext items={videoData}>
{videoData.map((v, index) => (
<VideoListItem
video={v}
index={index + 1}
id={v.id}
key={index}
active={state.playingIndex == index}
checked={checkedIdArray.includes(v.id)}
onCheckedChange={(checked) => {
setCheckedIdArray(idArray => {
const newArr = checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
setState({checkedAll: newArr.length == videoData.length})
return newArr;
})
}}
onPlay={() => playVideo(v,index)}
onEdit={() => {
setEditId(v.article_id)
}}
editable
/>))}
</SortableContext>
</DndContext>
</div>
{videoData.length == 0 ? <div className="m-auto"><Empty/></div> : <>
<div className="sort-number-container mr-2">
{videoData.map((v, index) => (
<div key={index} className="flex items-center px-2 h-[80px] mb-5">
<div
className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index + 1}</div>
</div>
))}
</div>
<div className="sort-list-container">
<DndContext onDragEnd={(e) => {
const {active, over} = e;
if (over && active.id !== over.id) {
let oldIndex = -1, newIndex = -1;
const originArr = [...videoData]
setVideoData((items) => {
oldIndex = items.findIndex(s => s.id == active.id);
newIndex = items.findIndex(s => s.id == over.id);
return arrayMove(items, oldIndex, newIndex);
});
modal.confirm({
title: '提示',
content: '是否要移动到指定位置',
onCancel: () => {
setVideoData(originArr);
}
})
}
}}>
<SortableContext items={videoData}>
{videoData.map((v, index) => (
<VideoListItem
video={v}
index={index + 1}
id={v.id}
key={index}
active={state.playingIndex == index}
checked={checkedIdArray.includes(v.id)}
onCheckedChange={(checked) => {
setCheckedIdArray(idArray => {
const newArr = checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
setState({checkedAll: newArr.length == videoData.length})
return newArr;
})
}}
onPlay={() => playVideo(v, index)}
onEdit={() => {
setEditId(v.article_id)
}}
editable
/>))}
</SortableContext>
</DndContext>
</div>
</>}
</div>
<div className="text-right mt-10">
<ButtonPush2Room ids={checkedIdArray}/>