feat: 视频生成优化生成中ui

This commit is contained in:
LittleBoy 2024-12-18 21:08:07 +08:00
parent e1bcc4c13f
commit c3ea81e69f
8 changed files with 37 additions and 36 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "digital-news-live", "name": "ai-live",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.2",
"type": "module", "type": "module",
"description": "数字人直播间", "description": "数字人直播间",
"scripts": { "scripts": {

View File

@ -2,6 +2,8 @@ import AppRouter from "@/routes";
import {ConfigProvider} from "@/contexts/config"; import {ConfigProvider} from "@/contexts/config";
import {AuthProvider} from "@/contexts/auth"; import {AuthProvider} from "@/contexts/auth";
console.log(`APP-BUILD-AT: ${AppBuildVersion}`)
function App() { function App() {
return ( return (
<ConfigProvider> <ConfigProvider>

View File

@ -49,26 +49,20 @@ export const VideoListItem = (
{/* <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>*/}
{/*</div>}*/} {/*</div>}*/}
<div <div
className={`video-item-info flex gap-2 flex-1 bg-gray-100 h-[80px] overflow-hidden rounded-lg p-3 shadow-blue-500 ${active ? 'video-item-shadow' : ''}`}> className={`video-item-info relative flex gap-2 flex-1 bg-gray-100 h-[80px] overflow-hidden rounded-lg p-3 shadow-blue-500 ${active ? 'video-item-shadow' : ''}`}>
<div className={'video-title leading-7 flex-1'}>{video.title || video.video_title}</div> <div className={'video-title leading-7 flex-1'}>{video.title || video.video_title}</div>
<div className={'video-item-cover bg-white rounded-md overflow-hidden'}> <div className={'video-item-cover bg-white rounded-md overflow-hidden'}>
<img className="w-[100px] h-[56px] object-cover" src={video.cover || ImageCover} /> <img className="w-[100px] h-[56px] object-cover" src={video.cover || ImageCover} />
</div> </div>
{type == 'create' && video.status == VideoStatus.Generating && <div className={'absolute inset-0 bg-black/30 text-white flex items-center justify-center'}>
<span className="ml-1"></span>
</div>}
</div> </div>
<div className="operation flex items-center ml-2 gap-3 text-lg text-gray-400"> <div className="operation flex items-center ml-2 gap-3 text-lg text-gray-400 w-[116px]">
{sortable && (!active ? <button className="hover:text-blue-500 cursor-move" {...attributes} {...listeners}> {sortable && (!active ? <button className="hover:text-blue-500 cursor-move" {...attributes} {...listeners}>
<MenuOutlined/> <MenuOutlined/>
</button> : <button disabled className="cursor-not-allowed"><MenuOutlined/></button>)} </button> : <button disabled className="cursor-not-allowed"><MenuOutlined/></button>)}
{onPlay && {onPlay &&<button className="hover:text-blue-500" onClick={onPlay} style={{fontSize: '1.3em'}}><IconPlay/></button>}
<>
{(
type == 'create' && video.status == VideoStatus.Generating
) ? <button title="视频生成中" className="flex items-center justify-center">
<LoadingOutlined className="block text-gray-500" style={{fontSize: '0.85em'}}/>
</button>: <button className="hover:text-blue-500" onClick={onPlay} style={{fontSize: '1.3em'}}><IconPlay/>
</button>
}
</>}
{editable && <> {editable && <>
{onEdit && {onEdit &&

View File

@ -50,17 +50,17 @@ export default function NewsIndex() {
<SearchPanel onSearch={setParams}/> <SearchPanel onSearch={setParams}/>
</Card> </Card>
<Card className="news-list-container"> <Card className="news-list-container">
<Modal open={!!activeNews} width={1000} footer={null} onCancel={() => setActiveNews(undefined)}> {activeNews && <Modal open={true} width={1000} footer={null} onCancel={() => setActiveNews(undefined)}>
<div className="news-detail px-3 pb-5"> <div className="news-detail px-3 pb-5">
<div className="new-title text-2xl">{activeNews?.title}</div> <div className="new-title text-2xl">{activeNews?.title}</div>
<div className="info mt-2 mb-5 text-sm flex gap-3"> <div className="info mt-2 mb-5 text-sm flex gap-3">
<span className="source text-blue-700">{activeNews?.media_name}</span> <span className="source text-blue-700">{activeNews?.media_name}</span>
<span className="create-time text-gray-400">{formatTime(activeNews?.publish_time)}</span> <span className="create-time text-gray-400">{formatTime(activeNews?.publish_time)}</span>
</div> </div>
<div className="overflow-auto leading-7 text-base" <div className="overflow-auto leading-7 text-base"
style={{maxHeight: 1000}} dangerouslySetInnerHTML={{__html: activeNews?.content || ''}}></div> style={{maxHeight: 1000}} dangerouslySetInnerHTML={{__html: activeNews?.content || ''}}></div>
</div> </div>
</Modal> </Modal>}
<div className="controls flex justify-between mb-1"> <div className="controls flex justify-between mb-1">
<div> <div>
<Checkbox checked={state.checkAll} onChange={e => { <Checkbox checked={state.checkAll} onChange={e => {
@ -90,7 +90,7 @@ export default function NewsIndex() {
} }
}}/> }}/>
</div> </div>
<div className="news-content"> <div className="news-content flex-1">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="title text-lg cursor-pointer" onClick={() => { <div className="title text-lg cursor-pointer" onClick={() => {
handleViewNewsDetail(item.id) handleViewNewsDetail(item.id)

View File

@ -4,13 +4,14 @@ import {showErrorToast, showToast} from "@/components/message.ts";
import {push2room, VideoStatus} from "@/service/api/video.ts"; import {push2room, VideoStatus} from "@/service/api/video.ts";
export default function ButtonPush2Room(props: { ids: Id[]; list: VideoInfo[] }) { export default function ButtonPush2Room(props: { ids: Id[]; list: VideoInfo[];onSuccess?:()=>void; }) {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const handlePush = () => { const handlePush = () => {
setLoading(true) setLoading(true)
// 只需要已经生成视频的数据id // 只需要已经生成视频的数据id
const vids = props.list.filter(v => v.status == VideoStatus.Generated && props.ids.includes(v.id)).map(v => v.id) const vids = props.list.filter(v => v.status == VideoStatus.Generated && props.ids.includes(v.id)).map(v => v.id)
push2room(vids).then(() => { push2room(vids).then(() => {
props.onSuccess?.()
if(props.ids.length == vids.length){ if(props.ids.length == vids.length){
showToast('一键推流成功,已推流至数字人直播间,请前往数字人直播间页面查看!', 'success') showToast('一键推流成功,已推流至数字人直播间,请前往数字人直播间页面查看!', 'success')
}else{ }else{

View File

@ -150,12 +150,12 @@ export default function VideoIndex() {
return newArr; return newArr;
}) })
}} }}
onPlay={() => playVideo(v, index)} onPlay={v.status == VideoStatus.Generating ? undefined :() => playVideo(v, index)}
onEdit={() => { onEdit={v.status == VideoStatus.Generating ? undefined : () => {
setEditId(v.article_id) setEditId(v.article_id)
}} }}
editable={true} editable={v.status != VideoStatus.Generating}
sortable={true} sortable={v.status != VideoStatus.Generating}
/>))} />))}
</SortableContext> </SortableContext>
</DndContext> </DndContext>
@ -172,13 +172,13 @@ export default function VideoIndex() {
{/* confirmMessage={`是否确定一键推流选中新闻视频?`}*/} {/* confirmMessage={`是否确定一键推流选中新闻视频?`}*/}
{/* onSuccess={loadList}*/} {/* onSuccess={loadList}*/}
{/*>一键推流</ButtonBatch>*/} {/*>一键推流</ButtonBatch>*/}
<ButtonPush2Room ids={checkedIdArray} list={videoData}/> <ButtonPush2Room ids={checkedIdArray} list={videoData} onSuccess={loadList}/>
</div> </div>
</div> </div>
<div className="video-player-container ml-16 w-[360px] flex flex-col"> <div className="video-player-container ml-16 w-[360px] flex flex-col">
<div className="text-center text-base"></div> <div className="text-center text-base"></div>
<div className="video-player flex items-center mt-2"> <div className="video-player flex items-center mt-2">
<div className=" w-[360px] h-[640px] rounded overflow-hidden"> <div className=" w-[360px] h-[630px] rounded overflow-hidden">
{/*<video ref={videoRef} poster={videoData[state.playingIndex]?.cover} preload="auto" playsinline webkit-playsinline className="w-full bg-white w-[360px] h-[640px]"></video>*/} {/*<video ref={videoRef} poster={videoData[state.playingIndex]?.cover} preload="auto" playsinline webkit-playsinline className="w-full bg-white w-[360px] h-[640px]"></video>*/}
<Player <Player
ref={player} url={videoData[state.playingIndex]?.oss_video_url} ref={player} url={videoData[state.playingIndex]?.oss_video_url}

1
src/vite-env.d.ts vendored
View File

@ -5,6 +5,7 @@ declare type decimal = number;
declare type int = number; declare type int = number;
declare type AllType = string | number | object | undefined | null; declare type AllType = string | number | object | undefined | null;
declare const APP_SITE_URL: string; declare const APP_SITE_URL: string;
declare const AppBuildVersion: string;
declare const AppConfig: { declare const AppConfig: {
// 登录凭证 token key // 登录凭证 token key
AUTH_TOKEN_KEY: string; AUTH_TOKEN_KEY: string;

View File

@ -1,14 +1,16 @@
import {defineConfig} from 'vite' import {defineConfig} from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import {resolve} from "path"; import {resolve} from "path";
import AppPackage from './package.json'
import dayjs from "dayjs";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({mode}) => { export default defineConfig(({mode}) => {
const devServerHost = mode == 'test' ? '124.220.14.192' : '192.168.0.231:9999' const devServerHost = mode == 'test' ? '124.220.14.192' : '192.168.0.231:9999'
const AUTH_TOKEN_KEY = mode == 'production' ? 'digital-person-token' : `digital-person-token_${mode}` const AUTH_TOKEN_KEY = mode == 'production' ? 'digital-person-token' : `digital-person-token_${mode}`
if (mode !== 'production') { if (mode !== 'production') {
console.log('dev server is', devServerHost,mode) console.log('dev server is', devServerHost, mode)
} }
return { return {
plugins: [react()], plugins: [react()],
@ -20,7 +22,8 @@ export default defineConfig(({mode}) => {
AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || AUTH_TOKEN_KEY, AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || AUTH_TOKEN_KEY,
AUTHED_PERSON_DATA_KEY: process.env.AUTHED_PERSON_DATA_KEY || 'digital-person-user-info', AUTHED_PERSON_DATA_KEY: process.env.AUTHED_PERSON_DATA_KEY || 'digital-person-user-info',
}), }),
AppMode: JSON.stringify(mode) AppMode: JSON.stringify(mode),
AppBuildVersion: JSON.stringify(AppPackage.name + '-' + AppPackage.version + '-' + dayjs().format('YYYYMMDDHH_mmss'))
}, },
resolve: { resolve: {
alias: { alias: {