|
@@ -0,0 +1,192 @@
|
|
|
+// 添加计划弹窗
|
|
|
+
|
|
|
+import React, { useEffect, useState } from 'react';
|
|
|
+import { Modal, Form, Input, Select, Button, Card, Typography, message } from 'antd';
|
|
|
+import { WeComPlanType, WeVideoItem, AddWeComPlanParam } from '../../type';
|
|
|
+import { CloseOutlined, PlusOutlined, CaretRightFilled } from '@ant-design/icons';
|
|
|
+import VideoSelectModal from '../videoSelectModal';
|
|
|
+import VideoPlayModal from '../videoPlayModal';
|
|
|
+
|
|
|
+const { Paragraph } = Typography;
|
|
|
+
|
|
|
+const AddPlanModal: React.FC<{
|
|
|
+ initType: string;
|
|
|
+ visible: boolean;
|
|
|
+ onClose: () => void;
|
|
|
+ onOk: (param: AddWeComPlanParam) => void;
|
|
|
+ isLoading: boolean;
|
|
|
+}> = ({ initType, visible, onClose, onOk, isLoading }) => {
|
|
|
+ const [form] = Form.useForm();
|
|
|
+ const type = Form.useWatch(['type'], form);
|
|
|
+ const [selectedVideos, setSelectedVideos] = useState<WeVideoItem[]>([]);
|
|
|
+ const [isVideoSelectVisible, setIsVideoSelectVisible] = useState(false);
|
|
|
+ const [playingVideo, setPlayingVideo] = useState<WeVideoItem | null>(null);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ form.setFieldsValue({
|
|
|
+ type: initType,
|
|
|
+ subChannel: '',
|
|
|
+ });
|
|
|
+ setSelectedVideos([]);
|
|
|
+ }, [visible]);
|
|
|
+ const handleSelectVideo = (videos: WeVideoItem[]) => {
|
|
|
+ setSelectedVideos(videos);
|
|
|
+ setIsVideoSelectVisible(false);
|
|
|
+ }
|
|
|
+ const removeVideo = (videoId: number) => {
|
|
|
+ setSelectedVideos(selectedVideos.filter((video) => video.videoId !== videoId));
|
|
|
+ }
|
|
|
+ const openVideoSelector = () => {
|
|
|
+ setIsVideoSelectVisible(true);
|
|
|
+ }
|
|
|
+ const handleChangeSelectVideo = (videoId: number, scene: 0 | 1) => {
|
|
|
+ setSelectedVideos(selectedVideos.map((video) => video.videoId === videoId ? { ...video, scene } : video));
|
|
|
+ }
|
|
|
+ const playVideo = (video: WeVideoItem) => {
|
|
|
+ setPlayingVideo(video);
|
|
|
+ }
|
|
|
+ const handleOk = () => {
|
|
|
+ form.validateFields().then((values) => {
|
|
|
+ if (selectedVideos.length === 0) {
|
|
|
+ message.error('请选择发布内容');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ onOk({
|
|
|
+ type: values.type,
|
|
|
+ subChannel: values.subChannel,
|
|
|
+ videoList: selectedVideos,
|
|
|
+ });
|
|
|
+
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const handleChangeType = (value: string) => {
|
|
|
+ form.setFieldsValue({
|
|
|
+ type: value,
|
|
|
+ });
|
|
|
+ setSelectedVideos(selectedVideos.map((video) => ({ ...video, scene: 0 })));
|
|
|
+ }
|
|
|
+
|
|
|
+ return <>
|
|
|
+ <Modal
|
|
|
+ width={1000}
|
|
|
+ open={visible}
|
|
|
+ onCancel={onClose}
|
|
|
+ title="创建企微发布内容"
|
|
|
+ destroyOnClose
|
|
|
+ onOk={handleOk}
|
|
|
+ okText="创建"
|
|
|
+ cancelText="取消"
|
|
|
+ confirmLoading={isLoading}
|
|
|
+ >
|
|
|
+ <Form form={form} layout="vertical">
|
|
|
+ <Form.Item label="发布类型" name="type">
|
|
|
+ <Select onChange={ handleChangeType }>
|
|
|
+ <Select.Option value="0">社群</Select.Option>
|
|
|
+ <Select.Option value="1">自动回复</Select.Option>
|
|
|
+ </Select>
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="子渠道"
|
|
|
+ name="subChannel"
|
|
|
+ rules={[
|
|
|
+ {
|
|
|
+ pattern: /^[a-zA-Z0-9]*$/,
|
|
|
+ message: '只能输入英文和数字',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ max: 20,
|
|
|
+ message: '最长不超过20个字符',
|
|
|
+ }
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <Input
|
|
|
+ placeholder="请输入子渠道"
|
|
|
+ allowClear
|
|
|
+ maxLength={20}
|
|
|
+ onKeyPress={(e) => {
|
|
|
+ if (!/[a-zA-Z0-9]/.test(e.key)) {
|
|
|
+ e.preventDefault();
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="发布内容" required>
|
|
|
+ <div className="flex flex-wrap gap-4">
|
|
|
+ {selectedVideos.map((video) => (
|
|
|
+ <Card
|
|
|
+ key={video.videoId}
|
|
|
+ className="w-[240px] relative group"
|
|
|
+ >
|
|
|
+ <Button
|
|
|
+ shape="circle"
|
|
|
+ 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"
|
|
|
+ size="small"
|
|
|
+ onClick={() => removeVideo(video.videoId)}
|
|
|
+ />
|
|
|
+ <div className="p-0">
|
|
|
+ <Paragraph className="mt-1 !mb-1" ellipsis={{ rows: 2, tooltip: true }} title={video.title}>{video.title}</Paragraph>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ className="relative"
|
|
|
+ style={{ paddingBottom: '79.8%' }}
|
|
|
+ onClick={(e) => {
|
|
|
+ e.stopPropagation(); // Prevent card selection if clicking thumbnail/play
|
|
|
+ playVideo(video);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <img src={video.cover} referrerPolicy="no-referrer" className="absolute inset-0 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 className="p-3 flex justify-between items-center relative z-10">
|
|
|
+ <Select
|
|
|
+ placeholder="选择场景"
|
|
|
+ style={{ width: 180 }}
|
|
|
+ defaultValue={video.scene ?? 0}
|
|
|
+ value={video.scene}
|
|
|
+ onChange={(value: 0 | 1) => handleChangeSelectVideo(video.videoId, value)}
|
|
|
+ options={ type === WeComPlanType.社群 ?
|
|
|
+ [{ label: '群发', value: 0 }, { label: '单发', value: 1 }]
|
|
|
+ : [{ label: '关注回复', value: 0 }]
|
|
|
+ }
|
|
|
+ onClick={(e) => e.stopPropagation()}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {/* Add Video Button - Conditionally Rendered */}
|
|
|
+ {selectedVideos.length < 3 && (
|
|
|
+ <div
|
|
|
+ className="w-[240px] h-[316px] flex flex-col justify-center items-center border border-dashed border-gray-300 rounded cursor-pointer dark:border-gray-600 hover:border-blue-500 hover:text-blue-500"
|
|
|
+ onClick={openVideoSelector} // Open the drawer on click
|
|
|
+ >
|
|
|
+ <PlusOutlined className="text-2xl mb-2" />
|
|
|
+ <Typography.Text>添加视频</Typography.Text>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+ <VideoSelectModal
|
|
|
+ visible={isVideoSelectVisible}
|
|
|
+ onClose={() => {
|
|
|
+ setIsVideoSelectVisible(false);
|
|
|
+ }}
|
|
|
+ onOk={handleSelectVideo}
|
|
|
+ initialSelectedIds={selectedVideos.map((video) => video.videoId)}
|
|
|
+ planType={type}
|
|
|
+ />
|
|
|
+ <VideoPlayModal
|
|
|
+ visible={!!playingVideo}
|
|
|
+ onClose={() => setPlayingVideo(null)}
|
|
|
+ videoUrl={playingVideo?.video || ''}
|
|
|
+ title={playingVideo?.title || ''}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ };
|
|
|
+
|
|
|
+export default AddPlanModal;
|