Compare commits
No commits in common. "ff6e66d7521350cf89652de2839246e1bc400425" and "4935c0674ff7f27a3fc9f24be8b47bf641a3b3a5" have entirely different histories.
ff6e66d752
...
4935c0674f
@ -223,30 +223,6 @@
|
|||||||
//max-height: calc(100vh - var(--app-header-header) - 200px);
|
//max-height: calc(100vh - var(--app-header-header) - 200px);
|
||||||
//overflow: auto;
|
//overflow: auto;
|
||||||
}
|
}
|
||||||
.root-modal-confirm{
|
|
||||||
z-index: calc(var(--header-z-index) + 1) !important;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
.ant-modal-confirm-title{
|
|
||||||
font-size: 20px;
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
.ant-modal-confirm-content{
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: -30px;
|
|
||||||
}
|
|
||||||
.ant-modal-confirm-btns{
|
|
||||||
@apply mt-8;
|
|
||||||
button{
|
|
||||||
@apply rounded-2xl py-4 px-8;
|
|
||||||
}
|
|
||||||
.ant-btn-default{
|
|
||||||
@apply bg-white shadow-none text-popconfirm-btn-cancel border border-popconfirm-btn-cancel hover:border-popconfirm-btn-cancel hover:text-popconfirm-btn-cancel hover:bg-white hover:bg-popconfirm-btn-cancel/10;
|
|
||||||
}
|
|
||||||
.ant-btn-primary{
|
|
||||||
@apply bg-white shadow-none text-popconfirm-bg border border-popconfirm-bg hover:text-popconfirm-bg hover:bg-white hover:bg-popconfirm-btn-primary-hover/10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-player {
|
.video-player {
|
||||||
.video-js {
|
.video-js {
|
||||||
|
@ -3,7 +3,6 @@ import {Modal} from "antd";
|
|||||||
import {ButtonType} from "antd/es/button";
|
import {ButtonType} from "antd/es/button";
|
||||||
import {showErrorToast, showToast} from "@/components/message.ts";
|
import {showErrorToast, showToast} from "@/components/message.ts";
|
||||||
import {BizError} from "@/service/types.ts";
|
import {BizError} from "@/service/types.ts";
|
||||||
import {IconWarningCircle} from "@/components/icons";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
selected: any[],
|
selected: any[],
|
||||||
@ -48,7 +47,6 @@ export default function ButtonBatch(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
wrapClassName:'root-modal-confirm',
|
|
||||||
title: title || '操作提示',
|
title: title || '操作提示',
|
||||||
centered: true,
|
centered: true,
|
||||||
content: confirmMessage,
|
content: confirmMessage,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, {useEffect, useMemo, useRef} from "react";
|
import React, {useMemo, useRef} from "react";
|
||||||
import {Checkbox, Popover} from "antd";
|
import {Checkbox, Popover} from "antd";
|
||||||
import {useBoolean, useClickAway} from "ahooks";
|
import {useBoolean, useClickAway} from "ahooks";
|
||||||
import {CaretUpOutlined} from "@ant-design/icons";
|
import {CaretUpOutlined} from "@ant-design/icons";
|
||||||
@ -9,7 +9,6 @@ const TagSelect = (props: {
|
|||||||
options: OptionItem[];
|
options: OptionItem[];
|
||||||
onChange: (values: Id[][]) => void;
|
onChange: (values: Id[][]) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
defaultSelectTags?: Id[][];
|
|
||||||
}) => {
|
}) => {
|
||||||
const [selectValues, __setSelectValues] = React.useState<ValueType>([])
|
const [selectValues, __setSelectValues] = React.useState<ValueType>([])
|
||||||
const [checkedAll, _setCheckedAll] = React.useState<boolean>(false)
|
const [checkedAll, _setCheckedAll] = React.useState<boolean>(false)
|
||||||
@ -105,12 +104,6 @@ const TagSelect = (props: {
|
|||||||
useClickAway(()=>{
|
useClickAway(()=>{
|
||||||
set(false)
|
set(false)
|
||||||
},ref)
|
},ref)
|
||||||
useEffect(()=>{
|
|
||||||
if(props.defaultSelectTags){
|
|
||||||
__setSelectValues(props.defaultSelectTags)
|
|
||||||
}
|
|
||||||
//console.log('props.defaultSelectTags',props.defaultSelectTags)
|
|
||||||
},[props.defaultSelectTags])
|
|
||||||
|
|
||||||
return (<div ref={ref} className={`tag-select-container z-10 select-none relative group ${props.className}`}>
|
return (<div ref={ref} className={`tag-select-container z-10 select-none relative group ${props.className}`}>
|
||||||
<div className="select-value w-[120px] flex justify-center items-center"
|
<div className="select-value w-[120px] flex justify-center items-center"
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// import {PauseOutlined, PlayCircleOutlined, FullscreenOutlined, FullscreenExitOutlined} from "@ant-design/icons"
|
// import {PauseOutlined, PlayCircleOutlined, FullscreenOutlined, FullscreenExitOutlined} from "@ant-design/icons"
|
||||||
// import {Progress} from "antd";
|
// import {Progress} from "antd";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {PlayerInstance} from "@/hooks/useCache.ts";
|
|
||||||
import TCPlayer from 'tcplayer.js';
|
import TCPlayer from 'tcplayer.js';
|
||||||
import 'tcplayer.js/dist/tcplayer.min.css';
|
import 'tcplayer.js/dist/tcplayer.min.css';
|
||||||
|
|
||||||
@ -59,10 +58,6 @@ export const Player = React.forwardRef<PlayerInstance, Props>((props, ref) => {
|
|||||||
},[props.url, tcPlayer])
|
},[props.url, tcPlayer])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(PlayerInstance.length != 0){
|
|
||||||
PlayerInstance.forEach(player => player.pause())
|
|
||||||
PlayerInstance.length = 0
|
|
||||||
}
|
|
||||||
const playerVideo = document.createElement('video');
|
const playerVideo = document.createElement('video');
|
||||||
const playerId = `player-container-${Date.now().toString(16)}`;
|
const playerId = `player-container-${Date.now().toString(16)}`;
|
||||||
playerVideo.setAttribute('id', playerId)
|
playerVideo.setAttribute('id', playerId)
|
||||||
@ -70,8 +65,6 @@ export const Player = React.forwardRef<PlayerInstance, Props>((props, ref) => {
|
|||||||
playerVideo.setAttribute('playsInline', 'true')
|
playerVideo.setAttribute('playsInline', 'true')
|
||||||
playerVideo.setAttribute('webkit-playsinline', 'true')
|
playerVideo.setAttribute('webkit-playsinline', 'true')
|
||||||
if(props.className) playerVideo.setAttribute('className', props.className)
|
if(props.className) playerVideo.setAttribute('className', props.className)
|
||||||
playerVideo.classList.add('digital-video-player')
|
|
||||||
PlayerInstance.push(playerVideo)
|
|
||||||
document.querySelector('.video-player-container-inner')!.appendChild(playerVideo)
|
document.querySelector('.video-player-container-inner')!.appendChild(playerVideo)
|
||||||
|
|
||||||
const player = TCPlayer(playerId, {
|
const player = TCPlayer(playerId, {
|
||||||
|
@ -107,8 +107,8 @@ export const VideoListItem = (
|
|||||||
placement={'left'}
|
placement={'left'}
|
||||||
arrow={false}
|
arrow={false}
|
||||||
icon={<IconWarningCircle/>}
|
icon={<IconWarningCircle/>}
|
||||||
title={'你确定要删除此视频吗?'}
|
title={'你确定要删除吗?'}
|
||||||
// description={`删除后需从重新${type == 'create' ? '生成' : '推流'}`}
|
description={`删除后需从重新${type == 'create' ? '生成' : '推流'}`}
|
||||||
onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
><button className="hover:text-blue-500"><IconDelete/></button></Popconfirm>}
|
><button className="hover:text-blue-500"><IconDelete/></button></Popconfirm>}
|
||||||
<Checkbox checked={state.checked} onChange={() => {
|
<Checkbox checked={state.checked} onChange={() => {
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import {create} from "zustand"
|
import {create} from "zustand"
|
||||||
|
|
||||||
export const PlayerInstance: HTMLVideoElement[] = [];
|
|
||||||
export const defaultCache:{
|
|
||||||
firstLoadPath?: string
|
|
||||||
} = {};
|
|
||||||
type StoreInstance<T> = {
|
type StoreInstance<T> = {
|
||||||
cache: T[];
|
cache: T[];
|
||||||
clear: () => void;
|
clear: () => void;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, {useEffect, useMemo, useRef, useState} from "react";
|
import React, {useEffect, useMemo, useRef, useState} from "react";
|
||||||
import {Checkbox, Empty, Modal} from "antd";
|
import {Checkbox, Modal} from "antd";
|
||||||
import {SortableContext, arrayMove} from '@dnd-kit/sortable';
|
import {SortableContext, arrayMove} from '@dnd-kit/sortable';
|
||||||
import {DndContext} from "@dnd-kit/core";
|
import {DndContext} from "@dnd-kit/core";
|
||||||
|
|
||||||
@ -130,14 +130,11 @@ export default function LiveIndex() {
|
|||||||
loadList()
|
loadList()
|
||||||
return ()=>{
|
return ()=>{
|
||||||
clearAllTimer();
|
clearAllTimer();
|
||||||
setTimeout(()=>{
|
try{
|
||||||
console.log('pause all video')
|
Array.from(document.querySelectorAll('video')).forEach(v => v.pause())
|
||||||
try{
|
}catch (e){
|
||||||
Array.from(document.querySelectorAll('video')).forEach(v => v.pause())
|
console.log(e)
|
||||||
}catch (e){
|
}
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
},20)
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -206,6 +203,7 @@ export default function LiveIndex() {
|
|||||||
}, [checkedIdArray, state.activeIndex])
|
}, [checkedIdArray, state.activeIndex])
|
||||||
|
|
||||||
return (<div className="container py-5 page-live">
|
return (<div className="container py-5 page-live">
|
||||||
|
{contextHolder}
|
||||||
<div className="h-[36px]"></div>
|
<div className="h-[36px]"></div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="video-player-container mr-16 flex items-center">
|
<div className="video-player-container mr-16 flex items-center">
|
||||||
@ -228,27 +226,26 @@ export default function LiveIndex() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="video-list-container video-list-sort-container flex flex-col flex-1 mt-2">
|
|
||||||
|
<div className="video-list-container video-list-sort-container flex-1 mt-1">
|
||||||
<div className="live-control flex justify-between mb-1">
|
<div className="live-control flex justify-between mb-1">
|
||||||
<div>
|
<div className="text-sm">
|
||||||
<div className="text-sm">
|
<span>当前{state.activeIndex == -1 ? '暂未播放' : `播放到${state.activeIndex}条`},</span>
|
||||||
<span>当前{state.activeIndex == -1 ? '暂未播放' : `播放到${state.activeIndex}条`},</span>
|
<span>共{videoData.length}条</span>
|
||||||
<span>共{videoData.length}条</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex gap-2 items-center text-sm">
|
||||||
<div className={'flex items-center text-gray-400 cursor-pointer select-none'}
|
<div className={'flex items-center text-gray-400 cursor-pointer select-none'}
|
||||||
onClick={handleConfirm}>
|
onClick={handleConfirm}>
|
||||||
<span>{editable ? '已解锁' : '锁定状态不可排序'}</span>
|
<span>{editable ? '已解锁' : '锁定状态不可排序'}</span>
|
||||||
<span className="ml-2 text-sm">
|
<span className="ml-2">
|
||||||
{editable ? <IconUnlock/> : <IconLocked/>}
|
{editable ? <IconUnlock/> : <IconLocked/>}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="check-all ml-10">
|
<div className="check-all ml-10">
|
||||||
<button className="hover:text-blue-300 text-gray-400"
|
<button className="hover:text-blue-300 text-gray-400 text-lg"
|
||||||
onClick={handleAllCheckedChange}>
|
onClick={handleAllCheckedChange}>
|
||||||
<span className="text-sm mr-2 whitespace-nowrap">全选</span>
|
<span className="text-sm mr-2">全选</span>
|
||||||
{/*<CheckCircleFilled className={clsx({'text-blue-500': state.checkedAll})}/>*/}
|
{/*<CheckCircleFilled className={clsx({'text-blue-500': state.checkedAll})}/>*/}
|
||||||
</button>
|
</button>
|
||||||
<Checkbox checked={state.checkedAll} onChange={() => handleAllCheckedChange()}/>
|
<Checkbox checked={state.checkedAll} onChange={() => handleAllCheckedChange()}/>
|
||||||
@ -273,7 +270,6 @@ export default function LiveIndex() {
|
|||||||
onCallback={() => {
|
onCallback={() => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoData.length == 0 && <div className="m-auto py-16"><Empty/></div>}
|
|
||||||
<div className="sort-list-container flex-1">
|
<div className="sort-list-container flex-1">
|
||||||
<DndContext onDragEnd={(e) => {
|
<DndContext onDragEnd={(e) => {
|
||||||
const {active, over} = e;
|
const {active, over} = e;
|
||||||
@ -331,6 +327,5 @@ export default function LiveIndex() {
|
|||||||
<IconDelete/>
|
<IconDelete/>
|
||||||
</ButtonBatch>}
|
</ButtonBatch>}
|
||||||
</div>
|
</div>
|
||||||
{contextHolder}
|
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
@ -25,7 +25,6 @@ export default function ButtonDeleteBatch(props: { ids: Id[];onSuccess?: () => v
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
wrapClassName:'root-modal-confirm',
|
|
||||||
title: `你确定要删除选择的 ${props.ids.length} 条新闻吗?`,
|
title: `你确定要删除选择的 ${props.ids.length} 条新闻吗?`,
|
||||||
content: '删除后需从新闻素材中重新选择',
|
content: '删除后需从新闻素材中重新选择',
|
||||||
onOk: handlePush,
|
onOk: handlePush,
|
||||||
|
@ -47,7 +47,7 @@ export default function EditSearchForm(props: {
|
|||||||
if(!defaultParams){
|
if(!defaultParams){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tags:Id[][] = []
|
const tags = []
|
||||||
|
|
||||||
if(defaultParams.tags){
|
if(defaultParams.tags){
|
||||||
defaultParams.tags.forEach(it=>{
|
defaultParams.tags.forEach(it=>{
|
||||||
@ -79,7 +79,7 @@ export default function EditSearchForm(props: {
|
|||||||
{/* options={articleTags}*/}
|
{/* options={articleTags}*/}
|
||||||
{/* onChange={setTags}*/}
|
{/* onChange={setTags}*/}
|
||||||
{/*/>*/}
|
{/*/>*/}
|
||||||
<TagSelect defaultSelectTags={tags} onChange={setTags} options={articleTags}/>
|
<TagSelect onChange={setTags} options={articleTags}/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -145,7 +145,7 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
|
|||||||
<Input
|
<Input
|
||||||
value={prevSearchName}
|
value={prevSearchName}
|
||||||
onChange={e => setPrevSearchName(e.target.value)}
|
onChange={e => setPrevSearchName(e.target.value)}
|
||||||
className="w-[270px] rounded-3xl"
|
className="w-[250px] rounded-3xl"
|
||||||
placeholder={'请输入新闻标题关键词进行搜索'}
|
placeholder={'请输入新闻标题关键词进行搜索'}
|
||||||
onPressEnter={onFinish}
|
onPressEnter={onFinish}
|
||||||
onBlur={onFinish}
|
onBlur={onFinish}
|
||||||
|
@ -180,7 +180,7 @@ export default function VideoIndex() {
|
|||||||
</div>
|
</div>
|
||||||
<InfiniteScroller loading={state.loading} ref={scrollerRef} onScroll={top => setState({showToTop: top > 30})}>
|
<InfiniteScroller loading={state.loading} ref={scrollerRef} onScroll={top => setState({showToTop: top > 30})}>
|
||||||
{
|
{
|
||||||
videoData.length == 0 ? <div className="m-auto py-16"><Empty/></div> :
|
videoData.length == 0 ? <div className="m-auto"><Empty/></div> :
|
||||||
<div className="sort-list-container flex-1">
|
<div className="sort-list-container flex-1">
|
||||||
<DndContext onDragEnd={(e) => {
|
<DndContext onDragEnd={(e) => {
|
||||||
const {active, over} = e;
|
const {active, over} = e;
|
||||||
@ -248,7 +248,7 @@ export default function VideoIndex() {
|
|||||||
emptyMessage={`请选择要删除的新闻视频`}
|
emptyMessage={`请选择要删除的新闻视频`}
|
||||||
title={`已选择${checkedIdArray.length}条,确定要全部删除吗?`}
|
title={`已选择${checkedIdArray.length}条,确定要全部删除吗?`}
|
||||||
className='bg-gray-300 hover:bg-gray-400 text-white'
|
className='bg-gray-300 hover:bg-gray-400 text-white'
|
||||||
confirmMessage={`删除后需重新生成视频`}
|
confirmMessage={`删除后需从新闻素材中`}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
showToast('删除成功!', 'success')
|
showToast('删除成功!', 'success')
|
||||||
loadList()
|
loadList()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Outlet, useLocation, useNavigate} from "react-router-dom";
|
import {Outlet, useNavigate} from "react-router-dom";
|
||||||
import {Dropdown, MenuProps} from "antd";
|
import {Dropdown, MenuProps} from "antd";
|
||||||
import React, {useEffect} from "react";
|
import React from "react";
|
||||||
|
|
||||||
import AuthGuard from "@/routes/layout/auth-guard.tsx";
|
import AuthGuard from "@/routes/layout/auth-guard.tsx";
|
||||||
import {LogoText} from "@/components/icons/logo.tsx";
|
import {LogoText} from "@/components/icons/logo.tsx";
|
||||||
@ -10,7 +10,6 @@ import {DashboardNavigation} from "@/routes/layout/dashboard-navigation.tsx";
|
|||||||
|
|
||||||
import useAuth from "@/hooks/useAuth.ts";
|
import useAuth from "@/hooks/useAuth.ts";
|
||||||
import {hidePhone} from "@/util/strings.ts";
|
import {hidePhone} from "@/util/strings.ts";
|
||||||
import {defaultCache} from "@/hooks/useCache.ts";
|
|
||||||
|
|
||||||
|
|
||||||
type LayoutProps = {
|
type LayoutProps = {
|
||||||
@ -71,16 +70,7 @@ export const BaseLayout: React.FC<LayoutProps> = ({children}) => {
|
|||||||
|
|
||||||
|
|
||||||
const DashboardLayout: React.FC<{ children?: React.ReactNode }> = ({children}) => {
|
const DashboardLayout: React.FC<{ children?: React.ReactNode }> = ({children}) => {
|
||||||
const loc = useLocation()
|
|
||||||
const navigate = useNavigate()
|
|
||||||
useEffect(()=>{
|
|
||||||
if(!defaultCache.firstLoadPath && loc.pathname == '/live'){
|
|
||||||
defaultCache.firstLoadPath = loc.pathname;
|
|
||||||
navigate('/')
|
|
||||||
}
|
|
||||||
},[])
|
|
||||||
return <AuthGuard>
|
return <AuthGuard>
|
||||||
<div className="fixed">first path:{defaultCache.firstLoadPath}</div>
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
{children ? children : <Outlet/>}
|
{children ? children : <Outlet/>}
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user