Explorar el Código

云学校 口述史更新

shaogen1995 hace 1 año
padre
commit
0a60fbe977

+ 129 - 0
src/components/ZupVideosTxt/index.module.scss

@@ -0,0 +1,129 @@
+.ZupVideosTxt {
+  width: 100%;
+
+  :global {
+
+    .zVmain {
+      width: 100%;
+
+      .file_upIcon {
+        display: flex;
+        align-items: center;
+
+        &>span {
+          display: block;
+          margin-left: 20px;
+          color: #ff4d4f;
+          position: relative;
+          opacity: 0;
+          top: -10px;
+          transition: all .2s;
+        }
+
+        .ZvUpTit {
+          opacity: 1;
+          top: 3px;
+        }
+      }
+
+
+
+      .fileBoxRow_r_tit {
+        height: 46px;
+        margin-top: 5px;
+        font-size: 14px;
+        color: rgb(126, 124, 124);
+
+
+      }
+
+      .noUpThumb {
+        position: relative;
+        overflow: hidden;
+        opacity: 0;
+        transition: top .2s;
+        color: #ff4d4f;
+        top: -10px;
+      }
+
+      .noUpThumbAc {
+        top: 0;
+        opacity: 1;
+      }
+
+      .zVRow {
+        padding-bottom: 30px;
+        position: relative;
+        border-bottom: 1px solid #ccc;
+        margin-bottom: 10px;
+
+        .zVR1 {
+          font-size: 16px;
+          display: flex;
+
+          &>div {
+            margin-right: 15px;
+            max-width: 650px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+
+            &>span {
+              font-weight: 700;
+            }
+          }
+
+          &>a {
+            display: block;
+            margin: 0 10px;
+            color: black;
+          }
+        }
+
+        .zVR2 {
+          margin-top: 15px;
+          display: flex;
+          justify-content: space-between;
+
+          .zVR2_1 {
+            width: 70%;
+          }
+
+          .zVR2_1Err .ant-input-affix-wrapper {
+            border-color: #ff4d4f !important;
+          }
+
+          .zVR2_2 {
+            width: 26%;
+
+            .zVR2_2Img {
+              position: absolute;
+              display: flex;
+              top: 0;
+              right: 0;
+
+              .zVR2_2ImgIcon {
+                margin-left: 5px;
+                display: flex;
+                flex-direction: column;
+                justify-content: space-around;
+
+                &>a {
+                  color: black;
+                }
+              }
+            }
+          }
+
+          .zVR2_2Err .ant-btn {
+            border-color: #ff4d4f !important;
+          }
+
+        }
+      }
+
+    }
+
+
+  }
+}

+ 362 - 0
src/components/ZupVideosTxt/index.tsx

@@ -0,0 +1,362 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import { forwardRef, useImperativeHandle } from "react";
+import { MessageFu } from "@/utils/message";
+import { API_upFile } from "@/store/action/layout";
+import { fileDomInitialFu } from "@/utils/domShow";
+import {
+  UploadOutlined,
+  EyeOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+} from "@ant-design/icons";
+import classNames from "classnames";
+import { baseURL } from "@/utils/http";
+import store from "@/store";
+import MyPopconfirm from "../MyPopconfirm";
+import { Button, Input } from "antd";
+import ImageLazy from "../ImageLazy";
+
+export type ZupVideosTxtType = {
+  fileName: string;
+  filePath: string;
+  id: number;
+  // type: "video" | "img";
+  // parentId: number | null;
+  son: { txt: string; thumb: string; thumbId: number };
+};
+
+type Props = {
+  size: number; //视频大小
+  fileNum: number; //数量限制
+  dirCode: string; //文件的code码
+  myUrl: string; //请求地址
+  fileCheck?: boolean; //是否检验
+  format?: string[]; //上传格式 默认["video/mp4"]
+  formatTxt?: string; //上传提示
+  checkTxt?: string;
+  upTxt?: string;
+  isLook?: boolean; //是不是查看
+  fromData?: any;
+  ref: any; //当前自己的ref,给父组件调用
+};
+
+function ZupVideosTxt(
+  {
+    size,
+    fileNum,
+    dirCode,
+    myUrl,
+    fileCheck = false,
+    format = ["video/mp4"],
+    formatTxt = ".mp4",
+    checkTxt = "请上传视频!",
+    upTxt = "",
+    isLook = false,
+    fromData,
+  }: Props,
+  ref: any
+) {
+  const [fileList, setFileList] = useState<ZupVideosTxtType[]>([]);
+
+  // 区分 一级视频上传 和 二级封面图上传
+  const [level, setLevel] = useState<0 | 1 | 2>(0);
+
+  const levelChangeFu = useCallback((val: 1 | 2) => {
+    setLevel(0);
+    setTimeout(() => {
+      setLevel(val);
+    }, 30);
+  }, []);
+
+  useEffect(() => {
+    if (level) myInput.current?.click();
+  }, [level]);
+
+  const fIdRef = useRef(0);
+
+  // 上传
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (e.target.files) {
+        // 拿到files信息
+        const filesInfo = e.target.files[0];
+        // console.log("-----", filesInfo.type);
+
+        const formatRes = level === 1 ? format : ["image/jpeg", "image/png"];
+        const formatTxtRes = level === 1 ? formatTxt : ".png,.jpg,.jpeg";
+        const sizeRes = level === 1 ? size : 1;
+
+        // 校验格式
+        if (!formatRes.includes(filesInfo.type)) {
+          e.target.value = "";
+          return MessageFu.warning(`只支持${formatTxtRes}格式!`);
+        }
+
+        // 校验大小
+        if (filesInfo.size > sizeRes * 1024 * 1024) {
+          e.target.value = "";
+          return MessageFu.warning(`最大支持${sizeRes}M!`);
+        }
+
+        // 创建FormData对象
+        const fd = new FormData();
+
+        fd.append("type", level === 1 ? "video" : "img");
+        fd.append("isDb", "true");
+        fd.append("dirCode", dirCode);
+        if (level === 2) fd.append("parentId", fIdRef.current + "");
+
+        fd.append("file", filesInfo);
+
+        if (fromData) {
+          for (const k in fromData) {
+            if (fromData[k]) fd.append(k, fromData[k]);
+          }
+        }
+
+        e.target.value = "";
+
+        try {
+          const res = await API_upFile(fd, myUrl);
+          if (res.code === 0) {
+            MessageFu.success("上传成功!");
+
+            const dataRes = res.data;
+
+            if (level === 1) {
+              setFileList([
+                ...fileList,
+                { ...dataRes, son: { txt: "", thumb: "" } },
+              ]);
+            } else if (level === 2) {
+              setFileList(
+                fileList.map((v) => ({
+                  ...v,
+                  son:
+                    v.id === fIdRef.current
+                      ? {
+                          txt: v.son.txt,
+                          thumb: dataRes.filePath,
+                          thumbId: dataRes.id,
+                        }
+                      : v.son,
+                }))
+              );
+            }
+          }
+          fileDomInitialFu();
+        } catch (error) {
+          fileDomInitialFu();
+        }
+      }
+    },
+    [dirCode, fileList, format, formatTxt, fromData, level, myUrl, size]
+  );
+
+  const myInput = useRef<HTMLInputElement>(null);
+
+  // 点击预览
+  const lookFileFu = useCallback((url: string, type: "video" | "img") => {
+    type === "video"
+      ? store.dispatch({
+          type: "layout/lookDom",
+          payload: { src: url, type: "video" },
+        })
+      : store.dispatch({
+          type: "layout/lookBigImg",
+          payload: { url: baseURL + url, show: true },
+        });
+  }, []);
+
+  // 上传了视频但是没有填写 采访问题 或者没有上传封面图
+  const isOneVideoFlag = useMemo(() => {
+    let flag = false;
+
+    if (fileList.some((v) => !v.son.thumb || !v.son.txt)) flag = true;
+
+    return flag;
+  }, [fileList]);
+
+  // 让父组件调用的 回显 附件 地址
+  const setFileComFileFu = useCallback((valList: ZupVideosTxtType[]) => {
+    setFileList(valList);
+  }, []);
+
+  // 让父组件调用的返回 附件 名字和路径
+  const fileComFileResFu = useCallback(() => {
+    return { fileList, isOneVideoFlag };
+  }, [fileList, isOneVideoFlag]);
+
+  // 可以让父组件调用子组件的方法
+  useImperativeHandle(ref, () => ({
+    setFileComFileFu,
+    fileComFileResFu,
+  }));
+
+  // 采访问题的输入框的改变 | 删除
+  const fileSonChangeFu = useCallback(
+    (id: number, val: string, key: "txt" | "thumb") => {
+      setFileList(
+        fileList.map((v) => ({
+          ...v,
+          son:
+            v.id === id
+              ? {
+                  ...v.son,
+                  [key]: val,
+                }
+              : v.son,
+        }))
+      );
+    },
+    [fileList]
+  );
+
+  return (
+    <div className={styles.ZupVideosTxt}>
+      <input
+        id="upInput"
+        type="file"
+        accept={level === 1 ? formatTxt : ".png,.jpg,.jpeg"}
+        ref={myInput}
+        onChange={(e) => handeUpPhoto(e)}
+      />
+
+      <div className="zVmain">
+        <div className="file_upIcon">
+          <Button
+            hidden={fileList.length >= fileNum}
+            disabled={isOneVideoFlag}
+            icon={<UploadOutlined rev={undefined} />}
+            onClick={() => levelChangeFu(1)}
+          >
+            上传
+          </Button>
+          <span className={classNames(isOneVideoFlag ? "ZvUpTit" : "")}>
+            请完整填写采访问题并且上传封面图!
+          </span>
+        </div>
+
+        <div className="fileBoxRow_r_tit" hidden={fileList.length >= fileNum}>
+          仅支持{formatTxt.replaceAll(".", "")}格式,最大支持{size}M。
+          {upTxt}
+          <div
+            className={classNames(
+              "noUpThumb",
+              fileList.length <= 0 && fileCheck ? "noUpThumbAc" : ""
+            )}
+          >
+            {checkTxt}
+          </div>
+        </div>
+
+        {fileList.map((v, i) => (
+          <div className="zVRow" key={v.id}>
+            <div className="zVR1">
+              <div title={v.fileName}>
+                <span>{i + 1}. </span>
+                {v.fileName}
+              </div>
+              <EyeOutlined
+                onClick={() => lookFileFu(v.filePath, "video")}
+                rev={undefined}
+              />
+              <a
+                href={baseURL + v.filePath}
+                download
+                target="_blank"
+                rel="noreferrer"
+              >
+                <DownloadOutlined rev={undefined} />
+              </a>
+
+              {isLook ? null : (
+                <MyPopconfirm
+                  txtK="删除"
+                  onConfirm={() =>
+                    setFileList(fileList.filter((c) => c.id !== v.id))
+                  }
+                  Dom={<CloseOutlined rev={undefined} />}
+                />
+              )}
+            </div>
+            <div className="zVR2">
+              <div
+                className={classNames("zVR2_1", v.son.txt ? "" : "zVR2_1Err")}
+              >
+                <Input
+                  maxLength={50}
+                  showCount
+                  placeholder="请输入采访问题"
+                  value={v.son.txt}
+                  onChange={(e) =>
+                    fileSonChangeFu(
+                      v.id,
+                      e.target.value.replace(/\s+/g, ""),
+                      "txt"
+                    )
+                  }
+                />
+              </div>
+              <div
+                className={classNames("zVR2_2", v.son.thumb ? "" : "zVR2_2Err")}
+              >
+                <Button
+                  hidden={!!v.son.thumb}
+                  icon={<UploadOutlined rev={undefined} />}
+                  onClick={() => {
+                    fIdRef.current = v.id;
+                    levelChangeFu(2);
+                  }}
+                >
+                  上传封面
+                </Button>
+
+                {v.son.thumb ? (
+                  <div className="zVR2_2Img">
+                    <ImageLazy
+                      width={90}
+                      height={90}
+                      src={v.son.thumb}
+                      noLook
+                    />
+                    <div className="zVR2_2ImgIcon">
+                      <EyeOutlined
+                        onClick={() => lookFileFu(v.son.thumb, "img")}
+                        rev={undefined}
+                      />
+                      <a
+                        href={baseURL + v.son.thumb}
+                        download
+                        target="_blank"
+                        rel="noreferrer"
+                      >
+                        <DownloadOutlined rev={undefined} />
+                      </a>
+
+                      {isLook ? null : (
+                        <MyPopconfirm
+                          txtK="删除"
+                          onConfirm={() => fileSonChangeFu(v.id, "", "thumb")}
+                          Dom={<CloseOutlined rev={undefined} />}
+                        />
+                      )}
+                    </div>
+                  </div>
+                ) : null}
+              </div>
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+export default forwardRef(ZupVideosTxt);

+ 44 - 17
src/pages/A7school/A7tab1M.tsx

@@ -19,6 +19,7 @@ import MyPopconfirm from "@/components/MyPopconfirm";
 import TextArea from "antd/es/input/TextArea";
 import ZupOne from "@/components/ZupOne";
 import { MessageFu } from "@/utils/message";
+import ZupVideosTxt, { ZupVideosTxtType } from "@/components/ZupVideosTxt";
 
 type Props = {
   fId: number;
@@ -35,6 +36,7 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
     const res = await A7_APIgetInfo(id);
     if (res.code === 0) {
       const data = res.data;
+      setDirCode(data.dirCode);
 
       FormBoxRef.current?.setFieldsValue(data);
 
@@ -44,10 +46,20 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
         filePath: data.thumb,
       });
       // 设置视频
-      ZupOneRef2.current?.setFileComFileFu({
-        fileName: data.fileName,
-        filePath: data.filePath,
+      const fileTemp: any[] = data.files;
+
+      let fileArr: ZupVideosTxtType[] = fileTemp.filter(
+        (v: any) => !v.parentId
+      );
+      fileArr = fileArr.map((v: any) => {
+        const obj = fileTemp.find((c: any) => c.parentId === v.id);
+        return {
+          ...v,
+          son: { txt: v.description, thumb: obj.filePath, thumbId: obj.id },
+        };
       });
+
+      ZupVideosRef.current?.setFileComFileFu(fileArr);
     }
   }, []);
 
@@ -61,11 +73,15 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
   useEffect(() => {
     getSelList();
     if (fId > 0) getInfoFu(fId);
+    else setDirCode(Date.now() + "");
   }, [fId, getInfoFu, getSelList]);
 
   // 附件的ref
   const ZupOneRef1 = useRef<any>(null);
-  const ZupOneRef2 = useRef<any>(null);
+  const ZupVideosRef = useRef<any>(null);
+
+  // 附件的code码
+  const [dirCode, setDirCode] = useState("");
 
   // 附件 是否 已经点击过确定
   const [fileCheck, setFileCheck] = useState(false);
@@ -80,15 +96,29 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
       setFileCheck(true);
       // 没有传 封面图 或者视频
       const coverUrl1 = ZupOneRef1.current?.fileComFileResFu();
-      const coverUrl2 = ZupOneRef2.current?.fileComFileResFu();
 
-      if (!coverUrl1.filePath || !coverUrl2.filePath) return;
+      if (!coverUrl1.filePath) return;
+
+      const coverUrl2Obj = ZupVideosRef.current?.fileComFileResFu() || {};
+      const fileIdsArr: ZupVideosTxtType[] = coverUrl2Obj.fileList || [];
+
+      if (coverUrl2Obj.isOneVideoFlag || fileIdsArr.length <= 0) return;
+
+      const fileIds: number[] = [];
+      const fileMap: any = {};
+
+      fileIdsArr.forEach((v) => {
+        Reflect.set(fileMap, v.id, v.son.txt);
+
+        fileIds.push(v.id);
+        if (v.son.thumbId) fileIds.push(v.son.thumbId);
+      });
 
       const obj = {
         ...values,
         id: fId > 0 ? fId : null,
-        fileName: coverUrl2.fileName,
-        filePath: coverUrl2.filePath,
+        fileIds: fileIds.join(","),
+        fileMap,
         thumb: coverUrl1.filePath,
       };
 
@@ -179,7 +209,7 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
                 isLook={false}
                 fileCheck={fileCheck}
                 size={2}
-                dirCode="schoolThumb"
+                dirCode={dirCode}
                 myUrl="cms/person/upload"
                 format={["image/jpeg", "image/png"]}
                 formatTxt="png、jpg和jpeg"
@@ -197,18 +227,15 @@ function A7tab1M({ fId, closeFu, addTableFu, upTableFu }: Props) {
               采访视频:
             </div>
             <div className="formRight">
-              <ZupOne
-                ref={ZupOneRef2}
+              <ZupVideosTxt
                 isLook={false}
                 fileCheck={fileCheck}
                 size={500}
-                dirCode="schoolVideo"
+                fileNum={10}
+                dirCode={dirCode}
                 myUrl="cms/person/upload"
-                format={["video/mp4"]}
-                formatTxt="mp4"
-                checkTxt="请上传采访视频!"
-                upTxt=""
-                myType="video"
+                upTxt="最多10个视频"
+                ref={ZupVideosRef}
               />
             </div>
           </div>

+ 1 - 1
src/pages/A7school/A7tab2M.tsx

@@ -139,7 +139,7 @@ function A7tab2M({ fId, closeFu, addTableFu, upTableFu }: Props) {
             rules={[{ required: true, message: "请输入刊物标题!" }]}
             getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
           >
-            <Input maxLength={10} showCount placeholder="请输入内容" />
+            <Input maxLength={30} showCount placeholder="请输入内容" />
           </Form.Item>
           {/* 刊物封面 */}
           <div className="formRow">

+ 2 - 2
src/pages/A7school/index.module.scss

@@ -52,8 +52,8 @@
     .A7mMaiin {
       margin-top: 15px;
       width: 100%;
-      // height: 570px;
-      // overflow-y: auto;
+      height: 600px;
+      overflow-y: auto;
 
       .ant-form {
         width: 800px;

+ 4 - 1
src/store/action/A3record.ts

@@ -8,7 +8,10 @@ export const A3_APIgetList = (data: any) => {
     const res = await http.post("cms/pointLog/pageList", data);
     if (res.code === 0) {
       const obj = {
-        list: res.data.records,
+        list: res.data.records.map((v: any) => ({
+          ...v,
+          score: v.score > 0 ? "+" + v.score : v.score,
+        })),
         total: res.data.total,
       };
       dispatch({ type: "A3/getList", payload: obj });

+ 1 - 0
src/utils/tableData.ts

@@ -55,6 +55,7 @@ export const A3tableC = [
   ["txt", "时间", "createTime"],
   ["txt", "用户名", "creatorName"],
   ["txt", "类型", "type"],
+  ["txt", "爱心币", "score"],
   ["text", "说明", "description", 50],
 ];