💄 update 新闻内容编辑器
This commit is contained in:
parent
cb1eb51ba4
commit
d4e9dde7df
@ -185,13 +185,17 @@
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.data-list-container {
|
||||
height: calc(100vh - var(--app-header-header) - 200px);
|
||||
.list-scroller-container{
|
||||
overflow: auto;
|
||||
margin-right: -20px;
|
||||
padding-right: 16px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.data-list-container {
|
||||
@apply list-scroller-container;
|
||||
height: calc(100vh - var(--app-header-header) - 200px);
|
||||
|
||||
|
||||
.data-list-container-inner {
|
||||
|
||||
@ -229,8 +233,39 @@
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 20px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.ant-modal-confirm-content{
|
||||
color: #999;
|
||||
}
|
||||
.ant-modal-confirm-btns{
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.article-edit-modal{
|
||||
|
||||
.ant-modal {
|
||||
.ant-modal-content {
|
||||
@apply bg-white p-0;
|
||||
.ant-modal-body{
|
||||
@apply p-0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.article-title{
|
||||
@apply px-6 pt-10 pb-6;
|
||||
}
|
||||
.article-body{
|
||||
@apply p-6
|
||||
}
|
||||
.modal-control-footer{
|
||||
@apply p-6
|
||||
}
|
||||
.input-box{
|
||||
// focus-within:shadow
|
||||
@apply bg-[#f8f8f8] border border-transparent w-full px-4 py-2 focus-within:bg-[#f0f0f0] focus-within:border-gray-300;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,28 +273,17 @@
|
||||
.page-action {
|
||||
@apply fixed right-10 bottom-10 flex flex-col gap-4;
|
||||
button {
|
||||
@apply border-0 min-w-[120px] h-[40px] rounded-3xl text-white bg-blue-500 pl-4;
|
||||
@apply border-0 min-w-[120px] h-[40px] rounded-3xl text-white pr-4 flex items-center justify-between;
|
||||
.text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply bg-blue-600;
|
||||
}
|
||||
|
||||
&:active {
|
||||
@apply bg-blue-700;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply bg-gray-400;
|
||||
}
|
||||
|
||||
&.btn-info {
|
||||
@apply bg-info text-gray-800;
|
||||
.svg-icon {
|
||||
@apply text-gray-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,30 @@
|
||||
.blockContainer {
|
||||
@apply flex mb-5;
|
||||
@apply relative;
|
||||
:global{
|
||||
.divider-container{
|
||||
@apply absolute inset-x-2 z-10;
|
||||
&.before{
|
||||
top:-12px;
|
||||
}
|
||||
&.after{
|
||||
bottom: -10px;
|
||||
}
|
||||
.ant-divider-horizontal{
|
||||
margin: 0;
|
||||
border-block-start: 1px rgba(5, 5, 5,0.1);
|
||||
}
|
||||
}
|
||||
.article-action-add{
|
||||
@apply text-gray-400 text-sm inline-block bg-[#cce2ff] w-[80px] justify-center flex rounded-xl cursor-pointer hover:bg-blue-300 hover:text-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blockInner{
|
||||
@apply flex px-4 py-10 hover:bg-[#e6ebf1] ;
|
||||
}
|
||||
.blockFooter{}
|
||||
.block {
|
||||
@apply border border-gray-300 border-dashed p-3 rounded flex-1;
|
||||
|
||||
@apply flex-1;
|
||||
&:last-child {
|
||||
@apply mb-0;
|
||||
}
|
||||
@ -23,10 +43,28 @@
|
||||
}
|
||||
|
||||
.group {
|
||||
@apply flex gap-4;
|
||||
:global{
|
||||
.area-title{
|
||||
@apply text-gray-400 text-sm text-gray-800;
|
||||
}
|
||||
.digital-person{
|
||||
width: 450px;
|
||||
}
|
||||
.panel{
|
||||
@apply flex flex-col;
|
||||
}
|
||||
.panel-body{
|
||||
@apply bg-[#f0f0f0] flex-1 rounded-xl mt-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.imagerOrText{
|
||||
@apply bg-[#f8f8f8] rounded-xl py-2;
|
||||
}
|
||||
.imageList {
|
||||
@apply grid grid-cols-4 gap-4 p-3 border border-blue-200;
|
||||
@apply grid grid-cols-4 imagerOrText px-2 gap-2;
|
||||
:global {
|
||||
.ant-upload-wrapper {
|
||||
display: block;
|
||||
@ -57,19 +95,14 @@
|
||||
}
|
||||
}
|
||||
.imageDelete{
|
||||
@apply absolute flex items-center justify-center p-0.5 w-[22px] h-[22px] rounded-full border border-red-500 text-red-500 cursor-pointer z-10;
|
||||
right:-10px;
|
||||
top:-10px;
|
||||
font-size: 14px;
|
||||
&:hover{
|
||||
@apply text-white bg-red-500;
|
||||
}
|
||||
@apply absolute flex items-center justify-center right-0 top-0 w-[22px] h-[22px] rounded-full cursor-pointer z-10 ;
|
||||
font-size: 16px;
|
||||
}
|
||||
.uploadImage {
|
||||
@apply flex justify-center items-center relative h-[100px] text-gray-400;
|
||||
|
||||
.uploadTips {
|
||||
@apply absolute inset-0 cursor-pointer opacity-0 transition rounded flex items-center justify-center bg-black/20 text-white;
|
||||
@apply absolute inset-0 cursor-pointer opacity-0 transition rounded flex items-center justify-center bg-black/50 text-white;
|
||||
}
|
||||
|
||||
.imagePlaceholder {
|
||||
@ -86,13 +119,14 @@
|
||||
}
|
||||
|
||||
.text {
|
||||
@apply border border-blue-200 overflow-hidden flex-1 rounded focus:border-blue-200 transition;
|
||||
@apply imagerOrText overflow-hidden flex-1 rounded focus:border-blue-200 transition;
|
||||
&:hover {
|
||||
@apply border-blue-500;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
@apply border-blue-500 shadow-md;
|
||||
:global{
|
||||
.ant-input{
|
||||
@apply px-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import {Popconfirm} from "antd";
|
||||
import {Divider, Popconfirm} from "antd";
|
||||
|
||||
import {IconAdd, IconDelete} from "@/components/icons";
|
||||
import {IconAdd, IconAddCircle, IconDelete} from "@/components/icons";
|
||||
import ImageList from "@/components/article/list.tsx";
|
||||
|
||||
import { BlockText} from "./item.tsx";
|
||||
@ -48,7 +48,7 @@ export default function ArticleBlock(
|
||||
onAdd,
|
||||
onChange,
|
||||
index,
|
||||
errorMessage
|
||||
errorMessage,
|
||||
}: Props) {
|
||||
const blocks = rebuildBlockArray(defaultBlocks)
|
||||
|
||||
@ -58,41 +58,43 @@ export default function ArticleBlock(
|
||||
onChange?.(_blocks)
|
||||
}
|
||||
|
||||
return <div className={styles.blockContainer}>
|
||||
<div className={clsx(className || '', styles.block, index == 0 ? styles.blockFist : '', ' hover:bg-blue-10')}>
|
||||
<div className={styles.blockBody}>
|
||||
<div>
|
||||
<div className={clsx(index == 0 ? '' : styles.blockItem, 'flex')}>
|
||||
<BlockText
|
||||
onChange={(block) => handleBlockChange(0, block)}
|
||||
data={blocks[0]}
|
||||
isFirstBlock={index == 0}
|
||||
editable={editable}/>
|
||||
return <div className={`${styles.blockContainer} group`}>
|
||||
{editable && index == 1 && <div className={'divider-container before'}><Divider>
|
||||
<span onClick={onAdd} className="article-action-add" title="新增分组"><IconAdd style={{fontSize: 24}}/></span>
|
||||
</Divider></div> }
|
||||
<div className={styles.blockInner}>
|
||||
<div className={clsx(className || '', styles.block, index == 0 ? styles.blockFist : '', ' hover:bg-blue-10')}>
|
||||
<div className={styles.blockBody}>
|
||||
<div>
|
||||
<div className={clsx(index == 0 ? '' : styles.blockItem, 'flex')}>
|
||||
<BlockText
|
||||
onChange={(block) => handleBlockChange(0, block)}
|
||||
data={blocks[0]}
|
||||
isFirstBlock={index == 0}
|
||||
editable={editable}/>
|
||||
</div>
|
||||
</div>
|
||||
{index == 0 && <div className="flex items-center text-red-500 justify-between text-sm mt-1">
|
||||
<div>{errorMessage}</div>
|
||||
<div>该编辑框内容由数字人播报</div>
|
||||
</div>}
|
||||
<ImageList blocks={blocks} editable={editable} onChange={onChange}/>
|
||||
</div>
|
||||
{index > 0 && <ImageList blocks={blocks} editable={editable} onChange={onChange}/>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{editable && <div className="ml-2 flex flex-col justify-between ">
|
||||
{
|
||||
index > 0 ? <Popconfirm
|
||||
title={<div style={{minWidth: 150}}><span>请确认删除此分组?</span></div>}
|
||||
onConfirm={onRemove}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className="article-action-icon" title="删除此分组">
|
||||
{editable && <div className="ml-2 flex items-center">
|
||||
{
|
||||
index > 0 ? <Popconfirm
|
||||
title={<div style={{minWidth: 150}}><span>请确认删除此分组?</span></div>}
|
||||
onConfirm={onRemove}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className="article-action-icon hidden group-hover:block" title="删除此分组">
|
||||
<IconDelete style={{fontSize: 24}}/>
|
||||
</span>
|
||||
</Popconfirm> : <span></span>
|
||||
}
|
||||
<span onClick={onAdd} className="article-action-icon" title="新增分组"><IconAdd
|
||||
style={{fontSize: 24}}/></span>
|
||||
</div>}
|
||||
</Popconfirm> : <span></span>
|
||||
}
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
{editable && <div className={'divider-container after'}><Divider>
|
||||
<span onClick={onAdd} className="article-action-add" title="新增分组"><IconAdd style={{fontSize: 24}}/></span>
|
||||
</Divider></div> }
|
||||
</div>
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import {Input, Modal} from "antd";
|
||||
import {Input, Modal, Space} from "antd";
|
||||
import ArticleGroup from "@/components/article/group.tsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useSetState} from "ahooks";
|
||||
@ -16,12 +16,12 @@ const DEFAULT_STATE = {
|
||||
open: false,
|
||||
msgTitle: '',
|
||||
msgGroup: '',
|
||||
error:''
|
||||
error: ''
|
||||
}
|
||||
|
||||
function pushBlocksToGroup(blocks: BlockContent[],groups: BlockContent[][]){
|
||||
function pushBlocksToGroup(blocks: BlockContent[], groups: BlockContent[][]) {
|
||||
const lastGroup = groups[groups.length - 1]
|
||||
if (lastGroup && lastGroup.filter(s=>s.type == 'text').length == 0) {
|
||||
if (lastGroup && lastGroup.filter(s => s.type == 'text').length == 0) {
|
||||
// 如果上一个group中没有文本则直接合并
|
||||
lastGroup.push(...blocks)
|
||||
} else {
|
||||
@ -32,21 +32,21 @@ function pushBlocksToGroup(blocks: BlockContent[],groups: BlockContent[][]){
|
||||
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;
|
||||
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)
|
||||
if (index == 0) _groups.push(blocks)
|
||||
else pushBlocksToGroup(blocks, _groups)
|
||||
} else {
|
||||
if(index == 0){
|
||||
if (index == 0) {
|
||||
_groups.push([blocks[0]])
|
||||
_groups.push(blocks.slice(1))
|
||||
}else{
|
||||
pushBlocksToGroup(blocks,_groups)
|
||||
} else {
|
||||
pushBlocksToGroup(blocks, _groups)
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -60,6 +60,7 @@ function rebuildGroups(groups: BlockContent[][]) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default function ArticleEditModal(props: Props) {
|
||||
|
||||
const [groups, setGroups] = useState<BlockContent[][]>([]);
|
||||
@ -83,7 +84,7 @@ export default function ArticleEditModal(props: Props) {
|
||||
setState({loading: true})
|
||||
save(title, groups, props.id && props.id > 0 ? props.id : undefined).then(() => {
|
||||
props.onClose?.(true)
|
||||
}).catch(e=>{
|
||||
}).catch(e => {
|
||||
setState({error: e.data || '保存失败,请重试!'})
|
||||
}).finally(() => {
|
||||
setState({loading: false})
|
||||
@ -91,7 +92,7 @@ export default function ArticleEditModal(props: Props) {
|
||||
}
|
||||
useEffect(() => {
|
||||
setState({...DEFAULT_STATE})
|
||||
if (typeof(props.id) != 'undefined') {
|
||||
if (typeof (props.id) != 'undefined') {
|
||||
if (props.id > 0) {
|
||||
article.getById(props.id).then(res => {
|
||||
setGroups(rebuildGroups(res.content_group))
|
||||
@ -106,34 +107,28 @@ export default function ArticleEditModal(props: Props) {
|
||||
}, [props.id])
|
||||
|
||||
return (<Modal
|
||||
title={'编辑文章'}
|
||||
title={null}
|
||||
centered={true}
|
||||
rootClassName={"article-edit-modal"}
|
||||
open={props.id != undefined && props.id >= 0}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={800}
|
||||
onCancel={()=>props.onClose?.()}
|
||||
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">
|
||||
<div className="title">
|
||||
<span className="text text-base">标题</span>
|
||||
<span className="require ml-1 font-bold text-red-500">*</span>
|
||||
</div>
|
||||
<div className="box mt-1">
|
||||
<Input rootClassName={state.msgTitle ? 'border-red-500' : ''} value={title} onChange={e => {
|
||||
setTitle(e.target.value)
|
||||
setState({msgTitle: e.target.value ? '' : '请输入标题内容'})
|
||||
}} placeholder={'请输入文章标题'}/>
|
||||
</div>
|
||||
<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="aricle-body mt-3">
|
||||
<div className="title">
|
||||
<span className="text text-base">正文</span>
|
||||
<span className="require ml-1 font-bold text-red-500">*</span>
|
||||
</div>
|
||||
<div className="article-body mt-3">
|
||||
<div className="box mt-1">
|
||||
<ArticleGroup
|
||||
errorMessage={state.msgGroup} editable groups={groups}
|
||||
@ -145,5 +140,12 @@ export default function ArticleEditModal(props: Props) {
|
||||
</div>
|
||||
{state.error && <div className="text-red-500">{state.error}</div>}
|
||||
</div>
|
||||
<div className="modal-control-footer flex justify-end">
|
||||
<Space>
|
||||
{props.type == 'news' ? <button>生成视频</button> : null}
|
||||
<button onClick={() => props.onClose?.()}>取消</button>
|
||||
<button>{props.type == 'news' ? '确定' : '重新生成'}</button>
|
||||
</Space>
|
||||
</div>
|
||||
</Modal>);
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import {message} from "antd"
|
||||
import {Input, message} from "antd"
|
||||
import ArticleBlock from "@/components/article/block.tsx";
|
||||
|
||||
import styles from './article.module.scss'
|
||||
import {showToast} from "@/components/message.ts";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
groups: BlockContent[][];
|
||||
@ -12,7 +13,6 @@ type Props = {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function ArticleGroup({groups, editable, onChange, errorMessage}: Props) {
|
||||
// const groups = rebuildGroups(_groups)
|
||||
/**
|
||||
@ -38,30 +38,69 @@ export default function ArticleGroup({groups, editable, onChange, errorMessage}:
|
||||
}
|
||||
onChange?.(_groups)
|
||||
}
|
||||
|
||||
const handleDigitalPersonContentChange = (content:string) => {
|
||||
groups[0] = [{type: 'text', content}]
|
||||
onChange?.([...groups])
|
||||
}
|
||||
|
||||
return <div className={styles.group}>
|
||||
{groups.map((g, index) => (
|
||||
<ArticleBlock
|
||||
editable={editable}
|
||||
key={index}
|
||||
blocks={g}
|
||||
onChange={(blocks) => {
|
||||
groups[index] = blocks
|
||||
onChange?.([...groups])
|
||||
}}
|
||||
errorMessage={errorMessage}
|
||||
index={index}
|
||||
onAdd={() => {
|
||||
handleAddGroup?.(index + 1)
|
||||
}}
|
||||
onRemove={async () => {
|
||||
if (groups.length == 1) {
|
||||
message.warning('至少保留一个内容块')
|
||||
return;
|
||||
}
|
||||
onChange?.(groups.filter((_, idx) => index !== idx))
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<div className={'panel digital-person'}>
|
||||
<div className="area-title">
|
||||
<span className="">数字人主播台编辑区</span>
|
||||
<span className="text-gray-400">(出现数字人形象)</span>
|
||||
</div>
|
||||
<div className="panel-body p-3">
|
||||
{/* value={groups || groups[0][0].content}*/}
|
||||
<div className="h-[486px] pt-2 rounded-xl overflow-hidden bg-gray-50">
|
||||
{editable ? <div className="relative">
|
||||
<Input.TextArea
|
||||
placeholder={'请输入文本内容'}
|
||||
value={groups && groups.length > 0 ? groups[0][0].content : ''}
|
||||
autoSize={{minRows: 20, maxRows: 21}}
|
||||
variant={"borderless"}
|
||||
onChange={e => {
|
||||
handleDigitalPersonContentChange(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div> : <p className="p-2">12123</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={"panel groups-list flex-1"}>
|
||||
<div className={"area-title"}>
|
||||
<span className="">数字人主播台编辑区</span>
|
||||
<span className="text-gray-400">(出现数字人形象)</span>
|
||||
</div>
|
||||
|
||||
<div className="panel-body py-3">
|
||||
<div className="max-h-[485px] overflow-auto py-4">
|
||||
{groups.map((g, index) => (
|
||||
index == 0 ? null : <ArticleBlock
|
||||
editable={editable}
|
||||
key={index}
|
||||
blocks={g}
|
||||
onChange={(blocks) => {
|
||||
groups[index] = blocks
|
||||
onChange?.([...groups])
|
||||
}}
|
||||
errorMessage={errorMessage}
|
||||
index={index}
|
||||
onAdd={() => {
|
||||
handleAddGroup?.(index + 1)
|
||||
}}
|
||||
onRemove={async () => {
|
||||
if (groups.length == 1) {
|
||||
message.warning('至少保留一个内容块')
|
||||
return;
|
||||
}
|
||||
onChange?.(groups.filter((_, idx) => index !== idx))
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{groups.length == 0 && editable &&
|
||||
<ArticleBlock editable onChange={blocks => onChange?.([blocks])} index={0}
|
||||
blocks={[{type: 'text', content: ''}]}/>}
|
||||
|
@ -64,15 +64,7 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
||||
}
|
||||
//
|
||||
return <div className={styles.image}>
|
||||
{editable ? <div className={'relative'}>
|
||||
{!onlyUpload && <Popconfirm
|
||||
title={<div style={{minWidth: 150}}><span>请确认删除此删除此图片?</span></div>}
|
||||
onConfirm={onRemove}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className={styles.imageDelete}><CloseOutlined/></span>
|
||||
</Popconfirm>}
|
||||
{editable && onlyUpload ? <div className={'relative'}>
|
||||
<Spin spinning={loading >= 0} percent={loading == 0 ? 'auto' : loading}>
|
||||
<Upload
|
||||
multiple={false} maxCount={1} data={getUploadData}
|
||||
@ -84,7 +76,14 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
||||
{data.content ? <>
|
||||
<img src={data.content}/>
|
||||
<div className={styles.uploadTips}>
|
||||
<span>更换图片</span>
|
||||
{!onlyUpload && <Popconfirm
|
||||
title={<div style={{minWidth: 150}}><span>请确认删除此删除此图片?</span></div>}
|
||||
onConfirm={onRemove}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className={styles.imageDelete}><CloseOutlined/></span>
|
||||
</Popconfirm>}
|
||||
</div>
|
||||
</> : <div className={styles.imagePlaceholder}>
|
||||
<div className={'text-center'}>
|
||||
@ -95,7 +94,19 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
||||
</div>
|
||||
</Upload>
|
||||
</Spin>
|
||||
</div> : <div className={styles.uploadImage}><img src={data.content}/></div>}
|
||||
</div> : <div className={styles.uploadImage}>
|
||||
<img src={data.content}/>
|
||||
<div className={styles.uploadTips}>
|
||||
{!onlyUpload && <Popconfirm
|
||||
title={<div style={{minWidth: 150}}><span>请确认删除此删除此图片?</span></div>}
|
||||
onConfirm={onRemove}
|
||||
okText="删除"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className={styles.imageDelete}><CloseOutlined/></span>
|
||||
</Popconfirm>}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -108,7 +119,7 @@ export function BlockText({data, editable, onChange, isFirstBlock}: Props) {
|
||||
onChange={e => {
|
||||
onChange?.({type: 'text', content: e.target.value})
|
||||
}}
|
||||
placeholder={'请输入文本内容'} value={data.content} autoSize={{minRows: 3, maxRows: 8}}
|
||||
placeholder={'请输入文本内容'} value={data.content} autoSize={{minRows: 4, maxRows: 5}}
|
||||
variant={"borderless"}/>
|
||||
</div> : <p className="p-2">{data.content}</p>}
|
||||
</div>
|
||||
|
@ -97,7 +97,7 @@ export const IconAddImage = ({style, className}: IconProps) => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconAdd = ({style, className}: IconProps) => (
|
||||
export const IconAddCircle = ({style, className}: IconProps) => (
|
||||
<svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em" height="1em" viewBox="0 0 1024 1024" version="1.1">
|
||||
<path
|
||||
@ -109,6 +109,17 @@ export const IconAdd = ({style, className}: IconProps) => (
|
||||
fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
export const IconAdd = ({style, className}: IconProps) => (
|
||||
<svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em" height="1em" viewBox="0 0 1024 1024" version="1.1">
|
||||
<path d="M544 288v448c0 17.6-14.4 32-32 32s-32-14.4-32-32V288c0-17.6 14.4-32 32-32s32 14.4 32 32z"
|
||||
fill="currentColor"/>
|
||||
<path d="M736 544H288c-17.6 0-32-14.4-32-32s14.4-32 32-32h448c17.6 0 32 14.4 32 32s-14.4 32-32 32z"
|
||||
fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
|
||||
|
||||
export const IconPlay = ({style, className}: IconProps) => (
|
||||
<svg className={`svg-icon ${className || ''} icon-delete`} style={style} xmlns="http://www.w3.org/2000/svg"
|
||||
|
47
src/pages/news/components/button-delete-batch.tsx
Normal file
47
src/pages/news/components/button-delete-batch.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import {App, Button} from "antd";
|
||||
import {showToast} from "@/components/message.ts";
|
||||
import {useState} from "react";
|
||||
import {push2article} from "@/service/api/news.ts";
|
||||
import {IconArrowRight, IconDelete} from "@/components/icons";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export default function ButtonDeleteBatch(props: { ids: Id[]; }) {
|
||||
const {modal} = App.useApp();
|
||||
const [loading,setLoading] = useState(false)
|
||||
const navigate = useNavigate();
|
||||
const handlePush = () => {
|
||||
setLoading(true)
|
||||
push2article(props.ids).then(() => {
|
||||
showToast('删除成功', 'success')
|
||||
navigate('/edit')
|
||||
}).catch(() => {
|
||||
showToast('删除失败', 'error')
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
const onPushClick = () => {
|
||||
if (props.ids.length === 0) {
|
||||
showToast('请选择要删除的新闻', 'warning')
|
||||
return
|
||||
}
|
||||
modal.confirm({
|
||||
title: '操作提示',
|
||||
content: '是否确定删除选中的新闻?',
|
||||
onOk: handlePush,
|
||||
centered: true
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
loading={loading}
|
||||
onClick={onPushClick}
|
||||
className='bg-gray-400 hover:bg-gray-500'
|
||||
>
|
||||
<span className={'text'}>批量删除</span>
|
||||
<IconDelete className={'text-white'} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -2,9 +2,10 @@ import {Button, Modal} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {showErrorToast, showToast} from "@/components/message.ts";
|
||||
import {push2video} from "@/service/api/article.ts";
|
||||
import {IconArrowRight, IconDelete} from "@/components/icons";
|
||||
|
||||
|
||||
export default function ButtonPush2Video(props: { ids: Id[];onSuccess?:()=>void; }) {
|
||||
export default function ButtonPush2Video(props: { ids: Id[]; onSuccess?: () => void; }) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const handlePush = () => {
|
||||
setLoading(true)
|
||||
@ -27,6 +28,17 @@ export default function ButtonPush2Video(props: { ids: Id[];onSuccess?:()=>void;
|
||||
})
|
||||
}
|
||||
return (
|
||||
<Button type="primary" loading={loading} onClick={onPushClick}>一键开播</Button>
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
className='btn-action btn-gray-300'
|
||||
icon={<IconArrowRight className={'text-white'}/>}
|
||||
onClick={onPushClick}
|
||||
iconPosition={'end'}
|
||||
>
|
||||
<span className={'text'}>生成视频</span>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -13,6 +13,7 @@ import InfiniteScroller, {InfiniteScrollerRef} from "@/components/scoller/infini
|
||||
import {IconDelete, IconEdit} from "@/components/icons";
|
||||
import {clsx} from "clsx";
|
||||
import ButtonToTop from "@/components/scoller/button-to-top.tsx";
|
||||
import ButtonDeleteBatch from "@/pages/news/components/button-delete-batch.tsx";
|
||||
|
||||
|
||||
export default function NewEdit() {
|
||||
@ -127,9 +128,8 @@ export default function NewEdit() {
|
||||
|
||||
<div className="page-action">
|
||||
<ButtonToTop visible={state.showToTop} onClick={()=>scrollerRef.current?.scrollToPosition(0)} />
|
||||
<div>
|
||||
<ButtonPush2Video ids={selectedRowKeys} onSuccess={refresh}/>
|
||||
</div>
|
||||
<ButtonDeleteBatch ids={selectedRowKeys} onSuccess={refresh}/>
|
||||
<ButtonPush2Video ids={selectedRowKeys} onSuccess={refresh}/>
|
||||
</div>
|
||||
</div>
|
||||
<ArticleEditModal
|
||||
|
Loading…
x
Reference in New Issue
Block a user