|
|
@@ -0,0 +1,477 @@
|
|
|
+import { MessageFu } from "@/utils/message";
|
|
|
+import { Button, Form, FormInstance, Input, Popconfirm, Radio } from "antd";
|
|
|
+import React, {
|
|
|
+ useCallback,
|
|
|
+ useEffect,
|
|
|
+ useMemo,
|
|
|
+ useRef,
|
|
|
+ useState,
|
|
|
+} from "react";
|
|
|
+import classNames from "classnames";
|
|
|
+import styles from "./index.module.scss";
|
|
|
+
|
|
|
+import ImageLazy from "@/components/ImageLazy";
|
|
|
+import { ImgListType, WallSaveAPIType } from "@/types";
|
|
|
+import WallLook from "../WallLook";
|
|
|
+import {
|
|
|
+ getWallDetailAPI,
|
|
|
+ wallUploadAPI,
|
|
|
+ setWallSave,
|
|
|
+} from "@/store/action/A2Wall";
|
|
|
+import { fileDomInitialFu } from "@/utils/domShow";
|
|
|
+import store from "@/store";
|
|
|
+import { baseURL } from "@/utils/http";
|
|
|
+import {
|
|
|
+ PlusOutlined,
|
|
|
+ EyeOutlined,
|
|
|
+ UploadOutlined,
|
|
|
+ CloseOutlined,
|
|
|
+ DownloadOutlined,
|
|
|
+} from "@ant-design/icons";
|
|
|
+import { ReactSortable } from "react-sortablejs";
|
|
|
+
|
|
|
+type Props = {
|
|
|
+ id: number;
|
|
|
+ closeMoalFu: (txt: string) => void;
|
|
|
+};
|
|
|
+
|
|
|
+function WallAdd({ id, closeMoalFu }: Props) {
|
|
|
+ const getInfoFu = useCallback(async (id: number) => {
|
|
|
+ const res = await getWallDetailAPI(id);
|
|
|
+ FormBoxRef.current?.setFieldsValue({ name: res.data.entity.name });
|
|
|
+ setImgNum(Number(res.data.entity.layout) as 1 | 2);
|
|
|
+
|
|
|
+ setTopType(res.data.entity.type)
|
|
|
+
|
|
|
+ if (res.data.entity.type === "img") {
|
|
|
+ const imgListRes = res.data.file;
|
|
|
+ setImgList(imgListRes);
|
|
|
+ imgListRef.current = imgListRes;
|
|
|
+ } else setVideoFile(res.data.file[0]);
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (id > 0) getInfoFu(id);
|
|
|
+ }, [getInfoFu, id]);
|
|
|
+
|
|
|
+ // 类型的选择
|
|
|
+ const [topType, setTopType] = useState<"img" | "video">("img");
|
|
|
+
|
|
|
+ // 视频的上传
|
|
|
+ const [videoFile, setVideoFile] = useState<ImgListType>({
|
|
|
+ id: 0,
|
|
|
+ fileName: "",
|
|
|
+ filePath: "",
|
|
|
+ });
|
|
|
+
|
|
|
+ // 表单的ref
|
|
|
+ const FormBoxRef = useRef<FormInstance>(null);
|
|
|
+ // 上传封面图的ref
|
|
|
+ const myInput = useRef<HTMLInputElement>(null);
|
|
|
+
|
|
|
+ // 版式的选择
|
|
|
+ const [imgNum, setImgNum] = useState<1 | 2>(1);
|
|
|
+
|
|
|
+ // 上传图片的校验
|
|
|
+ const [imgCheck, setImgCheck] = useState(false);
|
|
|
+
|
|
|
+ // 上传图片的全部数据(最多8张来进行切割)
|
|
|
+ const imgListRef = useRef<ImgListType[]>([]);
|
|
|
+
|
|
|
+ // 在页面展示的图片
|
|
|
+ const [imgList, setImgList] = useState<ImgListType[]>([]);
|
|
|
+
|
|
|
+ // 版式的选择改变,切割数组
|
|
|
+ useEffect(() => {
|
|
|
+ if (imgListRef.current.length) {
|
|
|
+ const newData = imgListRef.current.slice(0, imgNum);
|
|
|
+ setImgList(newData);
|
|
|
+ }
|
|
|
+ }, [imgNum]);
|
|
|
+
|
|
|
+ // 删除某一张图片
|
|
|
+ const delImgListFu = useCallback(
|
|
|
+ (id: number) => {
|
|
|
+ const newItems = imgList.filter((v) => v.id !== id);
|
|
|
+ setImgList(newItems);
|
|
|
+ imgListRef.current = imgListRef.current.filter((v) => v.id !== id);
|
|
|
+ },
|
|
|
+ [imgList]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 附件的校验
|
|
|
+ const btnOkCheck = useMemo(() => {
|
|
|
+ let flag = false;
|
|
|
+ if (topType === "img") {
|
|
|
+ if (imgList.length < imgNum) flag = true;
|
|
|
+ } else {
|
|
|
+ if (!videoFile.filePath) flag = true;
|
|
|
+ }
|
|
|
+ return flag;
|
|
|
+ }, [imgList.length, imgNum, topType, videoFile.filePath]);
|
|
|
+
|
|
|
+ // 没有通过校验
|
|
|
+ const onFinishFailed = useCallback(() => {
|
|
|
+ setImgCheck(true);
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ // 上传封面图
|
|
|
+ const handeUpPhoto = useCallback(
|
|
|
+ async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
+ if (e.target.files) {
|
|
|
+ // 拿到files信息
|
|
|
+ const filesInfo = e.target.files[0];
|
|
|
+
|
|
|
+ let anType = ["image/jpeg", "image/png"];
|
|
|
+ let anTit1 = "只支持png、jpg和jpeg格式!";
|
|
|
+ let anTit2 = "最大支持30M!";
|
|
|
+ let anSize = 30 * 1024 * 1024;
|
|
|
+
|
|
|
+ if (topType === "video") {
|
|
|
+ anType = ["video/mp4"];
|
|
|
+ anTit1 = "只支持mp4格式!";
|
|
|
+ anTit2 = "最大支持500M!";
|
|
|
+ anSize = 500 * 1024 * 1024;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验格式
|
|
|
+ if (!anType.includes(filesInfo.type)) {
|
|
|
+ e.target.value = "";
|
|
|
+ return MessageFu.warning(anTit1);
|
|
|
+ }
|
|
|
+ // 校验大小
|
|
|
+ if (filesInfo.size > anSize) {
|
|
|
+ e.target.value = "";
|
|
|
+ return MessageFu.warning(anTit2);
|
|
|
+ }
|
|
|
+ // 创建FormData对象
|
|
|
+ const fd = new FormData();
|
|
|
+ // 把files添加进FormData对象(‘photo’为后端需要的字段)
|
|
|
+ fd.append("type", "img");
|
|
|
+ fd.append("file", filesInfo);
|
|
|
+
|
|
|
+ e.target.value = "";
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await wallUploadAPI(fd);
|
|
|
+ if (res.code === 0) {
|
|
|
+ MessageFu.success("上传成功!");
|
|
|
+ if (topType === "img") {
|
|
|
+ setImgList([...imgList, res.data]);
|
|
|
+ imgListRef.current.unshift(res.data);
|
|
|
+ } else setVideoFile(res.data);
|
|
|
+ }
|
|
|
+ fileDomInitialFu();
|
|
|
+ } catch (error) {
|
|
|
+ fileDomInitialFu();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ [imgList, topType]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 通过校验点击确定
|
|
|
+ const onFinish = useCallback(
|
|
|
+ async (value: { name: string }) => {
|
|
|
+ console.log("通过校验,点击确定");
|
|
|
+ setImgCheck(true);
|
|
|
+
|
|
|
+ if (topType === "img" && imgList.length < imgNum) return;
|
|
|
+ if (topType === "video" && !videoFile.filePath) return;
|
|
|
+
|
|
|
+ const fileIds =
|
|
|
+ topType === "img"
|
|
|
+ ? imgList.map((v) => v.id).join(",")
|
|
|
+ : videoFile.id + "";
|
|
|
+
|
|
|
+ const obj: WallSaveAPIType = {
|
|
|
+ id: id > 0 ? id : null,
|
|
|
+ fileIds,
|
|
|
+ name: value.name,
|
|
|
+ type: topType,
|
|
|
+ layout: imgNum,
|
|
|
+ };
|
|
|
+
|
|
|
+ const res = await setWallSave(obj);
|
|
|
+
|
|
|
+ if (res.code === 0) {
|
|
|
+ MessageFu.success(id > 0 ? "编辑成功!" : "新增成功!");
|
|
|
+ closeMoalFu(id > 0 ? "编辑" : "新增");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ [
|
|
|
+ closeMoalFu,
|
|
|
+ id,
|
|
|
+ imgList,
|
|
|
+ imgNum,
|
|
|
+ topType,
|
|
|
+ videoFile.filePath,
|
|
|
+ videoFile.id,
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 点击预览效果
|
|
|
+ const [lookImg, setLookImg] = useState(false);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={styles.WallAdd}>
|
|
|
+ <div className="main">
|
|
|
+ <Form
|
|
|
+ ref={FormBoxRef}
|
|
|
+ name="basic"
|
|
|
+ labelCol={{ span: 3 }}
|
|
|
+ onFinish={onFinish}
|
|
|
+ onFinishFailed={onFinishFailed}
|
|
|
+ autoComplete="off"
|
|
|
+ >
|
|
|
+ <div className="myformBox">
|
|
|
+ <div className="label">
|
|
|
+ <span>*</span> 类型:
|
|
|
+ </div>
|
|
|
+ <div className="myformBoxR">
|
|
|
+ <Radio.Group
|
|
|
+ value={topType}
|
|
|
+ onChange={(e) => setTopType(e.target.value)}
|
|
|
+ >
|
|
|
+ <Radio value="img">海报</Radio>
|
|
|
+ <Radio value="video">视频</Radio>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="名称"
|
|
|
+ name="name"
|
|
|
+ rules={[{ required: true, message: "请输入名称!" }]}
|
|
|
+ getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
|
|
|
+ >
|
|
|
+ <Input maxLength={25} showCount placeholder="请输入内容" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <input
|
|
|
+ id="upInput"
|
|
|
+ type="file"
|
|
|
+ accept={topType === "img" ? ".png,.jpg,.jpeg" : ".mp4"}
|
|
|
+ ref={myInput}
|
|
|
+ onChange={(e) => handeUpPhoto(e)}
|
|
|
+ />
|
|
|
+
|
|
|
+ {topType === "img" ? (
|
|
|
+ <>
|
|
|
+ <div className="myformBox">
|
|
|
+ <div className="label">
|
|
|
+ <span>*</span> 版式:
|
|
|
+ </div>
|
|
|
+ <div className="myformBoxR">
|
|
|
+ <Radio.Group
|
|
|
+ onChange={(e) => setImgNum(e.target.value)}
|
|
|
+ value={imgNum}
|
|
|
+ >
|
|
|
+ <Radio value={1}>1</Radio>
|
|
|
+ <Radio value={2}>2</Radio>
|
|
|
+ </Radio.Group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 图片上传 */}
|
|
|
+ <div className="myformBox myformBox0">
|
|
|
+ <div className="label"></div>
|
|
|
+ <div className="myformBoxR">
|
|
|
+ <div className="fileBoxRow_r">
|
|
|
+ <div className="upImgBox">
|
|
|
+ <div
|
|
|
+ hidden={imgList.length >= imgNum}
|
|
|
+ className="fileBoxRow_up"
|
|
|
+ onClick={() => myInput.current?.click()}
|
|
|
+ >
|
|
|
+ <PlusOutlined />
|
|
|
+ </div>
|
|
|
+ <ReactSortable
|
|
|
+ list={imgList}
|
|
|
+ setList={setImgList}
|
|
|
+ className="fileImgListBox"
|
|
|
+ >
|
|
|
+ {imgList.map((v) => (
|
|
|
+ <div className="fileBoxRow_r_img" key={v.id}>
|
|
|
+ {v.filePath ? (
|
|
|
+ <ImageLazy
|
|
|
+ noLook
|
|
|
+ width={100}
|
|
|
+ height={100}
|
|
|
+ src={v.filePath}
|
|
|
+ />
|
|
|
+ ) : null}
|
|
|
+
|
|
|
+ <Popconfirm
|
|
|
+ title="删除后无法恢复,是否删除?"
|
|
|
+ okText="删除"
|
|
|
+ cancelText="取消"
|
|
|
+ onConfirm={() => delImgListFu(v.id!)}
|
|
|
+ >
|
|
|
+ <div className="clearCover">
|
|
|
+ <CloseOutlined />
|
|
|
+ </div>
|
|
|
+ </Popconfirm>
|
|
|
+
|
|
|
+ {/* 下面的预览和下载 */}
|
|
|
+ <div className="fileImgListLDBox">
|
|
|
+ <EyeOutlined
|
|
|
+ onClick={() =>
|
|
|
+ store.dispatch({
|
|
|
+ type: "layout/lookBigImg",
|
|
|
+ payload: {
|
|
|
+ url: baseURL + v.filePath,
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <a
|
|
|
+ href={baseURL + v.filePath}
|
|
|
+ download
|
|
|
+ target="_blank"
|
|
|
+ rel="noreferrer"
|
|
|
+ >
|
|
|
+ <DownloadOutlined />
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </ReactSortable>
|
|
|
+ </div>
|
|
|
+ <div className="fileTit">
|
|
|
+ {imgList.length && imgList.length >= 2 ? (
|
|
|
+ <>
|
|
|
+ 按住鼠标可拖动图片调整顺序。
|
|
|
+ <br />
|
|
|
+ </>
|
|
|
+ ) : null}
|
|
|
+ {/* 建议尺寸:{12960 / imgNum}*1920 */}
|
|
|
+ 支持png、jpg和jpeg的图片格式;最大支持30M。
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ {/* 视频上传 */}
|
|
|
+ <div className="myformBox myformBox2">
|
|
|
+ <div className="label">
|
|
|
+ <span>*</span> 附件:
|
|
|
+ </div>
|
|
|
+ <div className="myformBoxR">
|
|
|
+ {videoFile.filePath ? (
|
|
|
+ <div className="fileInfo">
|
|
|
+ <div className="upSuccTxt">{videoFile.fileName}</div>
|
|
|
+ {/* 视频预览 */}
|
|
|
+ <div
|
|
|
+ className="clearCover"
|
|
|
+ hidden={!videoFile.filePath}
|
|
|
+ onClick={() =>
|
|
|
+ store.dispatch({
|
|
|
+ type: "layout/lookDom",
|
|
|
+ payload: {
|
|
|
+ src: videoFile.filePath!,
|
|
|
+ type: "video",
|
|
|
+ },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ >
|
|
|
+ <EyeOutlined />
|
|
|
+ </div>
|
|
|
+ {/* 视频下载 */}
|
|
|
+ <a
|
|
|
+ href={baseURL + videoFile.filePath}
|
|
|
+ download
|
|
|
+ target="_blank"
|
|
|
+ className="clearCover"
|
|
|
+ rel="noreferrer"
|
|
|
+ >
|
|
|
+ <DownloadOutlined />
|
|
|
+ </a>
|
|
|
+ {/* 视频删除 */}
|
|
|
+ <Popconfirm
|
|
|
+ title="删除后无法恢复,是否删除?"
|
|
|
+ okText="删除"
|
|
|
+ cancelText="取消"
|
|
|
+ onConfirm={() =>
|
|
|
+ setVideoFile({
|
|
|
+ id: 0,
|
|
|
+ fileName: "",
|
|
|
+ filePath: "",
|
|
|
+ })
|
|
|
+ }
|
|
|
+ >
|
|
|
+ <div className="clearCover">
|
|
|
+ <CloseOutlined />
|
|
|
+ </div>
|
|
|
+ </Popconfirm>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ <Button
|
|
|
+ onClick={() => myInput.current?.click()}
|
|
|
+ icon={<UploadOutlined />}
|
|
|
+ >
|
|
|
+ 上传
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ <div className="fileTit">
|
|
|
+ 仅支持MP4格式的视频文件,大小不得超过500MB。
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 校验提示 */}
|
|
|
+ <div
|
|
|
+ className={classNames(
|
|
|
+ "noUpThumb",
|
|
|
+ btnOkCheck && imgCheck ? "noUpThumbAc" : ""
|
|
|
+ )}
|
|
|
+ >
|
|
|
+ {topType === "img" ? `请上传 ${imgNum} 张图片!` : "请上传附件"}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 确定和取消按钮 */}
|
|
|
+ <br />
|
|
|
+ <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
|
|
|
+ <Button type="primary" htmlType="submit">
|
|
|
+ 提交
|
|
|
+ </Button>
|
|
|
+  
|
|
|
+ <Button
|
|
|
+ disabled={!imgList.length}
|
|
|
+ onClick={() => setLookImg(true)}
|
|
|
+ hidden={topType === "video"}
|
|
|
+ >
|
|
|
+ 预览效果
|
|
|
+ </Button>
|
|
|
+  
|
|
|
+ <Popconfirm
|
|
|
+ title="放弃编辑后,信息将不会保存!"
|
|
|
+ okText="放弃"
|
|
|
+ cancelText="取消"
|
|
|
+ onConfirm={() => closeMoalFu("取消")}
|
|
|
+ >
|
|
|
+ <Button>取消</Button>
|
|
|
+ </Popconfirm>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 点击预览效果出来的页面 */}
|
|
|
+ {lookImg ? (
|
|
|
+ <WallLook imgList={imgList} closeMoalFu={() => setLookImg(false)} />
|
|
|
+ ) : null}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+const MemoWallAdd = React.memo(WallAdd);
|
|
|
+
|
|
|
+export default MemoWallAdd;
|