💄 update video item active
This commit is contained in:
parent
950bc59847
commit
2525358eb9
@ -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 {
|
.video-item-shadow {
|
||||||
box-shadow: 0 0 6px 0 var(--tw-shadow-color);
|
box-shadow: 0 0 6px 0 var(--tw-shadow-color);
|
||||||
//filter: drop-shadow(0 0 6px var(--tw-shadow-color));
|
//filter: drop-shadow(0 0 6px var(--tw-shadow-color));
|
||||||
}
|
}
|
||||||
.video-list-sort-container{
|
.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;
|
overflow: auto;
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ type Props = {
|
|||||||
onEdit?: () => void;
|
onEdit?: () => void;
|
||||||
onRemove?: () => void;
|
onRemove?: () => void;
|
||||||
id: number;
|
id: number;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoListItem = (
|
export const VideoListItem = (
|
||||||
@ -27,6 +28,7 @@ export const VideoListItem = (
|
|||||||
// index,
|
// index,
|
||||||
id, video, onPlay, onRemove, checked,
|
id, video, onPlay, onRemove, checked,
|
||||||
onCheckedChange, onEdit, active, editable,
|
onCheckedChange, onEdit, active, editable,
|
||||||
|
className,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
attributes, listeners,
|
attributes, listeners,
|
||||||
@ -40,7 +42,7 @@ export const VideoListItem = (
|
|||||||
}, [checked])
|
}, [checked])
|
||||||
|
|
||||||
return <div
|
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)`,}}>
|
ref={setNodeRef} style={{transform: `translateY(${transform?.y || 0}px)`,}}>
|
||||||
{/*{index && index > 0 && <div className="flex items-center px-2">*/}
|
{/*{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>*/}
|
{/* <div className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index}</div>*/}
|
||||||
|
@ -4,9 +4,11 @@ import {SortableContext, arrayMove} from '@dnd-kit/sortable';
|
|||||||
import {DndContext} from "@dnd-kit/core";
|
import {DndContext} from "@dnd-kit/core";
|
||||||
|
|
||||||
import {VideoListItem} from "@/components/video/video-list-item.tsx";
|
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 styles from './style.module.scss'
|
||||||
|
import {set} from "lodash";
|
||||||
|
import {showToast} from "@/components/message.ts";
|
||||||
|
|
||||||
export default function LiveIndex() {
|
export default function LiveIndex() {
|
||||||
const [videoData, setVideoData] = useState<LiveVideoInfo[]>([])
|
const [videoData, setVideoData] = useState<LiveVideoInfo[]>([])
|
||||||
@ -15,14 +17,55 @@ export default function LiveIndex() {
|
|||||||
const [editable, setEditable] = useState<boolean>(false)
|
const [editable, setEditable] = useState<boolean>(false)
|
||||||
|
|
||||||
const [state, setState] = useState({
|
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(() => {
|
useEffect(() => {
|
||||||
getList().then(res => {
|
getList().then(res => {
|
||||||
setVideoData([
|
res.list = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 11,
|
||||||
video_id: 1,
|
video_id: 1,
|
||||||
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
||||||
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
|
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'
|
order_no: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 0,
|
||||||
video_id: 1,
|
video_id: 1,
|
||||||
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
||||||
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
|
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'
|
order_no: '1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 10,
|
||||||
video_id: 1,
|
video_id: 1,
|
||||||
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
video_title: '333专家分析丨叙土边境曼比季地理位置为何如此重要?',
|
||||||
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
|
cover_url: 'https://gachafun.oss-cn-beijing.aliyuncs.com/2021/08/13/1628840269744.jpg',
|
||||||
@ -91,7 +134,10 @@ export default function LiveIndex() {
|
|||||||
status: 1,
|
status: 1,
|
||||||
order_no: '1'
|
order_no: '1'
|
||||||
}
|
}
|
||||||
])
|
|
||||||
|
]
|
||||||
|
setVideoData(res.list || [])
|
||||||
|
initPlayingState(res.list || [])
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
const processDeleteVideo = async (_idArray: number[]) => {
|
const processDeleteVideo = async (_idArray: number[]) => {
|
||||||
@ -109,26 +155,25 @@ export default function LiveIndex() {
|
|||||||
title: '提示',
|
title: '提示',
|
||||||
content: '是否采纳全部编辑操作?',
|
content: '是否采纳全部编辑操作?',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
message.info('编辑成功!!!');
|
showToast('编辑成功!!!', 'info');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (<div className="container py-10 page-live">
|
return (<div className="container py-10 page-live">
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="video-player-container mr-8 flex flex-col">
|
<div className="video-player-container mr-8 flex flex-col">
|
||||||
<div className="text-center text-base">数字人直播间</div>
|
<div className="text-center text-base">数字人直播间</div>
|
||||||
<div className="video-player flex justify-center flex-1 mt-5">
|
<div className="video-player flex justify-center flex-1 mt-5">
|
||||||
<div className=" rounded overflow-hidden w-[360px] h-[700px]">
|
<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>
|
{/*<iframe src="https://fm.gachafun.com/" className="border-0 w-full h-full max-h-full"></iframe>*/}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="video-list-container flex-1 ">
|
<div className="video-list-container flex-1">
|
||||||
<div className=" bg-white py-8 px-6 rounded py-1">
|
<div className=" bg-white py-8 px-6 rounded">
|
||||||
<div className="live-control flex justify-between mb-8">
|
<div className="live-control flex justify-between mb-4">
|
||||||
{editable ? <>
|
{editable ? <>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button type="primary" onClick={handleConfirm}>确定</Button>
|
<Button type="primary" onClick={handleConfirm}>确定</Button>
|
||||||
@ -140,48 +185,66 @@ export default function LiveIndex() {
|
|||||||
</> : <div>
|
</> : <div>
|
||||||
<Button type="primary" onClick={() => setEditable(true)}>编辑</Button>
|
<Button type="primary" onClick={() => setEditable(true)}>编辑</Button>
|
||||||
</div>}
|
</div>}
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button type="primary" onClick={showFirst}>showFirst</Button>
|
||||||
|
<Button type="primary" onClick={activeToNext}>Next</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DndContext onDragEnd={(e) => {
|
<div className="live-video-list-sort-container">
|
||||||
const {active, over} = e;
|
<div className="flex">
|
||||||
if (over && active.id !== over.id) {
|
<div className="sort-number-container mr-2">
|
||||||
let oldIndex = -1, newIndex = -1;
|
{videoData.map((v, index) => (
|
||||||
const originArr = [...videoData]
|
<div key={index} className="flex items-center px-2 h-[80px] mt-3 mb-2">
|
||||||
setVideoData((items) => {
|
<div
|
||||||
oldIndex = items.findIndex(s => s.id == active.id);
|
className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index + 1}</div>
|
||||||
newIndex = items.findIndex(s => s.id == over.id);
|
</div>
|
||||||
return arrayMove(items, oldIndex, newIndex);
|
))}
|
||||||
});
|
</div>
|
||||||
modal.confirm({
|
<div className="sort-list-container flex-1">
|
||||||
title: '提示',
|
<DndContext onDragEnd={(e) => {
|
||||||
content: '是否要移动到指定位置',
|
const {active, over} = e;
|
||||||
onCancel: () => {
|
if (over && active.id !== over.id) {
|
||||||
setVideoData(originArr);
|
let oldIndex = -1, newIndex = -1;
|
||||||
},
|
const originArr = [...videoData]
|
||||||
onOk: () => {
|
setVideoData((items) => {
|
||||||
setVideoData([...videoData])
|
oldIndex = items.findIndex(s => s.id == active.id);
|
||||||
}
|
newIndex = items.findIndex(s => s.id == over.id);
|
||||||
})
|
return arrayMove(items, oldIndex, newIndex);
|
||||||
}
|
});
|
||||||
}}>
|
modal.confirm({
|
||||||
<SortableContext items={videoData}>
|
title: '提示',
|
||||||
{videoData.map((v, index) => (
|
content: '是否要移动到指定位置',
|
||||||
<VideoListItem
|
onCancel: () => {
|
||||||
video={v}
|
setVideoData(originArr);
|
||||||
index={index + 1}
|
},
|
||||||
id={v.id}
|
onOk: () => {
|
||||||
active={state.activeId == v.id}
|
setVideoData([...videoData])
|
||||||
key={index}
|
}
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
setCheckedIdArray(idArray => {
|
|
||||||
return checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
|
|
||||||
})
|
})
|
||||||
}}
|
}
|
||||||
onRemove={() => processDeleteVideo([v.id])}
|
}}>
|
||||||
editable={editable}
|
<SortableContext items={videoData}>
|
||||||
/>))}
|
{videoData.map((v, index) => (
|
||||||
</SortableContext>
|
<VideoListItem
|
||||||
</DndContext>
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {message, Modal} from "antd";
|
import {Empty, message, Modal} from "antd";
|
||||||
import React, {useEffect, useMemo, useRef, useState} from "react";
|
import React, {useEffect, useMemo, useRef, useState} from "react";
|
||||||
import {DndContext} from "@dnd-kit/core";
|
import {DndContext} from "@dnd-kit/core";
|
||||||
import {arrayMove, SortableContext} from "@dnd-kit/sortable";
|
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({
|
setState({
|
||||||
playingIndex
|
playingIndex
|
||||||
})
|
})
|
||||||
@ -88,58 +88,61 @@ export default function VideoIndex() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex video-list-sort-container'}>
|
<div className={'flex video-list-sort-container'}>
|
||||||
<div className="sort-number-container mr-2">
|
{videoData.length == 0 ? <div className="m-auto"><Empty/></div> : <>
|
||||||
{videoData.map((v, index) => (
|
<div className="sort-number-container mr-2">
|
||||||
<div className="flex items-center px-2 h-[80px] mb-5">
|
{videoData.map((v, index) => (
|
||||||
<div className="index-value w-[40px] h-[40px] flex items-center justify-center bg-gray-100 rounded-3xl">{index}</div>
|
<div key={index} className="flex items-center px-2 h-[80px] mb-5">
|
||||||
</div>
|
<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) => {
|
</div>
|
||||||
const {active, over} = e;
|
<div className="sort-list-container">
|
||||||
if (over && active.id !== over.id) {
|
<DndContext onDragEnd={(e) => {
|
||||||
let oldIndex = -1, newIndex = -1;
|
const {active, over} = e;
|
||||||
const originArr = [...videoData]
|
if (over && active.id !== over.id) {
|
||||||
setVideoData((items) => {
|
let oldIndex = -1, newIndex = -1;
|
||||||
oldIndex = items.findIndex(s => s.id == active.id);
|
const originArr = [...videoData]
|
||||||
newIndex = items.findIndex(s => s.id == over.id);
|
setVideoData((items) => {
|
||||||
return arrayMove(items, oldIndex, newIndex);
|
oldIndex = items.findIndex(s => s.id == active.id);
|
||||||
});
|
newIndex = items.findIndex(s => s.id == over.id);
|
||||||
modal.confirm({
|
return arrayMove(items, oldIndex, newIndex);
|
||||||
title: '提示',
|
});
|
||||||
content: '是否要移动到指定位置',
|
modal.confirm({
|
||||||
onCancel: () => {
|
title: '提示',
|
||||||
setVideoData(originArr);
|
content: '是否要移动到指定位置',
|
||||||
}
|
onCancel: () => {
|
||||||
})
|
setVideoData(originArr);
|
||||||
}
|
}
|
||||||
}}>
|
})
|
||||||
<SortableContext items={videoData}>
|
}
|
||||||
{videoData.map((v, index) => (
|
}}>
|
||||||
<VideoListItem
|
<SortableContext items={videoData}>
|
||||||
video={v}
|
{videoData.map((v, index) => (
|
||||||
index={index + 1}
|
<VideoListItem
|
||||||
id={v.id}
|
video={v}
|
||||||
key={index}
|
index={index + 1}
|
||||||
active={state.playingIndex == index}
|
id={v.id}
|
||||||
checked={checkedIdArray.includes(v.id)}
|
key={index}
|
||||||
onCheckedChange={(checked) => {
|
active={state.playingIndex == index}
|
||||||
setCheckedIdArray(idArray => {
|
checked={checkedIdArray.includes(v.id)}
|
||||||
const newArr = checked ? idArray.concat(v.id) : idArray.filter(id => id != v.id);
|
onCheckedChange={(checked) => {
|
||||||
setState({checkedAll: newArr.length == videoData.length})
|
setCheckedIdArray(idArray => {
|
||||||
return newArr;
|
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)
|
onPlay={() => playVideo(v, index)}
|
||||||
}}
|
onEdit={() => {
|
||||||
editable
|
setEditId(v.article_id)
|
||||||
/>))}
|
}}
|
||||||
</SortableContext>
|
editable
|
||||||
</DndContext>
|
/>))}
|
||||||
</div>
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right mt-10">
|
<div className="text-right mt-10">
|
||||||
<ButtonPush2Room ids={checkedIdArray}/>
|
<ButtonPush2Room ids={checkedIdArray}/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user