577 lines
16 KiB
JavaScript
577 lines
16 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
||
import { Form, Input, Table, Image, Space, Button, Modal, message } from "antd";
|
||
import baseRequest from "../../utils/baseRequest";
|
||
import ImageUploader from "../../components/ImageUploader";
|
||
import VideoUploader from "../../components/VideoUploader";
|
||
import VideoPlayer from "../../components/VideoPlayer";
|
||
const CreateAndEditPostContent = (props) => {
|
||
const { TextArea } = Input;
|
||
//表头
|
||
const columns = [
|
||
{
|
||
title: "动态信息",
|
||
dataIndex: "info",
|
||
key: "info",
|
||
render: (data) => (
|
||
<div>
|
||
<p>
|
||
动态id:<span className="text-red-400">{data.id}</span>
|
||
</p>
|
||
<p>
|
||
点赞:<span className="text-red-400">{data.like}</span>
|
||
</p>
|
||
<p>
|
||
创建时间:<span className="text-red-400">{data.ct}</span>
|
||
</p>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: "动态文案",
|
||
dataIndex: "content",
|
||
key: "content",
|
||
},
|
||
{
|
||
title: "媒体",
|
||
dataIndex: "media",
|
||
key: "media",
|
||
render: (data) => (
|
||
<div>
|
||
<Image.PreviewGroup
|
||
items={data?.images?.map((item) => item.urls[0])}
|
||
>
|
||
{data?.images?.map((item, index) => (
|
||
<Image
|
||
key={index}
|
||
src={item.urls[0]}
|
||
width={150}
|
||
style={{ marginBottom: 10 }}
|
||
/>
|
||
))}
|
||
</Image.PreviewGroup>
|
||
{data?.videos?.map((item, index) => (
|
||
<VideoPlayer key={index} url={item.urls[0]} />
|
||
))}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: "操作",
|
||
dataIndex: "opeartion",
|
||
key: "opeartion",
|
||
render: (_, record) => (
|
||
<div>
|
||
<Space>
|
||
<Space.Compact direction="vertical">
|
||
<Button type="primary" onClick={() => onClickEdit(record)}>
|
||
编辑
|
||
</Button>
|
||
<Button onClick={() => onClickDelete(record)}>删除</Button>
|
||
</Space.Compact>
|
||
</Space>
|
||
</div>
|
||
),
|
||
},
|
||
];
|
||
|
||
//控制编辑modal是否出现
|
||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||
|
||
//modal打开时的默认值
|
||
const [defaultValues, setDefaultValues] = useState({});
|
||
const [defaultMedia, setDefaultMedia] = useState({});
|
||
|
||
//点击modal取消按钮
|
||
const handleCancel = () => {
|
||
setDefaultValues({});
|
||
setDefaultMedia({});
|
||
setEditImageId([]);
|
||
setEditVideoId([]);
|
||
setIsEditModalOpen(false);
|
||
};
|
||
|
||
//点击编辑按钮
|
||
const onClickEdit = (record) => {
|
||
setDefaultValues(record);
|
||
setDefaultMedia(record.media);
|
||
setIsEditModalOpen(true);
|
||
};
|
||
|
||
//保存上传的媒体id
|
||
const [editImageId, setEditImageId] = useState([]);
|
||
const [editVideoId, setEditVideoId] = useState([]);
|
||
|
||
//删除默认的媒体
|
||
const handleDeleteMedia = (type, id) => {
|
||
if (type === "video") {
|
||
setDefaultMedia({
|
||
...defaultMedia,
|
||
video_ids: [],
|
||
});
|
||
return;
|
||
}
|
||
if (type === "image") {
|
||
setDefaultMedia({
|
||
...defaultMedia,
|
||
image_ids: defaultMedia.image_ids.filter((item) => item !== id),
|
||
images: defaultMedia.images.filter((item) => item.id !== id),
|
||
});
|
||
return;
|
||
}
|
||
};
|
||
|
||
//点击确认提交更改动态
|
||
const handleChange = async (value) => {
|
||
const imageIds = [...defaultMedia.image_ids, ...editImageId];
|
||
const videoIds = [...defaultMedia.video_ids, ...editVideoId];
|
||
if (imageIds.length === 0 && videoIds.length === 0) {
|
||
alert("图片或视频不可为空");
|
||
return;
|
||
}
|
||
if (imageIds.length !== 0 && videoIds.length !== 0) {
|
||
alert("不可同时上传图片和视频");
|
||
return;
|
||
}
|
||
if (imageIds.length > 9) {
|
||
alert("图片不可超过9张");
|
||
return;
|
||
}
|
||
if (videoIds.length > 1) {
|
||
alert("视频不可超过1个");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const base = baseRequest();
|
||
const response = await fetch("/op/moment/update", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
id: defaultValues.info.id,
|
||
text: value.content,
|
||
media_component: {
|
||
image_ids: imageIds,
|
||
video_ids: videoIds,
|
||
},
|
||
...base,
|
||
}),
|
||
});
|
||
const temData = await response.json();
|
||
if (temData.ret === -1) {
|
||
alert(temData.msg);
|
||
return;
|
||
}
|
||
setShowData(
|
||
showData.filter((item) => item.info.id !== defaultValues.info.id)
|
||
);
|
||
handleCancel();
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
|
||
//点击删除按钮
|
||
const onClickDelete = async (record) => {
|
||
try {
|
||
const base = baseRequest();
|
||
const response = await fetch("/op/moment/delete", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
id: record.info.id,
|
||
...base,
|
||
}),
|
||
});
|
||
const temData = await response.json();
|
||
if (temData.ret === -1) {
|
||
alert(temData.msg);
|
||
return;
|
||
}
|
||
setShowData(showData.filter((item) => item.info.id !== record.info.id));
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
|
||
//展示的数据
|
||
const [showData, setShowData] = useState([]);
|
||
|
||
//获取数据
|
||
const getData = async () => {
|
||
try {
|
||
const base = baseRequest();
|
||
const response = await fetch("/op/moment/list", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
ct_upper_bound: Math.floor(new Date().getTime() / 1000),
|
||
offset: 0,
|
||
limit: 2000,
|
||
...base,
|
||
}),
|
||
});
|
||
const temData = await response.json();
|
||
if (temData.ret === -1) {
|
||
alert(temData.msg);
|
||
return;
|
||
}
|
||
//匹配表格格式
|
||
const structedData = temData.data.list.map((item, index) => {
|
||
return {
|
||
key: index,
|
||
info: {
|
||
id: item.id,
|
||
like: item.thumbs_up_num,
|
||
ct: new Date(item.ct * 1000).toLocaleString(),
|
||
},
|
||
content: item.text,
|
||
media: item.media_component,
|
||
};
|
||
});
|
||
setShowData(structedData);
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
useEffect(() => {
|
||
getData();
|
||
}, []);
|
||
|
||
//搜索
|
||
const search = async (value) => {
|
||
try {
|
||
const base = baseRequest();
|
||
const detailResponse = await fetch(`/op/streamer/list_ext_by_user_id`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
user_id: parseInt(value.userId, 10),
|
||
...base,
|
||
}),
|
||
});
|
||
const detailData = await detailResponse.json();
|
||
if (detailData.ret === -1) {
|
||
alert(detailData.msg);
|
||
return;
|
||
}
|
||
const response = await fetch("/op/moment/list_by_mid", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
mid: detailData.data.streamer_ext.mid,
|
||
...base,
|
||
}),
|
||
});
|
||
const temData = await response.json();
|
||
if (temData.ret === -1) {
|
||
alert(temData.msg);
|
||
return;
|
||
}
|
||
//匹配表格格式
|
||
const structedData = temData.data.list.map((item, index) => {
|
||
return {
|
||
key: index,
|
||
info: {
|
||
id: item.id,
|
||
like: item.thumbs_up_num,
|
||
ct: new Date(item.ct * 1000).toLocaleString(),
|
||
},
|
||
content: item.text,
|
||
media: item.media_component,
|
||
};
|
||
});
|
||
setShowData(structedData);
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
//表单提交失败
|
||
const onFinishFailed = (errorInfo) => {
|
||
console.log("Failed:", errorInfo);
|
||
};
|
||
|
||
//控制创建动态modal是否出现
|
||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||
|
||
//保存上传的媒体的id
|
||
const [imageId, setImageId] = useState([]);
|
||
const [videoId, setVideoId] = useState([]);
|
||
|
||
//在modal中搜索用户
|
||
const [userInfo, setUserInfo] = useState();
|
||
const modalSearch = async (value) => {
|
||
try {
|
||
const base = baseRequest();
|
||
const response = await fetch(`/op/account/list_by_user_id`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
user_id: parseInt(value.userId, 10),
|
||
...base,
|
||
}),
|
||
});
|
||
const data = await response.json();
|
||
if (data.ret === -1) {
|
||
alert(data.msg);
|
||
return;
|
||
}
|
||
setSelectedUser();
|
||
setUserInfo(data.data.account);
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
//选中用户
|
||
const [selectedUser, setSelectedUser] = useState();
|
||
const handleSelected = () => {
|
||
if (selectedUser) {
|
||
setSelectedUser();
|
||
return;
|
||
}
|
||
setSelectedUser(userInfo.mid);
|
||
};
|
||
//提交创建动态
|
||
const [form] = Form.useForm();
|
||
const handleSubmit = async (value) => {
|
||
if (!selectedUser) {
|
||
alert("还未选中用户");
|
||
return;
|
||
}
|
||
if (imageId.length === 0 && videoId.length === 0) {
|
||
alert("图片或视频不可为空");
|
||
return;
|
||
}
|
||
if (imageId.length !== 0 && videoId.length !== 0) {
|
||
alert("不可同时上传图片和视频");
|
||
return;
|
||
}
|
||
if (imageId.length > 9) {
|
||
alert("图片不可超过9张");
|
||
return;
|
||
}
|
||
if (videoId.length > 1) {
|
||
alert("视频不可超过1个");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const base = baseRequest();
|
||
const response = await fetch(`/op/moment/create`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
mid: selectedUser,
|
||
status: 2,
|
||
text: value.content,
|
||
media_component: { image_ids: imageId, video_ids: videoId },
|
||
...base,
|
||
}),
|
||
});
|
||
const data = await response.json();
|
||
if (data.ret === -1) {
|
||
alert(data.msg);
|
||
return;
|
||
}
|
||
message.success("发布成功");
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
|
||
form.resetFields();
|
||
setUserInfo();
|
||
setSelectedUser();
|
||
setIsModalOpen(false);
|
||
setImageId([]);
|
||
setVideoId([]);
|
||
};
|
||
//关闭弹窗
|
||
const handleCancelModal = () => {
|
||
form.resetFields();
|
||
setUserInfo();
|
||
setSelectedUser();
|
||
setIsModalOpen(false);
|
||
setImageId([]);
|
||
setVideoId([]);
|
||
};
|
||
|
||
return (
|
||
<div className="mt-4" style={{ marginLeft: 20, marginRight: 20 }}>
|
||
<Form name="search" onFinish={search} onFinishFailed={onFinishFailed}>
|
||
<Space style={{ marginBottom: 20 }}>
|
||
<Form.Item label="主播ID" name="userId" style={{ margin: 0 }}>
|
||
<Input type="number" />
|
||
</Form.Item>
|
||
<Button type="primary" htmlType="submit">
|
||
搜索
|
||
</Button>
|
||
</Space>
|
||
</Form>
|
||
<Button
|
||
className="mb-4"
|
||
type="primary"
|
||
onClick={() => setIsModalOpen(true)}
|
||
>
|
||
创建动态
|
||
</Button>
|
||
<Form name="remarks">
|
||
<Table
|
||
columns={columns}
|
||
dataSource={showData}
|
||
pagination={{ pageSize: 20 }}
|
||
scroll={{ y: window.innerHeight - 300 }}
|
||
/>
|
||
</Form>
|
||
{/* 重复判断isModalOpen是为了重新渲染ImageUploader和VideoUploader组件 */}
|
||
{isModalOpen && (
|
||
<Modal footer={null} open={isModalOpen} onCancel={handleCancelModal}>
|
||
<p className="text-sm text-red-400 font-bold">
|
||
*请选中主播后再发布,并确保文案不为空,图片数不超过9,视频数不超过1,图片视频不同时存在
|
||
</p>
|
||
<Form name="modal_search" onFinish={modalSearch}>
|
||
<Space style={{ marginBottom: 20 }}>
|
||
<Form.Item label="ID" name="userId" style={{ margin: 0 }}>
|
||
<Input type="number" />
|
||
</Form.Item>
|
||
<Button type="primary" htmlType="submit">
|
||
搜索
|
||
</Button>
|
||
</Space>
|
||
</Form>
|
||
{userInfo && (
|
||
<div
|
||
className={`flex flex-row items-center p-2 rounded-xl ${
|
||
selectedUser ? "bg-green-300" : "bg-gray-300"
|
||
}`}
|
||
>
|
||
<Image
|
||
src={userInfo?.avatar?.images[0].urls[0]}
|
||
width={80}
|
||
height={80}
|
||
/>
|
||
<div className="flex flex-col justify-center mx-4">
|
||
<p className="text-lg font-bold">ID:{userInfo.user_id}</p>
|
||
<p className="text-lg font-bold">昵称:{userInfo.name}</p>
|
||
</div>
|
||
<Button
|
||
type={selectedUser ? "default" : "primary"}
|
||
onClick={handleSelected}
|
||
>
|
||
{selectedUser ? "取消选中" : "选中用户"}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
<Form
|
||
className="mt-4 flex flex-col"
|
||
form={form}
|
||
onFinish={handleSubmit}
|
||
>
|
||
<Space>
|
||
<Form.Item
|
||
label="文案"
|
||
name="content"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: "请填写文案",
|
||
},
|
||
]}
|
||
style={{ margin: 0 }}
|
||
>
|
||
<TextArea />
|
||
</Form.Item>
|
||
</Space>
|
||
<ImageUploader setIds={setImageId} />
|
||
<VideoUploader setIds={setVideoId} />
|
||
<Button className="ml-8" type="primary" htmlType="submit">
|
||
确认
|
||
</Button>
|
||
</Form>
|
||
</Modal>
|
||
)}
|
||
{/* 编辑动态内容的弹窗,重复判断isEditModalOpen是为了重新渲染ImageUploader和VideoUploader组件 */}
|
||
{isEditModalOpen && (
|
||
<Modal footer={null} open={isEditModalOpen} onCancel={handleCancel}>
|
||
<p className="text-sm text-red-400 font-bold">
|
||
*请确保文案不为空,图片数不超过9,视频数不超过1,图片视频不同时存在
|
||
</p>
|
||
<Form className="mt-4 flex flex-col" onFinish={handleChange}>
|
||
<Space>
|
||
<Form.Item
|
||
label="文案"
|
||
name="content"
|
||
initialValue={defaultValues.content}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: "请填写文案",
|
||
},
|
||
]}
|
||
style={{ margin: 0 }}
|
||
>
|
||
<TextArea />
|
||
</Form.Item>
|
||
</Space>
|
||
{defaultMedia.image_ids.length !== 0 && (
|
||
<div className="flex flex-row flex-wrap gap-2 mt-4">
|
||
{defaultMedia.images.map((item) => (
|
||
<div key={item.urls[0]} className="relative">
|
||
<Image src={item.urls[0]} width={100} />
|
||
<Button
|
||
className="absolute top-0 left-0 w-full"
|
||
danger
|
||
type="primary"
|
||
onClick={() => handleDeleteMedia("image", item.id)}
|
||
>
|
||
删除
|
||
</Button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
{defaultMedia.video_ids.length !== 0 && (
|
||
<div className="flex flex-row mt-4">
|
||
<div className="relative">
|
||
<div className="mr-auto">
|
||
<VideoPlayer url={defaultMedia.videos[0].urls[0]} />
|
||
</div>
|
||
<Button
|
||
className="absolute top-0 left-0 w-full"
|
||
danger
|
||
type="primary"
|
||
onClick={() => handleDeleteMedia("video")}
|
||
>
|
||
删除
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
<ImageUploader setIds={setEditImageId} />
|
||
<VideoUploader setIds={setEditVideoId} />
|
||
<Button className="ml-8" type="primary" htmlType="submit">
|
||
确认
|
||
</Button>
|
||
</Form>
|
||
</Modal>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default function CreateAndEditPost() {
|
||
return <CreateAndEditPostContent />;
|
||
}
|