Compare commits

...

4 Commits

Author SHA1 Message Date
ff6e66d752 fixed confirm modal style 2024-12-24 19:28:14 +08:00
eecd7b766f fixed 直播间因为字体大小问题导致无法对齐 2024-12-24 18:47:53 +08:00
aa456d2b18 update 2024-12-24 17:11:51 +08:00
258900db12 fixed ts build error 2024-12-24 15:31:18 +08:00
12 changed files with 86 additions and 27 deletions

View File

@ -223,6 +223,30 @@
//max-height: calc(100vh - var(--app-header-header) - 200px);
//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-js {

View File

@ -3,6 +3,7 @@ import {Modal} from "antd";
import {ButtonType} from "antd/es/button";
import {showErrorToast, showToast} from "@/components/message.ts";
import {BizError} from "@/service/types.ts";
import {IconWarningCircle} from "@/components/icons";
type Props = {
selected: any[],
@ -47,6 +48,7 @@ export default function ButtonBatch(
return;
}
Modal.confirm({
wrapClassName:'root-modal-confirm',
title: title || '操作提示',
centered: true,
content: confirmMessage,

View File

@ -1,4 +1,4 @@
import React, {useMemo, useRef} from "react";
import React, {useEffect, useMemo, useRef} from "react";
import {Checkbox, Popover} from "antd";
import {useBoolean, useClickAway} from "ahooks";
import {CaretUpOutlined} from "@ant-design/icons";
@ -9,6 +9,7 @@ const TagSelect = (props: {
options: OptionItem[];
onChange: (values: Id[][]) => void;
className?: string;
defaultSelectTags?: Id[][];
}) => {
const [selectValues, __setSelectValues] = React.useState<ValueType>([])
const [checkedAll, _setCheckedAll] = React.useState<boolean>(false)
@ -104,6 +105,12 @@ const TagSelect = (props: {
useClickAway(()=>{
set(false)
},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}`}>
<div className="select-value w-[120px] flex justify-center items-center"

View File

@ -2,6 +2,7 @@
// import {PauseOutlined, PlayCircleOutlined, FullscreenOutlined, FullscreenExitOutlined} from "@ant-design/icons"
// import {Progress} from "antd";
import React, {useEffect, useState} from "react";
import {PlayerInstance} from "@/hooks/useCache.ts";
import TCPlayer from 'tcplayer.js';
import 'tcplayer.js/dist/tcplayer.min.css';
@ -58,6 +59,10 @@ export const Player = React.forwardRef<PlayerInstance, Props>((props, ref) => {
},[props.url, tcPlayer])
useEffect(() => {
if(PlayerInstance.length != 0){
PlayerInstance.forEach(player => player.pause())
PlayerInstance.length = 0
}
const playerVideo = document.createElement('video');
const playerId = `player-container-${Date.now().toString(16)}`;
playerVideo.setAttribute('id', playerId)
@ -65,6 +70,8 @@ export const Player = React.forwardRef<PlayerInstance, Props>((props, ref) => {
playerVideo.setAttribute('playsInline', 'true')
playerVideo.setAttribute('webkit-playsinline', 'true')
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)
const player = TCPlayer(playerId, {

View File

@ -107,8 +107,8 @@ export const VideoListItem = (
placement={'left'}
arrow={false}
icon={<IconWarningCircle/>}
title={'你确定要删除吗?'}
description={`删除后需从重新${type == 'create' ? '生成' : '推流'}`}
title={'你确定要删除此视频吗?'}
// description={`删除后需从重新${type == 'create' ? '生成' : '推流'}`}
onConfirm={onRemove}
><button className="hover:text-blue-500"><IconDelete/></button></Popconfirm>}
<Checkbox checked={state.checked} onChange={() => {

View File

@ -1,6 +1,9 @@
import {create} from "zustand"
export const PlayerInstance: HTMLVideoElement[] = [];
export const defaultCache:{
firstLoadPath?: string
} = {};
type StoreInstance<T> = {
cache: T[];
clear: () => void;

View File

@ -1,5 +1,5 @@
import React, {useEffect, useMemo, useRef, useState} from "react";
import {Checkbox, Modal} from "antd";
import {Checkbox, Empty, Modal} from "antd";
import {SortableContext, arrayMove} from '@dnd-kit/sortable';
import {DndContext} from "@dnd-kit/core";
@ -130,11 +130,14 @@ export default function LiveIndex() {
loadList()
return ()=>{
clearAllTimer();
setTimeout(()=>{
console.log('pause all video')
try{
Array.from(document.querySelectorAll('video')).forEach(v => v.pause())
}catch (e){
console.log(e)
}
},20)
}
}, [])
@ -203,7 +206,6 @@ export default function LiveIndex() {
}, [checkedIdArray, state.activeIndex])
return (<div className="container py-5 page-live">
{contextHolder}
<div className="h-[36px]"></div>
<div className="flex">
<div className="video-player-container mr-16 flex items-center">
@ -226,26 +228,27 @@ export default function LiveIndex() {
</div>
</div>
</div>
<div className="video-list-container video-list-sort-container flex-1 mt-1">
<div className="video-list-container video-list-sort-container flex flex-col flex-1 mt-2">
<div className="live-control flex justify-between mb-1">
<div>
<div className="text-sm">
<span>{state.activeIndex == -1 ? '暂未播放' : `播放到${state.activeIndex}`},</span>
<span>{videoData.length}</span>
</div>
</div>
<div className="flex gap-2 items-center text-sm">
<div className="flex items-center">
<div className={'flex items-center text-gray-400 cursor-pointer select-none'}
onClick={handleConfirm}>
<span>{editable ? '已解锁' : '锁定状态不可排序'}</span>
<span className="ml-2">
<span className="ml-2 text-sm">
{editable ? <IconUnlock/> : <IconLocked/>}
</span>
</div>
<div className="check-all ml-10">
<button className="hover:text-blue-300 text-gray-400 text-lg"
<button className="hover:text-blue-300 text-gray-400"
onClick={handleAllCheckedChange}>
<span className="text-sm mr-2"></span>
<span className="text-sm mr-2 whitespace-nowrap"></span>
{/*<CheckCircleFilled className={clsx({'text-blue-500': state.checkedAll})}/>*/}
</button>
<Checkbox checked={state.checkedAll} onChange={() => handleAllCheckedChange()}/>
@ -270,6 +273,7 @@ export default function LiveIndex() {
onCallback={() => {
}}
>
{videoData.length == 0 && <div className="m-auto py-16"><Empty/></div>}
<div className="sort-list-container flex-1">
<DndContext onDragEnd={(e) => {
const {active, over} = e;
@ -327,5 +331,6 @@ export default function LiveIndex() {
<IconDelete/>
</ButtonBatch>}
</div>
{contextHolder}
</div>)
}

View File

@ -25,6 +25,7 @@ export default function ButtonDeleteBatch(props: { ids: Id[];onSuccess?: () => v
return
}
modal.confirm({
wrapClassName:'root-modal-confirm',
title: `你确定要删除选择的 ${props.ids.length} 条新闻吗?`,
content: '删除后需从新闻素材中重新选择',
onOk: handlePush,

View File

@ -47,7 +47,7 @@ export default function EditSearchForm(props: {
if(!defaultParams){
return;
}
const tags = []
const tags:Id[][] = []
if(defaultParams.tags){
defaultParams.tags.forEach(it=>{
@ -79,7 +79,7 @@ export default function EditSearchForm(props: {
{/* options={articleTags}*/}
{/* onChange={setTags}*/}
{/*/>*/}
<TagSelect onChange={setTags} options={articleTags}/>
<TagSelect defaultSelectTags={tags} onChange={setTags} options={articleTags}/>
</div>
)
}

View File

@ -145,7 +145,7 @@ export default function SearchPanel({onSearch,defaultParams}: SearchPanelProps)
<Input
value={prevSearchName}
onChange={e => setPrevSearchName(e.target.value)}
className="w-[250px] rounded-3xl"
className="w-[270px] rounded-3xl"
placeholder={'请输入新闻标题关键词进行搜索'}
onPressEnter={onFinish}
onBlur={onFinish}

View File

@ -180,7 +180,7 @@ export default function VideoIndex() {
</div>
<InfiniteScroller loading={state.loading} ref={scrollerRef} onScroll={top => setState({showToTop: top > 30})}>
{
videoData.length == 0 ? <div className="m-auto"><Empty/></div> :
videoData.length == 0 ? <div className="m-auto py-16"><Empty/></div> :
<div className="sort-list-container flex-1">
<DndContext onDragEnd={(e) => {
const {active, over} = e;
@ -248,7 +248,7 @@ export default function VideoIndex() {
emptyMessage={`请选择要删除的新闻视频`}
title={`已选择${checkedIdArray.length}条,确定要全部删除吗?`}
className='bg-gray-300 hover:bg-gray-400 text-white'
confirmMessage={`删除后需从新闻素材中`}
confirmMessage={`删除后需重新生成视频`}
onSuccess={() => {
showToast('删除成功!', 'success')
loadList()

View File

@ -1,6 +1,6 @@
import {Outlet, useNavigate} from "react-router-dom";
import {Outlet, useLocation, useNavigate} from "react-router-dom";
import {Dropdown, MenuProps} from "antd";
import React from "react";
import React, {useEffect} from "react";
import AuthGuard from "@/routes/layout/auth-guard.tsx";
import {LogoText} from "@/components/icons/logo.tsx";
@ -10,6 +10,7 @@ import {DashboardNavigation} from "@/routes/layout/dashboard-navigation.tsx";
import useAuth from "@/hooks/useAuth.ts";
import {hidePhone} from "@/util/strings.ts";
import {defaultCache} from "@/hooks/useCache.ts";
type LayoutProps = {
@ -70,7 +71,16 @@ export const BaseLayout: React.FC<LayoutProps> = ({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>
<div className="fixed">first path:{defaultCache.firstLoadPath}</div>
<BaseLayout>
{children ? children : <Outlet/>}
</BaseLayout>