167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
import {Modal} from "antd";
|
|
import ArticleGroup from "@/components/article/group.tsx";
|
|
import {useEffect, useState} from "react";
|
|
import {useSetState} from "ahooks";
|
|
import * as article from "@/service/api/article.ts";
|
|
import {regenerate} from "@/service/api/video.ts";
|
|
import {push2video} from "@/service/api/article.ts";
|
|
import {showErrorToast, showToast} from "@/components/message.ts";
|
|
|
|
type Props = {
|
|
id?: number;
|
|
type: 'news' | 'video';
|
|
onClose?: (saved?: boolean) => void;
|
|
}
|
|
|
|
const DEFAULT_STATE = {
|
|
loading: false,
|
|
open: false,
|
|
msgTitle: '',
|
|
msgGroup: '',
|
|
error: ''
|
|
}
|
|
|
|
function pushBlocksToGroup(blocks: BlockContent[], groups: BlockContent[][]) {
|
|
const lastGroup = groups[groups.length - 1]
|
|
if (lastGroup && lastGroup.filter(s => s.type == 'text').length == 0) {
|
|
// 如果上一个group中没有文本则直接合并
|
|
lastGroup.push(...blocks)
|
|
} else {
|
|
groups.push(blocks)
|
|
}
|
|
}
|
|
|
|
function rebuildGroups(groups: BlockContent[][]) {
|
|
const _groups: BlockContent[][] = [];
|
|
if (!groups || groups.length == 0) return _groups;
|
|
groups.forEach((blocks, index) => {
|
|
if (!blocks) return;
|
|
blocks = blocks.filter(s => !!s).sort((a, b) => {
|
|
if (a.type == 'text' && b.type == 'text') return 1;
|
|
return a.type == 'text' ? -1 : 1
|
|
})
|
|
if (blocks.length == 1) {
|
|
if (index == 0) _groups.push(blocks)
|
|
else pushBlocksToGroup(blocks, _groups)
|
|
} else {
|
|
if (index == 0) {
|
|
_groups.push([blocks[0]])
|
|
_groups.push(blocks.slice(1))
|
|
} else {
|
|
pushBlocksToGroup(blocks, _groups)
|
|
}
|
|
}
|
|
});
|
|
if (_groups.length < 2) {
|
|
Array(2 - _groups.length).fill([{type: 'text', content: ''}]).forEach((it) => {
|
|
_groups.push(it)
|
|
})
|
|
}
|
|
console.log('rebuildGroups', _groups)
|
|
return _groups;
|
|
|
|
|
|
}
|
|
|
|
export default function ArticleEditModal(props: Props) {
|
|
|
|
const [groups, setGroups] = useState<BlockContent[][]>([]);
|
|
const [title, setTitle] = useState('')
|
|
|
|
const [state, setState] = useSetState({
|
|
...DEFAULT_STATE,
|
|
generating:false
|
|
})
|
|
// 保存数据
|
|
const handleSave = () => {
|
|
setState({error: ''})
|
|
if (!title) {
|
|
// setState({msgTitle: '请输入标题内容'});
|
|
return;
|
|
}
|
|
if (groups.length == 0 || groups[0].length == 0 || !groups[0][0].content) {
|
|
// setState({msgGroup: '请输入正文文本内容'});
|
|
return;
|
|
}
|
|
const save = props.type == 'news' ? article.save : regenerate
|
|
setState({loading: true})
|
|
save(title, groups, props.id && props.id > 0 ? props.id : undefined).then(() => {
|
|
props.onClose?.(true)
|
|
}).catch(e => {
|
|
setState({error: e.data || '保存失败,请重试!'})
|
|
}).finally(() => {
|
|
setState({loading: false})
|
|
});
|
|
}
|
|
const handlePush2Video = () =>{
|
|
if(!props.id || state.generating) return;
|
|
setState({generating:true})
|
|
push2video([props.id]).then(() => {
|
|
showToast('推流成功', 'success')
|
|
// navigate('/create?state=push-success',{
|
|
// state: 'push-success'
|
|
// })
|
|
// props.onSuccess?.()
|
|
}).catch(showErrorToast).finally(()=>{
|
|
setState({generating:false})
|
|
})
|
|
}
|
|
useEffect(() => {
|
|
setState({...DEFAULT_STATE})
|
|
if (typeof (props.id) != 'undefined') {
|
|
if (props.id > 0) {
|
|
article.getById(props.id).then(res => {
|
|
setGroups(rebuildGroups(res.content_group))
|
|
setTitle(res.title)
|
|
})
|
|
} else {
|
|
// 新增
|
|
setGroups([])
|
|
setTitle('')
|
|
}
|
|
}
|
|
}, [props.id])
|
|
|
|
return (<Modal
|
|
title={null}
|
|
centered={true}
|
|
rootClassName={"article-edit-modal"}
|
|
open={props.id != undefined && props.id >= 0}
|
|
maskClosable={false}
|
|
keyboard={false}
|
|
width={'1200px'}
|
|
footer={null}
|
|
closeIcon={null}
|
|
onCancel={() => props.onClose?.()}
|
|
okButtonProps={{loading: state.loading}}
|
|
onOk={handleSave}
|
|
okText={props.type == 'news' ? '确定' : '重新生成'}
|
|
>
|
|
<div className="article-title mt-5">
|
|
<input className={'input-box text-lg'} value={title} onChange={e => {
|
|
setTitle(e.target.value)
|
|
setState({msgTitle: e.target.value ? '' : '请输入标题内容'})
|
|
}} placeholder={'请输入文章标题'}/>
|
|
<div className="text-red-500">{state.msgTitle}</div>
|
|
</div>
|
|
<div className="article-body">
|
|
<div className="box">
|
|
<ArticleGroup
|
|
errorMessage={state.msgGroup} editable groups={groups}
|
|
onChange={list => {
|
|
setGroups(() => list)
|
|
setState({msgGroup: (list.length == 0 || list[0].length == 0 || !list[0][0].content) ? '请输入正文文本内容' : ''});
|
|
}}
|
|
/>
|
|
</div>
|
|
{state.error && <div className="text-red-500">{state.error}</div>}
|
|
</div>
|
|
<div className="modal-control-footer flex justify-end">
|
|
<div className="flex gap-10 ">
|
|
{props.type == 'news' && props.id ? <button className="text-gray-400 hover:text-gray-800" onClick={handlePush2Video}>{state.generating?'推送中...':'生成视频'}</button> : null}
|
|
<button className="text-gray-400 hover:text-gray-800" onClick={() => props.onClose?.()}>取消</button>
|
|
<button onClick={handleSave} className="text-gray-800 hover:text-blue-500">{props.type == 'news' ? '确定' : '重新生成'}</button>
|
|
</div>
|
|
</div>
|
|
</Modal>);
|
|
} |