Kaynağa Gözat

app-馆藏-简介

shaogen1995 1 yıl önce
ebeveyn
işleme
58f536e008

+ 2 - 0
pc/src/assets/styles/base.css

@@ -148,6 +148,8 @@ textarea {
 .appM {
   width: 100%;
   height: 100%;
+  max-width: 500px;
+  margin: 0 auto;
 }
 .appM > div {
   width: 100%;

+ 2 - 0
pc/src/assets/styles/base.less

@@ -202,6 +202,8 @@ textarea {
 .appM{
   width: 100%;
   height: 100%;
+  max-width: 500px;
+  margin: 0 auto;
   &>div{
     width: 100%;
     height: 100%;

+ 1 - 0
pc/src/pages/A2Main/Tab4/index.module.scss

@@ -29,6 +29,7 @@
       .tab4Top1Ac {
         background-color: var(--themeColor);
         color: #47392C;
+        pointer-events: none;
       }
 
       .tab4Top2 {

+ 1 - 5
pc/src/pages/A2Main/Tab4/index.tsx

@@ -20,7 +20,7 @@ import "swiper/css";
 import "swiper/css/free-mode";
 import Tab4Info from "../Tab4Info";
 
-const typeArr = [
+export const typeArr = [
   { id: "model", name: "三维文物" },
   { id: "img", name: "二维文物" },
 ];
@@ -66,10 +66,6 @@ function Tab4() {
   const inpValueRef = useRef<any>(null);
 
   useEffect(() => {
-    inpValueRef.current = inpValue;
-  }, [inpValue]);
-
-  useEffect(() => {
     // 发送请求拿到顶部筛选数据
     dispatch(A2_APIgetTopClass(getData.type === "img" ? "2d" : "3d"));
   }, [dispatch, getData.type]);

+ 192 - 0
pc/src/pages/App/MainM/Tab3/Tab3InfoM/Tab3InfoMtxt/index.module.scss

@@ -0,0 +1,192 @@
+.Tab3InfoMtxt {
+  position: absolute;
+  z-index: 50;
+  width: 100%;
+  bottom: 0;
+  left: 0;
+  height: 200px;
+  transition: all .3s;
+  background-color: rgba(0, 0, 0, .6);
+  // backdrop-filter: blur(4px);
+  border-radius: 24px 24px 0 0;
+  padding: 20px;
+  color: #fff;
+
+  :global {
+
+    // 顶部
+    .tab3ITtop {
+      display: flex;
+      justify-content: center;
+      height: 46px;
+
+      .tab3ITtopRow {
+        height: 100%;
+        display: flex;
+        width: 25%;
+        position: relative;
+
+        .tab3ITtopRow1 {
+          width: 46px;
+          height: 100%;
+          border-radius: 50%;
+          border: 1px dashed var(--themeColor);
+          padding: 3px;
+
+          &>div {
+            width: 38px;
+            height: 38px;
+            text-align: center;
+            line-height: 38px;
+            color: #857959;
+            border-radius: 50%;
+          }
+        }
+
+        .tab3ITtopRow2 {
+          position: absolute;
+          top: 50%;
+          left: 51px;
+          transform: translateY(-50%);
+          width: calc(100% - 56px);
+          height: 1px;
+          border-bottom: 1px dashed var(--themeColor);
+        }
+
+        &:last-child {
+          width: 46px;
+
+          .tab3ITtopRow2 {
+            display: none;
+          }
+        }
+      }
+
+      .tab3ITtopRowAc {
+        .tab3ITtopRow1 {
+          pointer-events: none;
+
+          &>div {
+            background-color: var(--themeColor);
+
+
+          }
+        }
+      }
+    }
+
+    // 箭头
+    .tab3ITshow {
+      position: absolute;
+      z-index: 10;
+      left: 50%;
+      bottom: 20px;
+      transform: translateX(-50%);
+
+      &>img {
+        width: 46px;
+      }
+    }
+
+    .tab3ITmain {
+      width: 100%;
+      height: calc(100% - 60px);
+      margin-top: 14px;
+
+      &>div {
+        width: 100%;
+        height: 100%;
+      }
+
+      // 简介
+      .tab3ITmain1 {
+        overflow-y: auto;
+        padding: 10px 15px 80px;
+
+        .t3m1Title {
+          font-weight: 700;
+          font-size: 18px;
+          margin-bottom: 20px;
+        }
+
+        .t3m1ConRow {
+          display: flex;
+          // align-items: center;
+          padding-left: 30px;
+          padding-bottom: 20px;
+          position: relative;
+
+          // border-left: 1px dashed var(--themeColor);
+          &::before {
+            content: '';
+            position: absolute;
+            z-index: 2;
+            top: 5px;
+            left: 0px;
+            width: 12px;
+            height: 12px;
+            border-radius: 50%;
+            background-color: var(--themeColor);
+          }
+
+          &::after {
+            content: '';
+            position: absolute;
+            width: 1px;
+            height: 100%;
+            top: 5px;
+            left: 5px;
+            border-left: 1px dashed var(--themeColor);
+          }
+
+          .t3m1ConRow1 {
+            font-weight: 700;
+            font-size: 16px;
+            width: 50px;
+          }
+
+          .t3m1ConRow2 {
+            padding-top: 3px;
+            width: calc(100% - 50px);
+            opacity: .8;
+            letter-spacing: 2px;
+            line-height: 20px;
+          }
+
+          &:last-child {
+            padding-bottom: 0px;
+
+            &::after {
+              display: none;
+            }
+          }
+        }
+      }
+    }
+
+  }
+}
+
+.Tab3InfoMtxtAc {
+  height: calc(100% - 20px);
+}
+
+.Tab3InfoMtxtHide {
+  padding-top: 0px;
+
+  :global {
+    .tab3ITmain {
+      height: 100%;
+
+      .tab3ITmain1 {
+        // overflow: hidden;
+
+        // .t3m1Title {
+        //   overflow: hidden;
+        //   text-overflow: ellipsis;
+        //   white-space: nowrap;
+        // }
+      }
+    }
+  }
+}

+ 144 - 0
pc/src/pages/App/MainM/Tab3/Tab3InfoM/Tab3InfoMtxt/index.tsx

@@ -0,0 +1,144 @@
+import React, { useEffect, useMemo, useState } from "react";
+import styles from "./index.module.scss";
+import { A2BarrageType, A2GoodsType, A2QuestionType } from "@/types";
+import classNames from "classnames";
+import { envUrl } from "@/utils/env";
+
+const topArrTemp = [
+  {
+    id: 0,
+    name: "简介",
+    flag: true,
+  },
+  {
+    id: 3,
+    name: "问答",
+    flag: false,
+  },
+  {
+    id: 4,
+    name: "知识",
+    flag: false,
+  },
+  {
+    id: 2,
+    name: "留言",
+    flag: true,
+  },
+];
+
+type Props = {
+  info: A2GoodsType;
+  // 问答
+  wdFlag: boolean;
+  questionList: A2QuestionType[];
+  // 知识
+  zsFlag: boolean;
+  // 留言
+  barrageList: A2BarrageType[];
+  btnOkFlag: boolean;
+  goodsId: number;
+};
+
+function Tab3InfoMtxt({
+  info,
+  wdFlag,
+  questionList,
+  zsFlag,
+  barrageList,
+  btnOkFlag,
+  goodsId,
+}: Props) {
+  // 页面的展开和收起
+  const [pageShow, setPageShow] = useState(true);
+
+  useEffect(() => {
+    if (!pageShow) setTopAc(0);
+  }, [pageShow]);
+
+  // 顶部的状态 3-问答 4-知识 2-留言
+  const [topAc, setTopAc] = useState(0);
+
+  // 顶部的数组
+  const topArr = useMemo(() => {
+    const arr = [...topArrTemp];
+    // 判断 问答 和 知识 是否显示
+    if (wdFlag) arr[1].flag = true;
+    if (zsFlag) arr[2].flag = true;
+    return arr;
+  }, [wdFlag, zsFlag]);
+
+  return (
+    <div
+      className={classNames(
+        styles.Tab3InfoMtxt,
+        pageShow ? styles.Tab3InfoMtxtAc : styles.Tab3InfoMtxtHide
+      )}
+    >
+      {/* 顶部的筛选 */}
+      {pageShow ? (
+        <div className="tab3ITtop">
+          {topArr.map((v) => (
+            <div
+              key={v.id}
+              className={classNames(
+                "tab3ITtopRow",
+                topAc === v.id ? "tab3ITtopRowAc" : ""
+              )}
+            >
+              <div className="tab3ITtopRow1" onClick={() => setTopAc(v.id)}>
+                <div>{v.name}</div>
+              </div>
+              <div className="tab3ITtopRow2"></div>
+            </div>
+          ))}
+        </div>
+      ) : null}
+
+      {/* 箭头 */}
+      <div className="tab3ITshow" onClick={() => setPageShow(!pageShow)}>
+        <img
+          src={`${envUrl}/App/main3/${pageShow ? "hide" : "show"}.png`}
+          alt=""
+        />
+      </div>
+
+      <div className={classNames("tab3ITmain")}>
+        {/* --------------------简介-------------------- */}
+        {topAc === 0 ? (
+          <div className="tab3ITmain1 myscroll">
+            <div className="t3m1Title">{info.name}</div>
+            {info.dictTexture ? (
+              <div className="t3m1ConRow">
+                <div className="t3m1ConRow1">类别:</div>
+                <div className="t3m1ConRow2">{info.dictTexture}</div>
+              </div>
+            ) : null}
+            {info.dictAge ? (
+              <div className="t3m1ConRow">
+                <div className="t3m1ConRow1">年代:</div>
+                <div className="t3m1ConRow2">{info.dictAge}</div>
+              </div>
+            ) : null}
+            {info.dictLevel ? (
+              <div className="t3m1ConRow">
+                <div className="t3m1ConRow1">级别:</div>
+                <div className="t3m1ConRow2">{info.dictLevel}</div>
+              </div>
+            ) : null}
+            {info.description ? (
+              <div className="t3m1ConRow">
+                <div className="t3m1ConRow1">简介:</div>
+                <div className="t3m1ConRow2">{info.description}</div>
+              </div>
+            ) : null}
+          </div>
+        ) : null}
+      </div>
+    </div>
+  );
+}
+
+const MemoTab3InfoMtxt = React.memo(Tab3InfoMtxt);
+
+export default MemoTab3InfoMtxt;

+ 16 - 0
pc/src/pages/App/MainM/Tab3/Tab3InfoM/index.module.scss

@@ -0,0 +1,16 @@
+.Tab3InfoM{
+  position: fixed;
+  z-index: 9999;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  width: 100%;
+  height: 100%;
+  max-width: 500px;
+  background-color: #554a3e;
+  padding: 10px 0 60px;
+
+  :global{
+
+  }
+}

+ 327 - 0
pc/src/pages/App/MainM/Tab3/Tab3InfoM/index.tsx

@@ -0,0 +1,327 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import Tab3InfoMtxt from "./Tab3InfoMtxt";
+import {
+  A2BarrageType,
+  A2FileObjType,
+  A2FileType,
+  A2GoodsType,
+  A2QuestionType,
+} from "@/types";
+import {
+  A2_APIgetBarrage,
+  A2_APIgetBarrageAll,
+  A2_APIgetConfigBarrage,
+  A2_APIgetGoodsInfo,
+  A2_APIgetQuestion,
+  A2_APIgoodsaddStar,
+} from "@/store/action/A2Main";
+import { Toast } from "antd-mobile";
+
+type Props = {
+  isOpen: boolean;
+  id: number;
+  closePage?: () => void;
+};
+
+function Tab3InfoM({ isOpen, id, closePage }: Props) {
+  // 文物信息
+  const [info, setInfo] = useState<A2GoodsType>({} as A2GoodsType);
+
+  // 附件数据(除开音频)
+  const [fileList, setFileList] = useState<A2FileObjType>({
+    model: [],
+    video: [],
+    img: [],
+  });
+
+  // 音频
+  const [right1, setRight1] = useState({ show: false, url: "" });
+  const audioUrlRef = useRef("");
+
+  // 弹幕
+  const [right5, setRight5] = useState({ show: false, done: false });
+
+  // 点赞
+  const [right6, setRight6] = useState(false);
+  // 分享
+  const [right7, setRight7] = useState(false);
+
+  //问答
+  const [right3, setRight3] = useState(false);
+
+  // 知识
+  const [right4, setRight4] = useState(false);
+
+  // 留言板(默认显示),传递 到二级页面 看看是否开启了  留言到后端的  功能
+  const [right2, setRight2] = useState(false);
+
+  // 点击点赞
+  const likeClickFu = useCallback(async () => {
+    if (right6) return;
+    setRight6(true);
+    try {
+      await A2_APIgoodsaddStar(id);
+    } catch (error) {
+      console.log(error);
+    }
+    window.setTimeout(() => {
+      setRight6(false);
+    }, 3000);
+  }, [id, right6]);
+
+  // 点击分享链接
+  const fenXClickFu = useCallback(() => {
+    if (right7) return;
+    setRight7(true);
+    window.setTimeout(() => {
+      setRight7(false);
+    }, 3000);
+    let OrderNumber = window.location.origin + "/pc/#/goods?id=" + id;
+    let newInput = document.createElement("input");
+    newInput.value = OrderNumber;
+    document.body.appendChild(newInput);
+    newInput.select();
+    document.execCommand("Copy");
+    newInput.remove();
+    Toast.show({
+      icon: "success",
+      content: "复制链接成功",
+    });
+  }, [id, right7]);
+
+  // 问答数组
+  const [question, setQuestion] = useState<A2QuestionType[]>([]);
+
+  // 文物留言的数组
+  const [barrage, setBarrage] = useState<A2BarrageType[]>([]);
+
+  // 所有弹幕的数组
+  const [barrageAll, setBarrageAll] = useState<A2BarrageType[]>([]);
+  const barrMoveRef = useRef<HTMLDivElement>(null);
+  const [barrInd, setBarrInd] = useState(0);
+  const barrMoveRefKill = useRef<any>(null);
+
+  // 每次弹幕的索引变化的时候
+  useEffect(() => {
+    window.setTimeout(() => {
+      const width = barrMoveRef.current?.offsetWidth || 0;
+
+      if (barrMoveRef.current) {
+        barrMoveRef.current.style.right = -width - 200 + "px";
+
+        const endNum = -window.innerWidth - width - 200;
+        barrMoveRefKill.current?.kill();
+        // 开始动画
+
+        // 总弹幕数量只有 1  的情况
+        if (barrageAll.length === 1) {
+          barrMoveRefKill.current = gsap.to(barrMoveRef.current, {
+            duration: 16,
+            ease: "none",
+            x: endNum,
+            repeat: -1,
+          });
+        } else if (barrageAll.length > 1) {
+          // 有超过 1 的情况
+          barrMoveRefKill.current = gsap.fromTo(
+            barrMoveRef.current,
+            { x: 0 },
+            {
+              duration: 16,
+              ease: "none",
+              x: endNum,
+              onComplete: () => {
+                let num = barrInd + 1;
+                if (num >= barrageAll.length) num = 0;
+                setBarrInd(num);
+              },
+            }
+          );
+        }
+      }
+    }, 200);
+  }, [barrInd, barrageAll.length]);
+
+  // 通过id获取详情
+  const getDataFu = useCallback(
+    async (id: number) => {
+      const res = await A2_APIgetGoodsInfo(id);
+      if (res.code === 0) {
+        const info: A2GoodsType = res.data.entity;
+
+        // 留言板 和 弹幕开关 是否显示
+        const res_b = await A2_APIgetConfigBarrage();
+        if (res_b.code === 0) {
+          const value_b = JSON.parse(res_b.data.content);
+          if (value_b.value) {
+            // 获取所有的弹幕数组
+            const resBa_all = await A2_APIgetBarrageAll();
+            if (resBa_all.code === 0) {
+              setBarrageAll(resBa_all.data);
+            }
+            //打开了弹幕总开关 并且所有弹幕的数组长度>0
+            if (resBa_all.data.length) {
+              // 显示弹幕开关
+              setRight5({ show: true, done: true });
+            }
+
+            // 文物的弹幕留言开关 和总 弹幕开关都开启了(可以在二级页面发送接口留言)
+            if (info.isBarrage) setRight2(true);
+          }
+        }
+
+        const file: A2FileType[] = res.data.file;
+
+        // 整理附件数据
+        const fileObj: A2FileObjType = {
+          model: [],
+          video: [],
+          img: [],
+        };
+        file.forEach((v) => {
+          if (v.type === "model") fileObj.model.push(v);
+          else if (v.type === "video") fileObj.video.push(v);
+          else if (v.type === "img") fileObj.img.push(v);
+        });
+
+        setFileList(fileObj);
+
+        // 有上传音频
+        const isAudioObj = file.find((v) => v.type === "audio");
+        if (isAudioObj) {
+          setRight1({ show: !isOpen, url: isAudioObj.filePath });
+          audioUrlRef.current = isAudioObj.filePath;
+        }
+
+        setInfo(info);
+
+        // 看看是否有相关的问答
+        const res2 = await A2_APIgetQuestion(id);
+        if (res2.code === 0) {
+          // 如有有选择 知识驿站
+          if (info.tagType) setRight4(true);
+          // 如果有问答
+          if (res2.data.length) setRight3(true);
+          const res3 = await A2_APIgetBarrage(id);
+          setQuestion(res2.data);
+          if (res3.code === 0) {
+            // 如果这条文物有留言
+            // if (res3.data.length) setRight2;
+            setBarrage(res3.data);
+          }
+        }
+      }
+    },
+    [isOpen]
+  );
+
+  // 有关音频
+  useEffect(() => {
+    if (right1.url) {
+      // 如果是从 文物  详情 进来---直接播放音频
+      window.setTimeout(() => {
+        const dom: HTMLAudioElement | null = document.querySelector("#myAudio");
+
+        if (dom) {
+          dom.onended = () => {
+            // console.log("-------音频播放结束");
+            setRight1({ show: false, url: audioUrlRef.current });
+          };
+          // 音频的状态
+          if (!isOpen) dom.play();
+        }
+      }, 100);
+    }
+  }, [isOpen, right1.url]);
+
+  // 打开和关闭音频
+  const audioCutFu = useCallback((val: boolean) => {
+    const dom: HTMLAudioElement | null = document.querySelector("#myAudio");
+    if (dom) {
+      if (!val) dom.play();
+      else dom.pause();
+      setRight1({ show: !val, url: audioUrlRef.current });
+    }
+  }, []);
+
+  useEffect(() => {
+    getDataFu(id);
+    return () => {
+      barrMoveRefKill.current?.kill();
+    };
+  }, [getDataFu, id]);
+
+  //  文件类型 type
+  const [type, setType] = useState<"model" | "video" | "img">("model");
+
+  // 每次变化type的时候把 索引变成0
+  useEffect(() => {
+    setShowInd(0);
+  }, [type]);
+
+  // 切换不同文件(模型/视频/图片的数组)
+  const typeArr = useMemo(() => {
+    const arr: {
+      name: "模型" | "视频" | "图片";
+      type: "model" | "video" | "img";
+    }[] = [];
+    if (fileList.model.length) arr.push({ name: "模型", type: "model" });
+    if (fileList.video.length) arr.push({ name: "视频", type: "video" });
+    if (fileList.img.length) arr.push({ name: "图片", type: "img" });
+    return arr;
+  }, [fileList]);
+
+  // 当前默认展示什么模块(优先级按照 模型-视频-图片)
+  useEffect(() => {
+    if (fileList.model.length) setType("model");
+    else if (fileList.video.length) setType("video");
+    else setType("img");
+  }, [fileList]);
+
+  // 在页面展示的数组
+  const list = useMemo(() => {
+    return fileList[type];
+  }, [fileList, type]);
+
+  // 当前显示的 索引
+  const [showInd, setShowInd] = useState(0);
+  const cutIndFu = useCallback(
+    (num: number, flag: boolean, ind: number) => {
+      let index = showInd + num;
+
+      if (flag) index = ind;
+
+      setShowInd(index);
+    },
+    [showInd]
+  );
+
+  return (
+    <div className={styles.Tab3InfoM}>
+      {/* 底部的信息 */}
+      <Tab3InfoMtxt
+        info={info}
+        // 问答
+        wdFlag={right3}
+        questionList={question}
+        // 知识
+        zsFlag={right4}
+        // 留言
+        barrageList={barrage}
+        btnOkFlag={right2}
+        goodsId={id}
+      />
+    </div>
+  );
+}
+
+const MemoTab3InfoM = React.memo(Tab3InfoM);
+
+export default MemoTab3InfoM;

+ 200 - 3
pc/src/pages/App/MainM/Tab3/index.module.scss

@@ -1,5 +1,202 @@
-.Tab3{
-  :global{
-    
+.Tab3 {
+  background-color: #554a3e;
+  padding: 20px 0px 0 20px;
+
+  :global {
+
+    // 顶部筛选
+    .tab3Top {
+      padding-right: 20px;
+
+      .tab3Top1 {
+        display: flex;
+
+        .tab3Top1Row {
+          width: 25%;
+          height: 40px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          border-radius: 0 10px 0 10px;
+          border: 1px solid var(--themeColor);
+          color: var(--themeColor);
+          margin-right: 10px;
+        }
+
+        .tab3Top1RowAc {
+          background-color: var(--themeColor);
+          color: #47392C;
+          pointer-events: none;
+        }
+
+        .tab3Top1Row2 {
+          width: calc(50% - 20px);
+          border-radius: 0 10px 0 10px;
+          border: 1px solid var(--themeColor);
+          color: var(--themeColor);
+          padding-right: 30px;
+          position: relative;
+
+          .ant-input {
+            background-color: transparent;
+            border: none;
+            color: var(--themeColor);
+            height: 40px;
+            line-height: 42px;
+
+            &::-webkit-input-placeholder {
+              /* WebKit browsers */
+              color: var(--themeColor);
+              opacity: .6;
+            }
+
+            &:-moz-placeholder {
+              /* Mozilla Firefox 4 to 18 */
+              color: var(--themeColor);
+              opacity: .6;
+            }
+
+            &::-moz-placeholder {
+              /* Mozilla Firefox 19+ */
+              color: var(--themeColor);
+              opacity: .6;
+            }
+
+            &:-ms-input-placeholder {
+              /* Internet Explorer 10+ */
+              color: var(--themeColor);
+              opacity: .6;
+            }
+          }
+
+          .tab3Top1Row2Icon {
+            position: absolute;
+            z-index: 10;
+            right: 0;
+            top: 0;
+            width: 30px;
+            height: 100%;
+            line-height: 37px;
+            text-align: center;
+            color: var(--themeColor);
+            font-size: 20px;
+          }
+        }
+      }
+
+      .tab3Top2 {
+        margin: 10px 0 10px;
+        width: 100%;
+        height: 36px;
+        overflow-x: auto;
+
+        .appSw {
+          height: 100%;
+
+          .swiper-slide {
+            width: auto !important;
+
+            .row {
+              margin-right: 10px;
+              padding: 0 5px;
+              font-size: 16px;
+              color: #fff;
+              height: 100%;
+            }
+
+            .active {
+              pointer-events: none;
+              color: var(--themeColor);
+              position: relative;
+
+              &::before {
+                content: '';
+                position: absolute;
+                bottom: 10px;
+                left: 0;
+                width: 100%;
+                height: 2px;
+                background-color: var(--themeColor);
+              }
+            }
+          }
+        }
+      }
+
+    }
+
+    // 主体
+    .tab3MainNone {
+      width: 100%;
+      height: calc(100% - 98px);
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      letter-spacing: 4px;
+      font-size: 18px;
+      color: var(--themeColor);
+      padding-bottom: 100px;
+    }
+
+    .tab3Main {
+      width: 100%;
+      height: calc(100% - 98px);
+      display: flex;
+      flex-wrap: wrap;
+      overflow-y: auto;
+      padding-top: 2px;
+      padding-right: 20px;
+      padding-bottom: 20px;
+
+      .tab3Mrow {
+        // border: 1px solid var(--themeColor);
+        height: 200px;
+        width: calc(50% - 8px);
+        margin-right: 16px;
+        margin-bottom: 16px;
+        border-radius: 6px;
+        overflow: hidden;
+
+        &:nth-of-type(2n) {
+          margin-right: 0;
+        }
+      }
+
+      .tab3MrowImg {
+        background-color: #fff;
+        width: 100%;
+        height: calc(100% - 55px);
+      }
+
+      .tab3MrowTxt {
+        background-size: 100% 100%;
+        padding: 10px 8px;
+        height: 55px;
+        color: #fff;
+
+        &>div {
+          width: 100%;
+          height: 100%;
+          line-height: 20px;
+          width: 100%;
+          display: -webkit-box;
+          overflow: hidden;
+          white-space: normal !important;
+          text-overflow: ellipsis;
+          word-wrap: break-word;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+          text-align: center;
+        }
+      }
+      // 加载更多 下拉刷新
+      .adm-infinite-scroll{
+        width: 100%;
+        color: #fff;
+        .adm-loading{
+          color: #fff !important;
+        }
+      }
+    }
   }
 }

+ 242 - 5
pc/src/pages/App/MainM/Tab3/index.tsx

@@ -1,12 +1,249 @@
-import React from "react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
 import styles from "./index.module.scss";
- function Tab3() {
-  
+import classNames from "classnames";
+import { SearchOutlined } from "@ant-design/icons";
+
+// 轮播图
+import { Swiper, SwiperSlide } from "swiper/react";
+import { FreeMode } from "swiper";
+// Import Swiper styles
+import "swiper/css";
+import "swiper/css/free-mode";
+import { useDispatch, useSelector } from "react-redux";
+import { A2getGoodsDataType } from "@/types";
+import {
+  A2_APIgetGoodsList,
+  A2_APIgetGoodsListApp,
+  A2_APIgetTopClass,
+} from "@/store/action/A2Main";
+import { typeArr } from "@/pages/A2Main/Tab4";
+import { Input } from "antd";
+import store, { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import { envUrl } from "@/utils/env";
+import { InfiniteScroll } from "antd-mobile";
+import Tab3InfoM from "./Tab3InfoM";
+
+function Tab3() {
+  const dispatch = useDispatch();
+
+  // 看看是不是还没有发送请求
+  const [isDataFlag, setIsDataFlag] = useState(false);
+
+  // 当前页 的 ref 用于 下拉 发送请求拿下一页的数据
+  const pageNumRef = useRef(1);
+
+  // 发送请求参数
+  const [getData, setGetData] = useState<A2getGoodsDataType>({
+    dictAge: "",
+    dictTexture: "全部",
+    searchKey: "",
+    pageNum: 1,
+    pageSize: 10,
+    type: "img",
+  });
+
+  const tab3MainRef = useRef<HTMLDivElement>(null);
+
+  // 发送请求函数
+  const getList = useCallback(() => {
+    // 数据变化的时候 滚动到顶部
+    // 兼容苹果手机
+    setTimeout(() => {
+      if (tab3MainRef.current) tab3MainRef.current.scrollTop = 1;
+    }, 100);
+
+    // 同步 页面=>用于 下拉 发送请求拿下一页的数据
+    pageNumRef.current = getData.pageNum;
+
+    setIsDataFlag(true);
+
+    let searchKey = "";
+    if (inpValueRef.current) searchKey = inpValueRef.current.input.value;
+
+    const obj = {
+      ...getData,
+      searchKey,
+      dictTexture: getData.dictTexture === "全部" ? "" : getData.dictTexture,
+    };
+
+    dispatch(A2_APIgetGoodsList(obj));
+  }, [dispatch, getData]);
+
+  useEffect(() => {
+    getList();
+  }, [getList]);
+
+  // 输入框
+  const [inpValue, setInpValue] = useState("");
+
+  const inpValueRef = useRef<any>(null);
+
+  useEffect(() => {
+    // 发送请求拿到顶部筛选数据
+    dispatch(A2_APIgetTopClass(getData.type === "img" ? "2d" : "3d"));
+  }, [dispatch, getData.type]);
+
+  // 获取下拉数据
+  const selectData = useSelector((state: RootState) => state.A2Main.selectData);
+
+  // 从仓库获取数据
+  const goodsList = useSelector((state: RootState) => state.A2Main.goodsList);
+
+  // 详情页 id
+  const [infoId, setInfoId] = useState(0);
+
+  // 上拉加载更多数据
+  const loadMore = useCallback(async () => {
+    let searchKey = "";
+    if (inpValueRef.current) searchKey = inpValueRef.current.input.value;
+
+    pageNumRef.current = pageNumRef.current + 1;
+
+    const obj = {
+      ...getData,
+      pageNum: pageNumRef.current,
+      searchKey,
+      dictTexture: getData.dictTexture === "全部" ? "" : getData.dictTexture,
+    };
+    const res = await A2_APIgetGoodsListApp(obj);
+    if (res.code === 0) {
+      const objRes = {
+        list: res.data.records,
+        total: res.data.total,
+      };
+      const oldData = store.getState().A2Main.goodsList;
+
+      const newData = {
+        list: [...oldData.list, ...objRes.list],
+        total: objRes.total,
+      };
+
+      store.dispatch({ type: "main/goodsList", payload: newData });
+    }
+  }, [getData]);
+
   return (
     <div className={styles.Tab3}>
-      <h1>Tab3</h1>
+      {/* 顶部筛选框 */}
+      <div className="tab3Top">
+        <div className="tab3Top1">
+          {/* 二维 三维的筛选 */}
+          {typeArr.map((v) => (
+            <div
+              key={v.id}
+              className={classNames(
+                "tab3Top1Row",
+                v.id === getData.type ? "tab3Top1RowAc" : ""
+              )}
+              onClick={() =>
+                setGetData({
+                  ...getData,
+                  type: v.id,
+                  pageNum: 1,
+                  dictTexture: "全部",
+                })
+              }
+            >
+              {v.name}
+            </div>
+          ))}
+
+          {/* 搜索框 */}
+          <div
+            className="tab3Top1Row2"
+            onKeyUp={(e) => {
+              if (e.key === "Enter") getList();
+            }}
+          >
+            <Input
+              placeholder="请输入关键词"
+              maxLength={30}
+              value={inpValue}
+              onChange={(e) => setInpValue(e.target.value.trim())}
+              ref={inpValueRef}
+            />
+
+            <div className="tab3Top1Row2Icon" onClick={() => getList()}>
+              <SearchOutlined />
+            </div>
+          </div>
+        </div>
+        {/* 类型的筛选 */}
+        <div className="tab3Top2">
+          <Swiper
+            modules={[FreeMode]}
+            className="appSw"
+            spaceBetween={0}
+            slidesPerView="auto"
+            freeMode={true}
+            mousewheel={true}
+            // onSlideChange={(e) => console.log("slide change", e)}
+            // onSwiper={(swiper) => (mySwiperRef.current = swiper)}
+          >
+            {selectData.map((v) => (
+              <SwiperSlide key={v}>
+                <div
+                  onClick={(e) =>
+                    setGetData({
+                      ...getData,
+                      dictTexture: v as string,
+                      pageNum: 1,
+                    })
+                  }
+                  className={classNames(
+                    "row",
+                    getData.dictTexture === v ? "active" : ""
+                  )}
+                >
+                  <span>{v}</span>
+                </div>
+              </SwiperSlide>
+            ))}
+          </Swiper>
+        </div>
+      </div>
+
+      {/* 主体内容 */}
+      {goodsList.list.length <= 0 && isDataFlag ? (
+        <div className="tab3MainNone">暂无数据</div>
+      ) : (
+        <div className="tab3Main" ref={tab3MainRef}>
+          {goodsList.list.map((v) => (
+            <div
+              className="tab3Mrow"
+              key={v.id}
+              title={v.name}
+              onClick={() => setInfoId(v.id)}
+            >
+              <div className="tab3MrowImg">
+                <ImageLazy src={v.thumb} noLook width="100%" height="100%" />
+              </div>
+              <div
+                className="tab3MrowTxt"
+                style={{
+                  backgroundImage: `url(${envUrl}/App/main3/txtBac.png)`,
+                }}
+              >
+                <div>{v.name}</div>
+              </div>
+            </div>
+          ))}
+          {isDataFlag ? (
+            <InfiniteScroll
+              loadMore={loadMore}
+              hasMore={goodsList.total > goodsList.list.length}
+            />
+          ) : null}
+        </div>
+      )}
+
+      {/* 点击文物进入详情页 */}
+      {infoId ? (
+        <Tab3InfoM id={infoId} closePage={() => setInfoId(0)} isOpen={false} />
+      ) : null}
     </div>
-  )
+  );
 }
 
 const MemoTab3 = React.memo(Tab3);

+ 3 - 1
pc/src/pages/App/MainM/Tab5/Info/index.module.scss

@@ -46,8 +46,10 @@
 
     .tab5Ibtn {
       position: fixed;
+      max-width: 480px;
       z-index: 10;
-      left: 20px;
+      left: 50%;
+      transform: translateX(-50%);
       width: calc(100% - 40px);
       height: 46px;
       display: flex;

+ 8 - 0
pc/src/store/action/A2Main.ts

@@ -24,6 +24,14 @@ export const A2_APIgetGoodsList = (data: A2getGoodsDataType) => {
 };
 
 /**
+ * 手机端 下拉加载更多数据
+ */
+export const A2_APIgetGoodsListApp = (data: A2getGoodsDataType) => {
+  return http.post("show/goods/pageList", data);
+};
+
+
+/**
  * 获取下拉框列表
  */
 // export const A2_APIgetSelectData = (type: "age" | "texture") => {

BIN
staticData/App/main3/hide.png


BIN
staticData/App/main3/show.png


BIN
staticData/App/main3/txtBac.png