|
@@ -1,29 +1,34 @@
|
|
-import React, { useState } from 'react';
|
|
|
|
-import { Modal, Form, Select, Button, Row, Col, Card, Typography } from 'antd';
|
|
|
|
-import { CloseOutlined, PlusOutlined, EditOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
|
|
|
-import VideoSelectModal from '../videoSelectModal'; // Import the new component
|
|
|
|
|
|
+import React, { useEffect, useState } from 'react';
|
|
|
|
+import { Modal, Form, Select, Button, Row, Col, Card, Typography, message } from 'antd';
|
|
|
|
+import { CloseOutlined, PlusOutlined, EditOutlined, PlayCircleOutlined, CaretRightFilled } from '@ant-design/icons';
|
|
|
|
+import VideoSelectModal from '../videoSelectModal';
|
|
|
|
+import EditTitleCoverModal from '../editTitleCoverModal';
|
|
|
|
+import { VideoItem } from '../types'; // Import from common types
|
|
|
|
+import { PlanData } from '../..';
|
|
|
|
|
|
const { Option } = Select;
|
|
const { Option } = Select;
|
|
const { Text } = Typography;
|
|
const { Text } = Typography;
|
|
|
|
|
|
-interface VideoItem {
|
|
|
|
- id: number;
|
|
|
|
- source: string;
|
|
|
|
- title: string;
|
|
|
|
- thumbnail: string; // URL to thumbnail image
|
|
|
|
- spreadEfficiency: number;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
interface AddPunlishPlanModalProps {
|
|
interface AddPunlishPlanModalProps {
|
|
visible: boolean;
|
|
visible: boolean;
|
|
onCancel: () => void;
|
|
onCancel: () => void;
|
|
onOk: (values: any) => void; // Pass form values on OK
|
|
onOk: (values: any) => void; // Pass form values on OK
|
|
|
|
+ actionType: 'add' | 'edit';
|
|
|
|
+ editPlanData?: PlanData;
|
|
}
|
|
}
|
|
|
|
|
|
-const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCancel, onOk }) => {
|
|
|
|
|
|
+const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCancel, onOk, actionType, editPlanData }) => {
|
|
const [form] = Form.useForm();
|
|
const [form] = Form.useForm();
|
|
- const [selectedVideos, setSelectedVideos] = useState<VideoItem[]>([]); // Start with empty selection
|
|
|
|
|
|
+ const [selectedVideos, setSelectedVideos] = useState<VideoItem[]>([]);
|
|
const [isVideoSelectVisible, setIsVideoSelectVisible] = useState(false);
|
|
const [isVideoSelectVisible, setIsVideoSelectVisible] = useState(false);
|
|
|
|
+ const [playingVideo, setPlayingVideo] = useState<VideoItem | null>(null); // State for video player modal
|
|
|
|
+ const [editingVideo, setEditingVideo] = useState<VideoItem | null>(null); // State for editing modal
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (actionType === 'edit') {
|
|
|
|
+ form.setFieldsValue(editPlanData);
|
|
|
|
+ }
|
|
|
|
+ }, [actionType, editPlanData]);
|
|
|
|
|
|
const handleOk = () => {
|
|
const handleOk = () => {
|
|
form
|
|
form
|
|
@@ -31,11 +36,16 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
.then((values) => {
|
|
.then((values) => {
|
|
// Ensure at least one video is selected before submitting
|
|
// Ensure at least one video is selected before submitting
|
|
if (selectedVideos.length === 0) {
|
|
if (selectedVideos.length === 0) {
|
|
- // Optionally show a message to the user
|
|
|
|
- console.error('Please select at least one video.');
|
|
|
|
|
|
+ message.error('请至少选择一个视频'); // Use Antd message
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- onOk({ ...values, videos: selectedVideos });
|
|
|
|
|
|
+ // Prepare data with potentially updated titles/thumbnails
|
|
|
|
+ const videosToSubmit = selectedVideos.map(v => ({
|
|
|
|
+ ...v,
|
|
|
|
+ title: v.customTitle || v.title,
|
|
|
|
+ thumbnail: v.customThumbnail || v.thumbnail,
|
|
|
|
+ }));
|
|
|
|
+ onOk({ ...values, videos: videosToSubmit });
|
|
})
|
|
})
|
|
.catch((info) => {
|
|
.catch((info) => {
|
|
console.log('Validate Failed:', info);
|
|
console.log('Validate Failed:', info);
|
|
@@ -51,7 +61,20 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
};
|
|
};
|
|
|
|
|
|
const handleVideoSelectionOk = (newlySelectedVideos: VideoItem[]) => {
|
|
const handleVideoSelectionOk = (newlySelectedVideos: VideoItem[]) => {
|
|
- setSelectedVideos(newlySelectedVideos);
|
|
|
|
|
|
+ // Merge existing custom data with newly selected videos
|
|
|
|
+ const currentCustomData = new Map<number, Partial<VideoItem>>();
|
|
|
|
+ selectedVideos.forEach(v => {
|
|
|
|
+ if (v.customTitle || v.customThumbnail) {
|
|
|
|
+ currentCustomData.set(v.id, { customTitle: v.customTitle, customThumbnail: v.customThumbnail });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const mergedVideos = newlySelectedVideos.map(newVideo => {
|
|
|
|
+ const customData = currentCustomData.get(newVideo.id);
|
|
|
|
+ return { ...newVideo, ...customData };
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ setSelectedVideos(mergedVideos);
|
|
setIsVideoSelectVisible(false);
|
|
setIsVideoSelectVisible(false);
|
|
};
|
|
};
|
|
|
|
|
|
@@ -59,13 +82,38 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
setIsVideoSelectVisible(false);
|
|
setIsVideoSelectVisible(false);
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const playVideo = (video: VideoItem) => {
|
|
|
|
+ setPlayingVideo(video);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const closeVideoPlayer = () => {
|
|
|
|
+ setPlayingVideo(null);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const openEditModal = (video: VideoItem) => {
|
|
|
|
+ setEditingVideo(video);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleEditOk = (updatedData: Partial<VideoItem>) => {
|
|
|
|
+ setSelectedVideos(currentVideos =>
|
|
|
|
+ currentVideos.map(v =>
|
|
|
|
+ v.id === editingVideo?.id ? { ...v, ...updatedData } : v
|
|
|
|
+ )
|
|
|
|
+ );
|
|
|
|
+ setEditingVideo(null); // Close modal
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleEditCancel = () => {
|
|
|
|
+ setEditingVideo(null);
|
|
|
|
+ };
|
|
|
|
+
|
|
return (
|
|
return (
|
|
<>
|
|
<>
|
|
<Modal
|
|
<Modal
|
|
title="创建发布计划"
|
|
title="创建发布计划"
|
|
open={visible}
|
|
open={visible}
|
|
onCancel={onCancel}
|
|
onCancel={onCancel}
|
|
- width={800} // Adjust width as needed
|
|
|
|
|
|
+ width={900} // Adjust width as needed
|
|
footer={[
|
|
footer={[
|
|
<Button key="back" onClick={onCancel}>
|
|
<Button key="back" onClick={onCancel}>
|
|
取消
|
|
取消
|
|
@@ -74,37 +122,43 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
确定
|
|
确定
|
|
</Button>,
|
|
</Button>,
|
|
]}
|
|
]}
|
|
|
|
+ zIndex={10}
|
|
|
|
+ destroyOnClose
|
|
>
|
|
>
|
|
<Form form={form} layout="vertical">
|
|
<Form form={form} layout="vertical">
|
|
- <Row gutter={16}>
|
|
|
|
- <Col span={12}>
|
|
|
|
- <Form.Item
|
|
|
|
- name="publisher"
|
|
|
|
- label="发布方"
|
|
|
|
- rules={[{ required: true, message: '请选择发布方' }]}
|
|
|
|
- >
|
|
|
|
- <Select placeholder="选择发布方">
|
|
|
|
- <Option value="platform">平台发布</Option>
|
|
|
|
- <Option value="user">用户发布</Option>
|
|
|
|
- </Select>
|
|
|
|
- </Form.Item>
|
|
|
|
- </Col>
|
|
|
|
- <Col span={12}>
|
|
|
|
- <Form.Item
|
|
|
|
- name="officialAccount"
|
|
|
|
- label="公众号名称"
|
|
|
|
- rules={[{ required: true, message: '请选择公众号' }]}
|
|
|
|
- >
|
|
|
|
- <Select placeholder="选择公众号">
|
|
|
|
- <Option value="account1">小慧爱厨房</Option>
|
|
|
|
- <Option value="account2">小阳看天下</Option>
|
|
|
|
- {/* Add more options as needed */}
|
|
|
|
- </Select>
|
|
|
|
- </Form.Item>
|
|
|
|
- </Col>
|
|
|
|
- </Row>
|
|
|
|
-
|
|
|
|
- <Form.Item label="发布场景">
|
|
|
|
|
|
+ <Form.Item
|
|
|
|
+ name="publisher"
|
|
|
|
+ label="发布方"
|
|
|
|
+ labelCol={{ span: 4 }}
|
|
|
|
+ labelAlign='left'
|
|
|
|
+ layout="horizontal"
|
|
|
|
+ rules={[{ required: true, message: '请选择发布方' }]}
|
|
|
|
+ >
|
|
|
|
+ <Select placeholder="选择发布方" allowClear className='!w-50'>
|
|
|
|
+ <Option value="platform">平台发布</Option>
|
|
|
|
+ <Option value="user">用户发布</Option>
|
|
|
|
+ </Select>
|
|
|
|
+ </Form.Item>
|
|
|
|
+ <Form.Item
|
|
|
|
+ name="officialAccount"
|
|
|
|
+ label="公众号名称"
|
|
|
|
+ labelCol={{ span: 4 }}
|
|
|
|
+ labelAlign='left'
|
|
|
|
+ layout="horizontal"
|
|
|
|
+ rules={[{ required: true, message: '请选择公众号' }]}
|
|
|
|
+ >
|
|
|
|
+ <Select placeholder="选择公众号" allowClear className='!w-50'>
|
|
|
|
+ <Option value="account1">小慧爱厨房</Option>
|
|
|
|
+ <Option value="account2">小阳看天下</Option>
|
|
|
|
+ {/* Add more options as needed */}
|
|
|
|
+ </Select>
|
|
|
|
+ </Form.Item>
|
|
|
|
+ <Form.Item
|
|
|
|
+ label="发布场景"
|
|
|
|
+ layout="horizontal"
|
|
|
|
+ labelCol={{ span: 4 }}
|
|
|
|
+ labelAlign='left'
|
|
|
|
+ >
|
|
<Text>关注回复</Text>
|
|
<Text>关注回复</Text>
|
|
</Form.Item>
|
|
</Form.Item>
|
|
|
|
|
|
@@ -113,24 +167,28 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
{selectedVideos.map((video) => (
|
|
{selectedVideos.map((video) => (
|
|
<Card
|
|
<Card
|
|
key={video.id}
|
|
key={video.id}
|
|
- className="w-[220px] relative group"
|
|
|
|
- bodyStyle={{ padding: 0 }}
|
|
|
|
|
|
+ className="w-[240px] relative group"
|
|
>
|
|
>
|
|
<Button
|
|
<Button
|
|
shape="circle"
|
|
shape="circle"
|
|
icon={<CloseOutlined />}
|
|
icon={<CloseOutlined />}
|
|
- className="absolute top-1 right-1 z-10 bg-gray-400 bg-opacity-50 border-none text-white hidden group-hover:inline-flex justify-center items-center"
|
|
|
|
|
|
+ className="!absolute top-1 right-1 z-10 bg-gray-400 bg-opacity-50 border-none text-white hidden group-hover:inline-flex justify-center items-center"
|
|
size="small"
|
|
size="small"
|
|
onClick={() => removeVideo(video.id)}
|
|
onClick={() => removeVideo(video.id)}
|
|
/>
|
|
/>
|
|
- <div className="p-3">
|
|
|
|
|
|
+ <div className="p-0">
|
|
<Text type="secondary" className="text-xs">{video.source}</Text>
|
|
<Text type="secondary" className="text-xs">{video.source}</Text>
|
|
- <Text className="block mt-1 mb-2 leading-tight line-clamp-2" title={video.title}>{video.title}</Text>
|
|
|
|
|
|
+ <Text className="block mt-1 mb-2 leading-tight line-clamp-2" title={video.customTitle || video.title}>{video.customTitle || video.title}</Text>
|
|
</div>
|
|
</div>
|
|
- <div className="relative h-[120px] bg-gray-200">
|
|
|
|
- <img src={video.thumbnail} alt={video.title} className="w-full h-full object-cover" />
|
|
|
|
- <div className="absolute inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
|
|
|
- <PlayCircleOutlined className="text-white text-4xl" />
|
|
|
|
|
|
+ <div className="relative h-[120px] bg-gray-200 cursor-pointer group/thumb"
|
|
|
|
+ onClick={(e) => {
|
|
|
|
+ e.stopPropagation(); // Prevent card selection if clicking thumbnail/play
|
|
|
|
+ playVideo(video);
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <img src={video.customThumbnail || video.thumbnail} alt={video.customTitle || video.title} className="w-full h-full object-cover" />
|
|
|
|
+ <div className="absolute inset-0 flex justify-center items-center cursor-pointer">
|
|
|
|
+ <CaretRightFilled className="!text-white text-4xl bg-black/20 rounded-full p-1 pl-2" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-3">
|
|
<div className="p-3">
|
|
@@ -138,7 +196,7 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
<Button
|
|
<Button
|
|
icon={<EditOutlined />}
|
|
icon={<EditOutlined />}
|
|
className="w-full mt-2"
|
|
className="w-full mt-2"
|
|
- // onClick={() => handleEditTitleCover(video.id)} // Add handler later
|
|
|
|
|
|
+ onClick={() => openEditModal(video)} // Open edit modal
|
|
>
|
|
>
|
|
编辑标题/封面
|
|
编辑标题/封面
|
|
</Button>
|
|
</Button>
|
|
@@ -149,7 +207,7 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
{/* Add Video Button - Conditionally Rendered */}
|
|
{/* Add Video Button - Conditionally Rendered */}
|
|
{selectedVideos.length < 3 && (
|
|
{selectedVideos.length < 3 && (
|
|
<div
|
|
<div
|
|
- className="w-[220px] h-[316px] flex flex-col justify-center items-center bg-gray-100 border border-dashed border-gray-300 rounded cursor-pointer hover:border-blue-500 hover:text-blue-500"
|
|
|
|
|
|
+ className="w-[240px] h-[316px] flex flex-col justify-center items-center bg-gray-100 border border-dashed border-gray-300 rounded cursor-pointer hover:border-blue-500 hover:text-blue-500"
|
|
onClick={openVideoSelector} // Open the drawer on click
|
|
onClick={openVideoSelector} // Open the drawer on click
|
|
>
|
|
>
|
|
<PlusOutlined className="text-2xl mb-2" />
|
|
<PlusOutlined className="text-2xl mb-2" />
|
|
@@ -168,6 +226,37 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, onCa
|
|
onOk={handleVideoSelectionOk}
|
|
onOk={handleVideoSelectionOk}
|
|
initialSelectedIds={selectedVideos.map(v => v.id)} // Pass current selection IDs
|
|
initialSelectedIds={selectedVideos.map(v => v.id)} // Pass current selection IDs
|
|
/>
|
|
/>
|
|
|
|
+
|
|
|
|
+ {/* Video Player Modal */}
|
|
|
|
+ <Modal
|
|
|
|
+ open={!!playingVideo}
|
|
|
|
+ onCancel={closeVideoPlayer}
|
|
|
|
+ title={playingVideo?.customTitle || playingVideo?.title}
|
|
|
|
+ footer={null}
|
|
|
|
+ destroyOnClose // Unmount video element when closed
|
|
|
|
+ width={720} // Adjust as needed
|
|
|
|
+ bodyStyle={{ padding: 0, background: '#000' }}
|
|
|
|
+ zIndex={20}
|
|
|
|
+ >
|
|
|
|
+ {playingVideo && (
|
|
|
|
+ <video
|
|
|
|
+ controls
|
|
|
|
+ autoPlay
|
|
|
|
+ className="w-full h-auto max-h-[80vh] block"
|
|
|
|
+ src={playingVideo.videoUrl}
|
|
|
|
+ >
|
|
|
|
+ Your browser does not support the video tag.
|
|
|
|
+ </video>
|
|
|
|
+ )}
|
|
|
|
+ </Modal>
|
|
|
|
+
|
|
|
|
+ {/* Edit Title/Cover Modal */}
|
|
|
|
+ <EditTitleCoverModal
|
|
|
|
+ visible={!!editingVideo}
|
|
|
|
+ onCancel={handleEditCancel}
|
|
|
|
+ onOk={handleEditOk}
|
|
|
|
+ video={editingVideo}
|
|
|
|
+ />
|
|
</>
|
|
</>
|
|
);
|
|
);
|
|
};
|
|
};
|