fixed: 新闻素材来源修改多选为单选;修改视频时长统计(视频时长单位为毫秒);

update: 更新导航栏文字(AI视频->视频生成)
This commit is contained in:
LittleBoy 2024-12-17 11:39:19 +08:00
parent 1acdc2a99d
commit e1a4005e27
7 changed files with 54 additions and 40 deletions

25
README.md Normal file
View File

@ -0,0 +1,25 @@
## 数值人直播间
### Start
```shell
git clone git@e.coding.net:starbite/aixiaodui/fengmang-backend.git
npm install
npm run dev
```
打开 [http://localhost:10021/](http://localhost:10021/) 查看效果
### Deploy
**直接部署**
> 需要配置 /mgmt 及 /api 反向代理
```shell
; 如果需要指定前缀(CDN)或者需要运行在相对路径中,不需要则跳过此命令
export PUBLIC_PATH=xxxxxxxx(相应路径)
npm run build
```
生成的资源在dist目录中将此目录中所有文件放置在待部署web目录即可。
**使用docker**
[x] TODO
```shell
dockercompose up -d
```

View File

@ -177,7 +177,7 @@ export default function LiveIndex() {
const totalDuration = useMemo(() => { const totalDuration = useMemo(() => {
if (!videoData || videoData.length == 0) return 0; if (!videoData || videoData.length == 0) return 0;
// 计算总时长 // 计算总时长
return videoData.reduce((sum, v) => sum + v.video_duration, 0); return videoData.reduce((sum, v) => sum + Math.ceil(v.video_duration / 1000), 0);
}, [videoData]) }, [videoData])
return (<div className="container py-10 page-live"> return (<div className="container py-10 page-live">

View File

@ -11,6 +11,11 @@ type SearchPanelProps = {
const pagination = { const pagination = {
limit: 10, page: 1 limit: 10, page: 1
} }
const DEFAULT_STATE = {
tag_level_1_id: -1,
tag_level_2_id: -1,
subOptions: []
}
export default function SearchPanel({onSearch}: SearchPanelProps) { export default function SearchPanel({onSearch}: SearchPanelProps) {
const tags = useArticleTags(); const tags = useArticleTags();
const [params, setParams] = useSetState<ApiArticleSearchParams>({ const [params, setParams] = useSetState<ApiArticleSearchParams>({
@ -18,36 +23,26 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
}); });
const [state, setState] = useSetState<{ const [state, setState] = useSetState<{
source: string | number; tag_level_1_id: number;
tag_level_2_id: number;
subOptions: (string | number)[] subOptions: (string | number)[]
}>({ }>({...DEFAULT_STATE})
source: -1,
subOptions: []
})
// 二级分类 // 二级分类
const [subOptions, setSubOptions] = useState<OptionItem[]>([]) const [subOptions, setSubOptions] = useState<OptionItem[]>([])
const onFinish = () => { const onFinish = () => {
if(state.source != -1){
params.tags = [];
state.subOptions.forEach(level2 => {
params.tags!.push({
level1: state.source,
level2
})
})
}else{
params.tags = undefined;
}
onSearch?.({ onSearch?.({
...params ...params,
tag_level_1_id: state.tag_level_1_id > 0?state.tag_level_1_id:undefined,
tag_level_2_id: state.tag_level_2_id > 0?state.tag_level_2_id:undefined,
pagination
}) })
} }
// 重置 // 重置
const onReset = () => { const onReset = () => {
setParams({pagination, title: ''}) setParams({pagination, title: ''})
setState({source: -1,subOptions: []}) setState({...DEFAULT_STATE})
setSubOptions([]) setSubOptions([])
onSearch?.({pagination}) onSearch?.({pagination})
} }
@ -84,37 +79,31 @@ export default function SearchPanel({onSearch}: SearchPanelProps) {
<div className="list-container flex-1"> <div className="list-container flex-1">
<div className="news-source-lv-1 flex flex-wrap"> <div className="news-source-lv-1 flex flex-wrap">
<div <div
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.source == -1 ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`} className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_1_id == -1 ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`}
onClick={() => { onClick={() => {
setState({source: -1, subOptions: []}) setState({...DEFAULT_STATE})
setSubOptions([]) setSubOptions([])
}}></div> }}></div>
{ {
tags.filter(s=>s.value !== 999999).map(it => ( tags.filter(s=>s.value !== 999999).map(it => (
<div <div
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.source == it.value ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`} className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_1_id == it.value ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`}
key={it.value} key={it.value}
onClick={() => { onClick={() => {
setState({source: it.value, subOptions: []}) setState({tag_level_1_id: Number(it.value),tag_level_2_id:-1})
setSubOptions(it.children || []) setSubOptions(it.children || [])
}}>{it.label}</div>) }}>{it.label}</div>)
) )
} }
</div> </div>
{state.source != -1 && subOptions.length > 0 && <div className="news-source-lv-2 bg-gray-100 p-2 rounded mt-2 flex flex-wrap"> {state.tag_level_1_id != -1 && subOptions.length > 0 && <div className="news-source-lv-2 bg-gray-100 p-2 rounded mt-2 flex flex-wrap">
{ {
subOptions.map(it => ( subOptions.map(it => (
<div <div
className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.subOptions.includes(it.value) ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`} className={`filter-item whitespace-nowrap px-2 py-1 mt-1 text-sm mr-1 cursor-pointer rounded ${state.tag_level_2_id == it.value ? 'bg-blue-500 text-white' : 'hover:bg-gray-100'}`}
key={it.value} key={it.value}
onClick={() => { onClick={() => {
const options = [...state.subOptions] setState({tag_level_2_id: Number(it.value)})
if (options.includes(it.value)) {
options.splice(options.indexOf(it.value), 1)
} else {
options.push(it.value)
}
setState({subOptions: options})
}}>{it.label}</div>) }}>{it.label}</div>)
) )
} }

View File

@ -77,7 +77,7 @@ export default function VideoIndex() {
const totalDuration = useMemo(() => { const totalDuration = useMemo(() => {
if (!videoData || videoData.length == 0) return 0; if (!videoData || videoData.length == 0) return 0;
// 计算总时长 // 计算总时长
return videoData.reduce((sum, v) => sum + v.duration, 0); return videoData.reduce((sum, v) => sum + Math.ceil(v.duration / 1000), 0);
}, [videoData]) }, [videoData])
return (<div className="container py-10 page-live"> return (<div className="container py-10 page-live">

View File

@ -17,7 +17,7 @@ const NavItems = [
}, },
{ {
key: 'create', key: 'create',
name: 'AI视频', name: '视频生成',
icon: 'ai', icon: 'ai',
path:'/create' path:'/create'
}, },

8
src/types/api.d.ts vendored
View File

@ -6,10 +6,10 @@ declare interface ApiRequestPageParams {
} }
declare interface ApiArticleSearchParams extends ApiRequestPageParams{ declare interface ApiArticleSearchParams extends ApiRequestPageParams{
// // 1级标签id // 1级标签id
// tag_level_1_id?: number; tag_level_1_id?: number;
// // 2级标签id 没有则为0 // 2级标签id 没有则为0
// tag_level_2_id?: number; tag_level_2_id?: number;
tags?: { tags?: {
level1: Id; level1: Id;
level2: Id; level2: Id;

View File

@ -32,12 +32,12 @@ export default defineConfig(({mode}) => {
port: 10021, port: 10021,
proxy: { proxy: {
'/mgmt': { '/mgmt': {
target: 'http://192.168.0.231:9999', target: 'http://124.220.14.192', // http://124.220.14.192/ 192.168.0.231:9999
changeOrigin: true, changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '') // rewrite: (path) => path.replace(/^\/api/, '')
}, },
'/api': { '/api': {
target: 'http://192.168.0.231:9999', target: 'http://124.220.14.192',
changeOrigin: true, changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '') // rewrite: (path) => path.replace(/^\/api/, '')
} }