shaogen1995 %!s(int64=2) %!d(string=hai) anos
pai
achega
21ba1076e0
Modificáronse 44 ficheiros con 8308 adicións e 58 borrados
  1. 22 30
      houtai/src/pages/B2Goods/GoodsAdd/index.tsx
  2. 1 1
      houtai/src/pages/C1User/UserAdd/index.tsx
  3. 7 3
      houtai/src/utils/http.ts
  4. 6330 0
      pc/public/4dage.js
  5. 34 0
      pc/public/model.html
  6. 12 5
      pc/src/App.tsx
  7. BIN=BIN
      pc/src/assets/img/goods/R_left.png
  8. BIN=BIN
      pc/src/assets/img/goods/R_right.png
  9. BIN=BIN
      pc/src/assets/img/goods/close.png
  10. BIN=BIN
      pc/src/assets/img/goods/icon1.png
  11. BIN=BIN
      pc/src/assets/img/goods/icon1Ac.png
  12. BIN=BIN
      pc/src/assets/img/goods/icon2.png
  13. BIN=BIN
      pc/src/assets/img/goods/icon3.png
  14. BIN=BIN
      pc/src/assets/img/goods/icon3Ac.png
  15. BIN=BIN
      pc/src/assets/img/goods/icon4.png
  16. BIN=BIN
      pc/src/assets/img/goods/icon4Ac.png
  17. BIN=BIN
      pc/src/assets/img/goods/icon5.png
  18. BIN=BIN
      pc/src/assets/img/goods/icon5Ac.png
  19. BIN=BIN
      pc/src/assets/img/goods/toHome.png
  20. 15 0
      pc/src/assets/styles/base.css
  21. 40 1
      pc/src/assets/styles/base.less
  22. 29 0
      pc/src/components/Message/index.tsx
  23. 51 0
      pc/src/pages/A2Main/GoodsInfo/Left2/index.module.scss
  24. 73 0
      pc/src/pages/A2Main/GoodsInfo/Left2/index.tsx
  25. 73 0
      pc/src/pages/A2Main/GoodsInfo/Left3/index.module.scss
  26. 107 0
      pc/src/pages/A2Main/GoodsInfo/Left3/index.tsx
  27. 67 0
      pc/src/pages/A2Main/GoodsInfo/Left4/index.module.scss
  28. 31 0
      pc/src/pages/A2Main/GoodsInfo/Left4/index.tsx
  29. 128 0
      pc/src/pages/A2Main/GoodsInfo/Rform/index.module.scss
  30. 139 0
      pc/src/pages/A2Main/GoodsInfo/Rform/index.tsx
  31. 95 0
      pc/src/pages/A2Main/GoodsInfo/RightFile/index.module.scss
  32. 88 0
      pc/src/pages/A2Main/GoodsInfo/RightFile/index.tsx
  33. 281 0
      pc/src/pages/A2Main/GoodsInfo/index.module.scss
  34. 456 0
      pc/src/pages/A2Main/GoodsInfo/index.tsx
  35. 8 2
      pc/src/pages/A2Main/GoodsSw/index.module.scss
  36. 12 4
      pc/src/pages/A2Main/GoodsSw/index.tsx
  37. 12 5
      pc/src/pages/A2Main/KnowLedge/index.module.scss
  38. 11 2
      pc/src/pages/A2Main/KnowLedge/index.tsx
  39. 5 1
      pc/src/pages/A2Main/index.module.scss
  40. 5 3
      pc/src/pages/A2Main/index.tsx
  41. 11 0
      pc/src/pages/A3Goods/index.module.scss
  42. 27 0
      pc/src/pages/A3Goods/index.tsx
  43. 66 1
      pc/src/store/action/A2Main.ts
  44. 72 0
      pc/src/types/store/A2Main.d.ts

+ 22 - 30
houtai/src/pages/B2Goods/GoodsAdd/index.tsx

@@ -6,7 +6,6 @@ import {
   FormInstance,
   Input,
   Popconfirm,
-  Radio,
   Select,
   Switch,
 } from "antd";
@@ -50,21 +49,18 @@ type Props = {
 
 function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
   // 后面添加的的知识驿站
-  const valueZS1Info = useMemo(() => {
-    return ["军事", "历史", "人物", "科学", "教育", "经济"];
+  const valueZS1Info: any = useMemo(() => {
+    return [
+      { label: "军事", value: "军事" },
+      { label: "历史", value: "历史" },
+      { label: "人物", value: "人物" },
+      { label: "科学", value: "科学" },
+      { label: "教育", value: "教育" },
+      { label: "经济", value: "经济" },
+    ];
   }, []);
 
-  const [valueZS1, setValueZS1] = useState("");
-  // 取消选中
-  const valueZS1Close = useCallback(
-    (item: string) => {
-      if (item === valueZS1) {
-        setValueZS1("");
-        setValueZS2([]);
-      }
-    },
-    [valueZS1]
-  );
+  const [valueZS1, setValueZS1] = useState<any>([]);
 
   const [valueZS2, setValueZS2] = useState<CheckboxValueType[]>([]);
 
@@ -89,7 +85,7 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
 
     if (res.data.entity.type) setTypeCheck(res.data.entity.type.split(","));
 
-    setValueZS1(res.data.entity.tagType);
+    setValueZS1(res.data.entity.tagType.split(","));
     if (res.data.entity.tagCountry)
       setValueZS2(res.data.entity.tagCountry.split(","));
 
@@ -341,7 +337,7 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
   const onFinishFailed = useCallback(() => {
     setCoverCheck(true);
     setTypeOk(true);
-    return MessageFu.warning("有表单不符号规则!");
+    return MessageFu.warning("当前填入内容不符合要求!");
   }, []);
 
   // 通过校验点击确定
@@ -351,7 +347,7 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
       setCoverCheck(true);
       setTypeOk(true);
       if (typeCheck.length === 0 || cover === "" || fileCheckFu)
-        return MessageFu.warning("有表单不符号规则!");
+        return MessageFu.warning("当前填入内容不符合要求!");
 
       const fileIds = [];
       if (fileList.model.id && typeCheck.includes("model"))
@@ -373,7 +369,7 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
         fileIds: fileIds.join(","),
         thumb: cover,
         type: typeCheck.join(","),
-        tagType: valueZS1,
+        tagType: valueZS1.join(","),
         tagCountry: valueZS2.join(","),
       } as GoodsTableType;
       const res = await getGoodsSaveAPI(obj);
@@ -851,16 +847,12 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
             <div className="laseFormRight">
               <div className="laseFormRightRow laseFormRightRow1">
                 <div className="laseFormRightLabel">类别标签:</div>
-                <Radio.Group
-                  onChange={(e) => setValueZS1(e.target.value)}
+
+                <Checkbox.Group
                   value={valueZS1}
-                >
-                  {valueZS1Info.map((v) => (
-                    <Radio key={v} value={v} onClick={() => valueZS1Close(v)}>
-                      {v}
-                    </Radio>
-                  ))}
-                </Radio.Group>
+                  onChange={(e) => setValueZS1(e)}
+                  options={valueZS1Info}
+                />
               </div>
               <div className="laseFormRightTit">
                 如需文物关联知识驿站,请至少标注类别标签
@@ -871,9 +863,9 @@ function GoodsAdd({ id, closeMoalFu, addListFu, editListFu }: Props) {
                   value={valueZS2}
                   onChange={(e) => setValueZS2(e)}
                   options={[
-                    { label: "清朝", value: "清朝", disabled: !valueZS1 },
-                    { label: "美国", value: "美国", disabled: !valueZS1 },
-                    { label: "西方", value: "西方", disabled: !valueZS1 },
+                    { label: "清朝", value: "清朝", disabled: !valueZS1.length },
+                    { label: "美国", value: "美国", disabled: !valueZS1.length },
+                    { label: "西方", value: "西方", disabled: !valueZS1.length },
                   ]}
                 />
               </div>

+ 1 - 1
houtai/src/pages/C1User/UserAdd/index.tsx

@@ -34,7 +34,7 @@ function UserAdd({ id, closePage, upTableList, addTableList }: Props) {
 
   // 没有通过校验
   const onFinishFailed = useCallback(() => {
-    // return MessageFu.warning("有表单不符号规则!");
+    // return MessageFu.warning("当前填入内容不符合要求!");
   }, []);
 
   useEffect(() => {

+ 7 - 3
houtai/src/utils/http.ts

@@ -56,19 +56,23 @@ http.interceptors.response.use(
   function (response) {
     // 请求回来的关闭加载提示
     axajInd--;
+
+    // 请求code
+    const resCode = response.data.code;
+
     if (axajInd === 0) {
       domShowFu("#AsyncSpinLoding", false);
     }
-    if (response.data.code === 5001 || response.data.code === 5002) {
+    if (resCode === 5001 || resCode === 5002) {
       clearTimeout(timeId);
       timeId = window.setTimeout(() => {
         removeTokenInfo();
         MessageFu.warning("登录失效!");
         history.push("/login");
       }, 200);
-    } else if (response.data.code === 0) {
+    } else if (resCode === 0) {
       // MessageFu.success(response.data.msg);
-    } else MessageFu.warning(response.data.msg);
+    } else if (resCode !== 3014) MessageFu.warning(response.data.msg);
 
     return response.data;
   },

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 6330 - 0
pc/public/4dage.js


+ 34 - 0
pc/public/model.html

@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="zh">
+
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <script src="./4dage.js"></script>
+  <title>Document</title>
+  <style>
+    html {
+      overflow: hidden;
+    }
+  </style>
+</head>
+
+<body>
+  <div id="ui"></div>
+  <script>
+    let url = getQueryVariable("m");
+    // fdage.embed(url, {
+    // fdage.embed('https://hnbwg.4dage.com' + url, {
+    fdage.embed('http://192.168.20.55:8041/api' + url, {
+      transparentBackground: true,
+      width: 800,
+      height: 600,
+      autoStart: true,
+      fullFrame: true,
+      pagePreset: false
+    });
+  </script>
+</body>
+
+</html>

+ 12 - 5
pc/src/App.tsx

@@ -8,17 +8,19 @@ import { Image } from "antd";
 import history from "./utils/history";
 import { useSelector } from "react-redux";
 import store, { RootState } from "./store";
+import MessageCom from "./components/Message";
 
 // 使用 React.lazy 懒加载页面
 const A1Home = React.lazy(() => import("./pages/A1Home"));
 const A2Main = React.lazy(() => import("./pages/A2Main"));
+const A3Goods = React.lazy(() => import("./pages/A3Goods"));
 const NotFound = React.lazy(() => import("@/components/NotFound"));
 
 function App() {
-    // 从仓库中获取查看图片的信息
-    const lookBigImg = useSelector(
-      (state: RootState) => state.A0layout.lookBigImg
-    );
+  // 从仓库中获取查看图片的信息
+  const lookBigImg = useSelector(
+    (state: RootState) => state.A0layout.lookBigImg
+  );
   return (
     <div id="app">
       {/* 关于路由 */}
@@ -27,8 +29,10 @@ function App() {
           <Switch>
             {/* 封面页 */}
             <Route path="/" exact component={A1Home} />
-            {/* 地图页 */}
+            {/* 主要页 */}
             <Route path="/main" exact component={A2Main} />
+            {/* 通过文物分享id打开的页面 */}
+            <Route path="/goods" component={A3Goods} />
             {/* 找不到页面 */}
             <Route path="*" component={NotFound} />
           </Switch>
@@ -52,6 +56,9 @@ function App() {
           },
         }}
       />
+
+      {/* antd 轻提示 ---兼容360浏览器 */}
+      <MessageCom />
     </div>
   );
 }

BIN=BIN
pc/src/assets/img/goods/R_left.png


BIN=BIN
pc/src/assets/img/goods/R_right.png


BIN=BIN
pc/src/assets/img/goods/close.png


BIN=BIN
pc/src/assets/img/goods/icon1.png


BIN=BIN
pc/src/assets/img/goods/icon1Ac.png


BIN=BIN
pc/src/assets/img/goods/icon2.png


BIN=BIN
pc/src/assets/img/goods/icon3.png


BIN=BIN
pc/src/assets/img/goods/icon3Ac.png


BIN=BIN
pc/src/assets/img/goods/icon4.png


BIN=BIN
pc/src/assets/img/goods/icon4Ac.png


BIN=BIN
pc/src/assets/img/goods/icon5.png


BIN=BIN
pc/src/assets/img/goods/icon5Ac.png


BIN=BIN
pc/src/assets/img/goods/toHome.png


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

@@ -95,3 +95,18 @@ textarea {
   border-radius: 10px;
   background: transparent;
 }
+.moveImg {
+  animation: moveRight 3.5s linear;
+}
+@keyframes moveRight {
+  0% {
+    opacity: 1;
+    transform: scale(1);
+    bottom: 0px;
+  }
+  100% {
+    opacity: 0;
+    transform: scale(1.5);
+    bottom: 100px;
+  }
+}

+ 40 - 1
pc/src/assets/styles/base.less

@@ -116,4 +116,43 @@ textarea {
     border-radius: 10px;
     background: transparent;
   }
-}
+}
+
+
+.moveImg {
+  animation: moveRight 3.5s linear;
+}
+
+
+// 动画帧
+@keyframes moveRight {
+  0% {
+    opacity: 1;
+    transform: scale(1);
+    bottom: 0px;
+  }
+
+  100% {
+    opacity: 0;
+    transform: scale(1.5);
+    bottom: 100px;
+  }
+
+}
+
+// .barrMove {
+//   animation: barrMove 3.5s linear infinite;
+// }
+
+// // 弹幕动画帧
+// // 动画帧
+// @keyframes barrMove {
+//   0% {
+//     right: -100px;
+//   }
+
+//   100% {
+//     right: 1000px;
+//   }
+
+// }

+ 29 - 0
pc/src/components/Message/index.tsx

@@ -0,0 +1,29 @@
+import React, { useEffect } from "react";
+import { message } from "antd";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+
+function MessageCom() {
+  // 从仓库中获取 antd 轻提示信息
+  const messageReducerInfo = useSelector(
+    (state: RootState) => state.A0layout.message
+  );
+
+  const [messageApi, contextHolder] = message.useMessage();
+
+  useEffect(() => {
+    if (messageReducerInfo.txt) {
+      messageApi.open({
+        type: messageReducerInfo.type,
+        content: messageReducerInfo.txt,
+        duration: messageReducerInfo.duration,
+      });
+    }
+  }, [messageApi, messageReducerInfo]);
+
+  return <>{contextHolder}</>;
+}
+
+const MemoMessage = React.memo(MessageCom);
+
+export default MemoMessage;

+ 51 - 0
pc/src/pages/A2Main/GoodsInfo/Left2/index.module.scss

@@ -0,0 +1,51 @@
+.Left2 {
+  width: 100%;
+  height: 100%;
+
+  :global {
+    .left2Main {
+      margin-top: 40px;
+      height: calc(100% - 40px);
+      overflow-y: auto;
+
+      .left2Box {
+        display: flex;
+        margin-bottom: 20px;
+
+        .left2Name {
+          font-weight: 700;
+          font-size: 18px;
+          width: 55px;
+        }
+
+        .left2RowAll {
+          line-height: 24px;
+          width: calc(100% - 55px);
+          font-size: 16px;
+          word-wrap: break-word;
+
+          .left2Row {
+            cursor: pointer;
+            margin-bottom: 10px;
+
+            &:hover {
+              text-decoration: underline
+            }
+          }
+        }
+      }
+    }
+
+    .left2Null {
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: var(--themeColor);
+      font-size: 20px;
+      padding-top: 20px;
+
+    }
+
+  }
+}

+ 73 - 0
pc/src/pages/A2Main/GoodsInfo/Left2/index.tsx

@@ -0,0 +1,73 @@
+import React, { useCallback, useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import { A2_APIgetKnowledge } from "@/store/action/A2Main";
+import { A2Left2Type } from "@/types";
+
+type Props = {
+  type: string;
+};
+
+function Left2({ type }: Props) {
+  const [list, setList] = useState<A2Left2Type[]>([]);
+
+  // 第一次不显示暂无信息
+  const [flagOne, setFlagOne] = useState(false);
+
+  useEffect(() => {
+    window.setTimeout(() => {
+      setFlagOne(true);
+    }, 500);
+  }, []);
+
+  const getListFu = useCallback(async (type: string) => {
+    const res = await A2_APIgetKnowledge(type);
+    if (res.code === 0) {
+      const obj: any = res.data;
+      const arr: any = [];
+      for (const k in obj) {
+        arr.push({
+          name: k,
+          arr: obj[k],
+        });
+      }
+      setList(arr);
+    }
+  }, []);
+
+  useEffect(() => {
+    getListFu(type);
+  }, [getListFu, type]);
+
+  return (
+    <div className={styles.Left2}>
+      {list.length ? (
+        <div className="left2Main myscroll">
+          {list.map((v) => (
+            <div className="left2Box" key={v.name}>
+              <div className="left2Name">{v.name}:</div>
+              <div className="left2RowAll">
+                {v.arr.map((v2) => (
+                  <div
+                    className="left2Row"
+                    key={v2.id}
+                    onClick={() => window.open(v2.link)}
+                  >
+                    {v2.name}
+                  </div>
+                ))}
+              </div>
+            </div>
+          ))}
+        </div>
+      ) : (
+        <div className="left2Null" hidden={!flagOne}>
+          暂无更多内容
+        </div>
+      )}
+    </div>
+  );
+}
+
+const MemoLeft2 = React.memo(Left2);
+
+export default MemoLeft2;

+ 73 - 0
pc/src/pages/A2Main/GoodsInfo/Left3/index.module.scss

@@ -0,0 +1,73 @@
+.Left3 {
+  width: 100%;
+  height: 100%;
+  padding-top: 20px;
+
+  :global {
+    .left3Title {
+      font-size: 18px;
+      margin-bottom: 20px;
+    }
+
+    .left3Main {
+      width: 100%;
+      height: calc(100% - 40px);
+      padding-right: 30px;
+      overflow-y: auto;
+
+      .left3MainRow {
+        font-size: 16px;
+        margin-bottom: 30px;
+
+        .left3MainRowT {
+          margin-bottom: 20px;
+          word-wrap: break-word;
+        }
+
+        .left3MainRowC {
+          cursor: pointer;
+          margin-bottom: 15px;
+          font-size: 14px;
+          word-wrap: break-word;
+
+          &:hover {
+            text-decoration: underline
+          }
+        }
+
+
+
+        .left3MainRowCX {
+          color: #FF5757;
+        }
+
+        .left3MainRowCD {
+          color: #8CFD79;
+        }
+
+        .left3MainRowCN {
+          pointer-events: none;
+        }
+
+
+        .left3Sta {
+          font-size: 14px;
+          margin-bottom: 5px;
+
+          .left3Sta1 {
+            color: #8CFD79;
+          }
+
+          .left3Sta2 {
+            color: #FF5757;
+          }
+        }
+
+        .left3Txt {
+          font-size: 14px;
+          word-wrap: break-word;
+        }
+      }
+    }
+  }
+}

+ 107 - 0
pc/src/pages/A2Main/GoodsInfo/Left3/index.tsx

@@ -0,0 +1,107 @@
+import React, { useCallback, useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import { A2QuestionResType, A2QuestionType } from "@/types";
+import { CheckOutlined, CloseOutlined } from "@ant-design/icons";
+import classNames from "classnames";
+const objC = {
+  0: "A",
+  1: "B",
+  2: "C",
+  3: "D",
+  4: "E",
+};
+
+type Props = {
+  list: A2QuestionType[];
+};
+function Left3({ list }: Props) {
+  const [listRes, setListRes] = useState<A2QuestionResType[]>([]);
+
+  useEffect(() => {
+    const arr = [] as A2QuestionResType[];
+    list.forEach((v) => {
+      const temp = JSON.parse(v.answer);
+      arr.push({
+        id: v.id,
+        txt: v.description,
+        goodsId: v.goodsId,
+        title: v.question,
+        ok: temp.correct,
+        answer: temp.answer,
+        done: false,
+        mySelect: "",
+      });
+    });
+    setListRes(arr);
+  }, [list]);
+
+  // 选择答案
+  const selecttFu = useCallback(
+    (item: A2QuestionResType, val: string) => {
+      if (!item.done) {
+        const arr = listRes.map((v) => {
+          return {
+            ...v,
+            done: v.id === item.id ? true : v.done,
+            mySelect: v.id === item.id ? val : v.mySelect,
+          };
+        });
+        setListRes(arr);
+      }
+    },
+    [listRes]
+  );
+
+  return (
+    <div className={styles.Left3}>
+      <div className="left3Title">单选题</div>
+      <div className="left3Main myscroll">
+        {listRes.map((v1, i1) => (
+          <div className="left3MainRow" key={v1.id}>
+            <div className="left3MainRowT">
+              {i1 + 1}.{v1.title}
+            </div>
+            {v1.answer.map((v2, i2) => (
+              <div
+                key={v2.val}
+                className={classNames(
+                  "left3MainRowC",
+                  v1.done ? "left3MainRowCN" : "",
+                  v1.ok === v2.val && v1.done ? "left3MainRowCD" : "",
+                  v1.mySelect === v2.val && v1.done ? "left3MainRowCX" : ""
+                )}
+                onClick={() => selecttFu(v1, v2.val)}
+              >
+                {Reflect.get(objC, i2)}.{v2.name}
+              </div>
+            ))}
+            {/* 回答情况 */}
+            <div className="left3Sta" hidden={!v1.done}>
+              {v1.ok === v1.mySelect ? (
+                <div className="left3Sta1">
+                  恭喜你,回答正确&emsp;
+                  <CheckOutlined />
+                </div>
+              ) : (
+                <div className="left3Sta2">
+                  很遗憾,回答错误&emsp;
+                  <CloseOutlined />
+                </div>
+              )}
+            </div>
+            {/* 说明 */}
+            <div className="left3Txt" hidden={!v1.done}>
+              {/* 正确答案:{v1.ok}
+              <br /> */}
+              题目解析:{v1.txt}
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+
+const MemoLeft3 = React.memo(Left3);
+
+export default MemoLeft3;

+ 67 - 0
pc/src/pages/A2Main/GoodsInfo/Left4/index.module.scss

@@ -0,0 +1,67 @@
+.Left4 {
+  width: 100%;
+  height: 100%;
+  padding-top: 20px;
+
+  :global {
+    .left4Main {
+      padding-right: 20px;
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+
+      .left4Row {
+        .left4_Title {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          margin-bottom: 8px;
+
+          .left4_Title_l {
+            padding-left: 24px;
+            font-size: 18px;
+            position: relative;
+
+            &::before {
+              content: '';
+              position: absolute;
+              left: 0;
+              top: 50%;
+              transform: translateY(-50%);
+              width: 12px;
+              height: 12px;
+              border: 1px double var(--themeColor);
+              // background-color: var(--themeColor);
+              border-radius: 50%;
+            }
+
+            &::after {
+              content: '';
+              position: absolute;
+              left: 3px;
+              top: 50%;
+              transform: translateY(-50%);
+              width: 8px;
+              height: 8px;
+              background-color: var(--themeColor);
+              border-radius: 50%;
+            }
+          }
+
+          .left4_Title_r {
+            font-size: 14px;
+            opacity: .6;
+          }
+        }
+
+        .left4_Txt {
+          padding-left: 24px;
+          font-size: 14px;
+          line-height: 18px;
+          word-wrap: break-word;
+          margin-bottom: 24px;
+        }
+      }
+    }
+  }
+}

+ 31 - 0
pc/src/pages/A2Main/GoodsInfo/Left4/index.tsx

@@ -0,0 +1,31 @@
+import React from "react";
+import styles from "./index.module.scss";
+import { A2BarrageType } from "@/types";
+
+type Props = {
+  list: A2BarrageType[];
+};
+
+function Left4({ list }: Props) {
+  // console.log("-----", list);
+
+  return (
+    <div className={styles.Left4}>
+      <div className="left4Main myscroll">
+        {list.map((v) => (
+          <div className="left4Row" key={v.id}>
+            <div className="left4_Title">
+              <div className="left4_Title_l">{v.authorName}</div>
+              <div className="left4_Title_r">{v.updateTime}</div>
+            </div>
+            <div className="left4_Txt">{v.name}</div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+
+const MemoLeft4 = React.memo(Left4);
+
+export default MemoLeft4;

+ 128 - 0
pc/src/pages/A2Main/GoodsInfo/Rform/index.module.scss

@@ -0,0 +1,128 @@
+.Rform {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 600px;
+  height: 500px;
+  transform: translate(-50%, -50%);
+  background-color: rgba(200, 185, 146, 0.50);
+  border-top: 4px solid #b6ab97;
+  border-radius: 0 0 4px 4px;
+  padding-top: 70px;
+
+  :global {
+    .F_close {
+      position: absolute;
+      z-index: 11;
+      right: 15px;
+      top: 15px;
+      width: 40px;
+      height: 40px;
+      cursor: pointer;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #98856f;
+      font-size: 30px;
+    }
+
+    .F_main {
+      padding: 15px 80px 0 50px;
+      width: 100%;
+      height: calc(100% - 60px);
+
+      .F_codeImg {
+        position: relative;
+        z-index: 99;
+        left: 230px;
+        top: -60px;
+        padding-left: 80px;
+
+        &>img {
+          cursor: pointer;
+        }
+      }
+
+      // antd表单样式重置
+      .ant-form-item-label>label {
+        color: #fff;
+      }
+
+      .ant-input-affix-wrapper {
+        background-color: rgba(133, 127, 108, .5);
+        border-color: rgba(255, 255, 255, .6) !important;
+
+        .ant-input {
+          color: #fff;
+          background-color: transparent;
+
+          /*修改提示文字的颜色*/
+          &::-webkit-input-placeholder {
+            /* WebKit browsers */
+            color: rgba(255, 255, 255, .6);
+          }
+
+          &:-moz-placeholder {
+            /* Mozilla Firefox 4 to 18 */
+            color: rgba(255, 255, 255, .6);
+          }
+
+          &::-moz-placeholder {
+            /* Mozilla Firefox 19+ */
+            color: rgba(255, 255, 255, .6);
+          }
+
+          &:-ms-input-placeholder {
+            /* Internet Explorer 10+ */
+            color: rgba(255, 255, 255, .6);
+          }
+
+        }
+
+        .ant-input-show-count-suffix {
+          color: rgba(255, 255, 255, .6);
+        }
+
+        .ant-input-data-count {
+          right: 4px;
+          color: rgba(255, 255, 255, .6);
+        }
+
+      }
+
+      .ant-form-item-explain-error {
+        color: #fff;
+      }
+
+      .ant-btn-primary {
+        background-color: #F0D99C !important;
+        color: #625241 !important;
+        font-size: 18px;
+        border: none !important;
+        width: 126px;
+        height: 36px;
+        line-height: 26px;
+        text-align: center;
+      }
+
+      .ant-btn-default {
+        background-color: transparent !important;
+        color: #fff !important;
+        font-size: 18px;
+        border-color: #F0D99C !important;
+        width: 126px;
+        height: 36px;
+        line-height: 26px;
+        text-align: center;
+      }
+
+    }
+
+    .F_tit {
+      margin-top: 24px;
+      font-size: 12px;
+      color: #fff;
+      text-align: center;
+    }
+  }
+}

+ 139 - 0
pc/src/pages/A2Main/GoodsInfo/Rform/index.tsx

@@ -0,0 +1,139 @@
+import React, { useCallback, useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import { CloseOutlined } from "@ant-design/icons";
+import { Button, Form, Input } from "antd";
+import TextArea from "antd/es/input/TextArea";
+import { A2_APIgetRandCode, A2_APIsaveBarrage } from "@/store/action/A2Main";
+import { A2R_fSaveType } from "@/types";
+import { MessageFu } from "@/utils/message";
+
+type Props = {
+  closeFu: () => void;
+  goodsId: number;
+};
+
+function Rform({ closeFu, goodsId }: Props) {
+  const [codeImg, setCodeImg] = useState<any>("");
+
+  // 获取验证码函数
+  const getRandCodeFu = useCallback(async () => {
+    const res: any = await A2_APIgetRandCode();
+    const reader = new FileReader();
+    reader.readAsDataURL(res);
+    reader.onload = () => {
+      setCodeImg(reader.result);
+    };
+    // console.log(123, res);
+  }, []);
+
+  useEffect(() => {
+    getRandCodeFu();
+  }, [getRandCodeFu]);
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {}, []);
+
+  // 通过校验
+  const onFinish = useCallback(
+    async (value: A2R_fSaveType) => {
+      const obj = {
+        ...value,
+        goodsId,
+      };
+      const res = await A2_APIsaveBarrage(obj);
+      if (res.code === 0) {
+        MessageFu.success("留言成功,等待审核");
+        closeFu();
+      }
+    },
+    [closeFu, goodsId]
+  );
+
+  return (
+    <div className={styles.Rform}>
+      {/* 关闭按钮 */}
+      <div className="F_close" onClick={() => closeFu()}>
+        <CloseOutlined />
+      </div>
+      {/* 表单主体 */}
+      <div className="F_main">
+        <Form
+          labelCol={{ span: 4 }}
+          name="basic"
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="您的昵称"
+            name="authorName"
+            rules={[{ required: true, message: "内容不能为空" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={8} showCount placeholder="请输入内容" />
+          </Form.Item>
+          <Form.Item
+            label="留言内容"
+            name="name"
+            rules={[
+              { required: true, message: "内容不能为空" },
+              {
+                validator: (rule, value) => {
+                  if (value) {
+                    const txt = value.replaceAll(" ", "").replaceAll("\n", "");
+                    return txt === ""
+                      ? Promise.reject("内容不能为空")
+                      : Promise.resolve();
+                  } else return Promise.resolve();
+                },
+              },
+            ]}
+          >
+            <TextArea
+              rows={4}
+              placeholder="请输入内容"
+              showCount
+              maxLength={50}
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="验证码"
+            name="randCode"
+            rules={[{ required: true, message: "内容不能为空" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input
+              style={{ width: 200 }}
+              maxLength={5}
+              showCount
+              placeholder="请输入内容"
+            />
+          </Form.Item>
+
+          {/* 验证码图 */}
+          <div className="F_codeImg">
+            <img src={codeImg} alt="" onClick={() => getRandCodeFu()} />
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item wrapperCol={{ offset: 6 }}>
+            <Button type="primary" htmlType="submit">
+              留言
+            </Button>
+            &emsp;
+            <Button onClick={() => closeFu()}>取消</Button>
+          </Form.Item>
+        </Form>
+      </div>
+
+      {/* 最后提示语句 */}
+      <div className="F_tit">您的留言经审核并采纳后,才会在平台中公开展示</div>
+    </div>
+  );
+}
+
+const MemoRform = React.memo(Rform);
+
+export default MemoRform;

+ 95 - 0
pc/src/pages/A2Main/GoodsInfo/RightFile/index.module.scss

@@ -0,0 +1,95 @@
+.RightFile {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  :global {
+
+    .R_oneAudio {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: var(--themeColor);
+      font-size: 20px;
+    }
+
+    .R1_row {
+      border-radius: 10px;
+      overflow: hidden;
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 3;
+      opacity: 0;
+      pointer-events: none;
+      transition: all .3s;
+
+      iframe {
+        width: 100%;
+        height: 100%;
+      }
+
+      video {
+        background-color: #211c19;
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .R1_rowAc {
+      opacity: 1;
+      pointer-events: auto;
+    }
+
+    .R_left {
+      cursor: pointer;
+      position: absolute;
+      top: 50%;
+      width: 40px;
+      height: 40px;
+      left: -40px;
+      z-index: 12;
+      transform: translateY(-50%);
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+
+    .R_right {
+      cursor: pointer;
+      position: absolute;
+      top: 50%;
+      width: 40px;
+      height: 40px;
+      right: -40px;
+      z-index: 12;
+      transform: translateY(-50%);
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+
+    .R_arrowNo {
+      opacity: .5;
+      pointer-events: none;
+    }
+
+    .R_indBox {
+      position: absolute;
+      bottom: -45px;
+      right: 0;
+      z-index: 11;
+      background-color: #2d2724;
+      height: 40px;
+      line-height: 40px;
+      color: var(--themeColor);
+      text-align: center;
+      padding: 0 20px;
+      border-radius: 20px;
+    }
+  }
+}

+ 88 - 0
pc/src/pages/A2Main/GoodsInfo/RightFile/index.tsx

@@ -0,0 +1,88 @@
+/* eslint-disable jsx-a11y/iframe-has-title */
+import React, { useCallback, useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import { A2FileType } from "@/types";
+import { baseURL } from "@/utils/http";
+import classNames from "classnames";
+import ImageLazy from "@/ImageLazy";
+import R_leftImg from "@/assets/img/goods/R_left.png";
+import R_rightImg from "@/assets/img/goods/R_right.png";
+
+type Props = {
+  list: A2FileType[];
+};
+
+function RightFile({ list }: Props) {
+  // 当前显示的 索引
+
+  const [showInd, setShowInd] = useState(0);
+
+  const cutIndFu = useCallback(
+    (num: number) => {
+      setShowInd(showInd + num);
+    },
+    [showInd]
+  );
+  // 第一次不显示暂无信息
+  const [flagOne, setFlagOne] = useState(false);
+
+  useEffect(() => {
+    window.setTimeout(() => {
+      setFlagOne(true);
+    }, 500);
+  }, []);
+
+  return (
+    <div className={styles.RightFile}>
+      {list.map((v, i) => (
+        <div
+          className={classNames("R1_row", i === showInd ? "R1_rowAc" : "")}
+          key={v.id}
+        >
+          {v.type === "model" ? (
+            <iframe src={`model.html?m=${v.filePath}`} frameBorder="0"></iframe>
+          ) : v.type === "video" ? (
+            <video src={baseURL + v.filePath} controls></video>
+          ) : v.type === "img" ? (
+            <ImageLazy src={v.filePath} width="100%" height="100%" />
+          ) : null}
+        </div>
+      ))}
+
+      {/* 只上传了音频的情况 */}
+      {list.length <= 0 ? (
+        <div className="R_oneAudio" hidden={!flagOne}>
+          暂无更多内容
+        </div>
+      ) : null}
+
+      {/* 左右按钮 */}
+      <div
+        hidden={list.length <= 1}
+        onClick={() => cutIndFu(-1)}
+        className={classNames("R_left", showInd === 0 ? "R_arrowNo" : "")}
+      >
+        <img src={R_leftImg} alt="" />
+      </div>
+      <div
+        hidden={list.length <= 1}
+        onClick={() => cutIndFu(1)}
+        className={classNames(
+          "R_right",
+          showInd >= list.length - 1 ? "R_arrowNo" : ""
+        )}
+      >
+        <img src={R_rightImg} alt="" />
+      </div>
+
+      {/* 下标 */}
+      <div className="R_indBox" hidden={list.length <= 1}>
+        {showInd + 1} / {list.length}
+      </div>
+    </div>
+  );
+}
+
+const MemoRightFile = React.memo(RightFile);
+
+export default MemoRightFile;

+ 281 - 0
pc/src/pages/A2Main/GoodsInfo/index.module.scss

@@ -0,0 +1,281 @@
+.GoodsInfo {
+  width: 100%;
+  height: 100%;
+  border-radius: 50px 50px 0 0;
+  background-color: rgba(0, 0, 0, .9);
+
+  :global {
+    .G_close {
+      padding: 15px 20px 0 20px;
+      height: 90px;
+      text-align: right;
+
+      &>img {
+        cursor: pointer;
+        width: 70px;
+        height: 70px;
+      }
+    }
+
+    .G_closeLL {
+      text-align: left;
+    }
+
+    .G_Main {
+      width: 100%;
+      height: calc(100% - 100px);
+      padding: 0px 140px 0 180px;
+
+      .G_title {
+        color: var(--themeColor);
+        font-size: 34px;
+        font-weight: 700;
+      }
+
+      .G_con {
+        width: 100%;
+        height: calc(100% - 160px);
+        padding: 30px 0;
+        margin-top: 15px;
+        display: flex;
+
+        .G_leftMain {
+          width: 46%;
+          margin-right: 4%;
+          border-top: 1px solid var(--themeColor);
+          border-bottom: 1px solid var(--themeColor);
+          position: relative;
+          padding: 15px 0;
+
+          &::before {
+            content: '';
+            position: absolute;
+            left: 0;
+            top: -3px;
+            width: 60px;
+            height: 3px;
+            background-color: var(--themeColor);
+          }
+
+          &::after {
+            content: '';
+            position: absolute;
+            right: 0;
+            bottom: 0px;
+            width: 60px;
+            height: 3px;
+            background-color: var(--themeColor);
+          }
+
+          .G_left1 {
+            height: 100%;
+            padding-top: 40px;
+
+            .G_left1Row {
+              display: flex;
+              color: #fff;
+              margin-bottom: 40px;
+
+              .G_left1Row_la {
+                font-weight: 700;
+                font-size: 18px;
+                width: 55px;
+              }
+
+              .G_left1Row_txt {
+                line-height: 24px;
+                width: calc(100% - 55px);
+                font-size: 16px;
+                word-wrap: break-word;
+              }
+            }
+          }
+        }
+
+        .G_rightMain {
+          width: 50%;
+        }
+
+      }
+
+      .G_bottom {
+        margin-top: 10px;
+        height: 60px;
+        display: flex;
+        align-items: center;
+
+        .G_bottom_left {
+          width: 46%;
+          margin-right: 4%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+
+          .G_bottom_left_row {
+            display: flex;
+            align-items: center;
+
+            .G_bottom_left_row_1 {
+              cursor: pointer;
+              position: relative;
+
+              width: 50px;
+              height: 50px;
+              border-radius: 50%;
+              border: 1px dashed var(--themeColor);
+              color: var(--themeColor);
+              font-size: 18px;
+              text-align: center;
+              line-height: 48px;
+
+              &>span {
+                position: relative;
+                z-index: 2;
+              }
+
+              &:hover {
+                color: #483A2D;
+
+                &::before {
+                  content: '';
+                  position: absolute;
+                  width: 86%;
+                  height: 86%;
+                  top: 50%;
+                  left: 50%;
+                  transform: translate(-50%, -50%);
+                  border-radius: 50%;
+                  background-color: #F0D99C;
+                }
+              }
+            }
+
+            .G_bottom_left_row_1Ac {
+              color: #483A2D;
+
+              &::before {
+                content: '';
+                position: absolute;
+                width: 86%;
+                height: 86%;
+                top: 50%;
+                left: 50%;
+                transform: translate(-50%, -50%);
+                border-radius: 50%;
+                background-color: #F0D99C;
+              }
+            }
+
+            .G_bottom_left_row_2 {
+              width: 150px;
+              margin: 0 10px;
+              height: 1px;
+              border-bottom: 1px dashed var(--themeColor);
+            }
+
+
+          }
+
+        }
+
+        .G_bottom_right {
+          width: 50%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          padding: 0 100px;
+
+          .G_bottom_right_main {
+            background-color: rgba(98, 82, 65, 0.6);
+            height: 50px;
+            border-radius: 25px;
+            display: flex;
+            justify-content: center;
+            padding: 0 20px;
+
+            .R_row {
+              padding: 0 20px;
+              display: flex;
+              align-items: center;
+              position: relative;
+
+              &>img {
+                cursor: pointer;
+
+                width: 40px;
+                height: 40px;
+              }
+
+
+
+
+              .moveImg {
+                pointer-events: none;
+                position: absolute;
+                bottom: 0;
+                left: 20px;
+                color: var(--themeColor);
+                width: 60px;
+
+                &>img {
+                  width: 40px;
+                  height: 40px;
+
+                }
+              }
+
+            }
+          }
+
+        }
+      }
+
+    }
+
+    // 滚动弹幕
+    .barrMove {
+      position: absolute;
+      right: -1300px;
+      top: 150px;
+      background-color: rgba(98, 82, 65, 0.6);
+      backdrop-filter: blur(6px);
+      padding: 8px 30px;
+      border-radius: 36px;
+      overflow: hidden;
+
+      &>h3 {
+        font-size: 20px;
+      }
+
+      &>P {
+        font-size: 12px;
+        opacity: .8;
+      }
+
+    }
+
+    // 留言板
+    .R_formBox {
+      pointer-events: none;
+      opacity: 0;
+      transition: all .3s;
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 99;
+      backdrop-filter: blur(4px);
+      background-color: rgba(0, 0, 0, .8);
+    }
+
+    .R_formBoxAc {
+      opacity: 1;
+      pointer-events: auto;
+    }
+
+  }
+
+
+
+}

+ 456 - 0
pc/src/pages/A2Main/GoodsInfo/index.tsx

@@ -0,0 +1,456 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import {
+  A2_APIgetBarrage,
+  A2_APIgetBarrageAll,
+  A2_APIgetConfigBarrage,
+  A2_APIgetGoodsInfo,
+  A2_APIgetQuestion,
+  A2_APIgoodsaddStar,
+} from "@/store/action/A2Main";
+import {
+  A2BarrageType,
+  A2FileType,
+  A2GoodsType,
+  A2QuestionType,
+} from "@/types";
+import { baseURL } from "@/utils/http";
+import closeImg from "@/assets/img/goods/close.png";
+import homeImg from "@/assets/img/goods/toHome.png";
+import history from "@/utils/history";
+import classNames from "classnames";
+import icon1 from "@/assets/img/goods/icon1.png";
+import icon2 from "@/assets/img/goods/icon2.png";
+import icon3 from "@/assets/img/goods/icon3.png";
+import icon4 from "@/assets/img/goods/icon4.png";
+import icon5 from "@/assets/img/goods/icon5.png";
+import icon1Ac from "@/assets/img/goods/icon1Ac.png";
+import icon3Ac from "@/assets/img/goods/icon3Ac.png";
+import icon4Ac from "@/assets/img/goods/icon4Ac.png";
+import icon5Ac from "@/assets/img/goods/icon5Ac.png";
+import { MessageFu } from "@/utils/message";
+import { gsap } from "gsap";
+import Left2 from "./Left2";
+import Left3 from "./Left3";
+import Left4 from "./Left4";
+import RightFile from "./RightFile";
+import Rform from "./Rform";
+
+type Props = {
+  isOpen: boolean;
+  id: number;
+  closePage?: () => void;
+};
+
+function GoodsInfo({ isOpen, id, closePage }: Props) {
+  // 文物信息
+  const [info, setInfo] = useState<A2GoodsType>({} as A2GoodsType);
+  // 附件数据
+  const [fileList, setFileList] = useState<A2FileType[]>([]);
+
+  // 音频的url
+  const [audio, setAudio] = useState("");
+
+  // 右侧图标
+  const [right1, setRight1] = useState({ show: false, done: false });
+  const [right2, setRight2] = useState({ show: false, done: false });
+  const [right3, setRight3] = useState({ show: false, done: false });
+  const [right4, setRight4] = useState(false);
+  const [right5, setRight5] = useState(false);
+
+  // 打开和关闭音频
+  const audioCutFu = useCallback((val: boolean) => {
+    const dom: HTMLAudioElement = document.querySelector("#myAudio")!;
+    if (val) dom.play();
+    else dom.pause();
+    setRight1({ show: true, done: val });
+  }, []);
+
+  // 点击点赞
+  const likeClickFu = useCallback(async () => {
+    if (right4) return;
+    await A2_APIgoodsaddStar(id);
+    setRight4(true);
+    window.setTimeout(() => {
+      setRight4(false);
+    }, 3000);
+  }, [id, right4]);
+
+  // 点击分享链接
+  const fenXClickFu = useCallback(() => {
+    if (right5) return;
+    setRight5(true);
+    window.setTimeout(() => {
+      setRight5(false);
+    }, 3000);
+    let OrderNumber = window.location.origin + "/#/goods?id=" + id;
+    let newInput = document.createElement("input");
+    newInput.value = OrderNumber;
+    document.body.appendChild(newInput);
+    newInput.select();
+    document.execCommand("Copy");
+    newInput.remove();
+    MessageFu.success("复制链接成功");
+  }, [id, right5]);
+
+  // 简介-知识-问答-留言
+  const [leftAc, setLeftAc] = useState(1);
+
+  const leftArrTemp = useMemo(() => {
+    return [
+      { id: 1, name: "简介", done: true },
+      { id: 2, name: "知识", done: false },
+      { id: 3, name: "问答", done: false },
+      { id: 4, name: "留言", done: false },
+    ];
+  }, []);
+
+  const [leftArr, setLeftArr] = useState(leftArrTemp.filter((v) => v.done));
+
+  // 问答数组
+  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 barrIndRef = useRef(0);
+  const barrTimeRef = useRef(0);
+  const barrMoveRefKill = useRef<any>(null);
+
+  useEffect(() => {
+    if (barrageAll.length) {
+      // 开启定时器
+      barrTimeRef.current = window.setInterval(() => {
+        let num = barrIndRef.current + 1;
+        if (num >= barrageAll.length) num = 0;
+        setBarrInd(num);
+        barrIndRef.current = num;
+      }, 17000);
+    }
+  }, [barrageAll.length]);
+
+  // 每次弹幕的索引变化的时候
+  useEffect(() => {
+    window.setTimeout(() => {
+      const width = barrMoveRef.current?.offsetWidth || 0;
+
+      if (barrMoveRef.current) {
+        if (barrMoveRef.current) {
+          barrMoveRef.current.style.right = -width - 200 + "px";
+        }
+
+        const endNum = -window.innerWidth - width - 200;
+        barrMoveRefKill.current?.kill();
+        // 开始动画
+        barrMoveRefKill.current = gsap.fromTo(
+          barrMoveRef.current,
+          { x: 0 },
+          {
+            duration: 16,
+            ease: "none",
+            x: endNum,
+          }
+        );
+      }
+    }, 200);
+  }, [barrInd]);
+
+  // 通过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) {
+              // 显示弹幕开关
+              setRight3({ show: true, done: true });
+            }
+
+            // 文物的弹幕留言开关 和总 弹幕开关都开启了
+            if (info.isBarrage) setRight2({ show: true, done: false });
+          }
+        }
+
+        const file: A2FileType[] = res.data.file;
+
+        // 附件按照 模型-视频-图片的顺序
+        let modelObj = {} as A2FileType;
+        let videoObj = {} as A2FileType;
+        const imgArr: A2FileType[] = [];
+        file.forEach((v) => {
+          if (v.type === "model") modelObj = v;
+          else if (v.type === "video") videoObj = v;
+          else if (v.type === "img") imgArr.push(v);
+        });
+        let fileListTemp: A2FileType[] = [];
+        if (modelObj.id) fileListTemp.push(modelObj);
+        if (videoObj.id) fileListTemp.push(videoObj);
+
+        fileListTemp = [...fileListTemp, ...imgArr];
+        setFileList(fileListTemp);
+
+        // 有上传音频
+        const isAudioObj = file.find((v) => v.type === "audio");
+        if (isAudioObj) {
+          setAudio(isAudioObj.filePath);
+          setRight1({ show: true, done: false });
+          window.setTimeout(() => {
+            const dom: HTMLAudioElement = document.querySelector("#myAudio")!;
+            dom.onended = () => {
+              // console.log("-------音频播放结束");
+
+              // 音频播放结束
+              setRight1({ show: true, done: false });
+            };
+            // 音频的状态
+            if (!isOpen) {
+              dom.play();
+
+              setRight1({ show: true, done: true });
+            }
+          }, 100);
+        }
+
+        setInfo(info);
+
+        // 看看是否有相关的问答
+        const res2 = await A2_APIgetQuestion(id);
+        if (res2.code === 0) {
+          // 如有有选择 知识驿站
+          if (info.tagType) leftArrTemp[1].done = true;
+          // 如果有问答
+          if (res2.data.length) leftArrTemp[2].done = true;
+          const res3 = await A2_APIgetBarrage(id);
+          setQuestion(res2.data);
+          if (res3.code === 0) {
+            // 如果这条文物有留言
+            if (res3.data.length) leftArrTemp[3].done = true;
+            setBarrage(res3.data);
+            setLeftArr(leftArrTemp.filter((v) => v.done));
+          }
+        }
+      }
+    },
+    [isOpen, leftArrTemp]
+  );
+
+  useEffect(() => {
+    getDataFu(id);
+    return () => {
+      clearInterval(barrTimeRef.current);
+      barrMoveRefKill.current?.kill();
+    };
+  }, [getDataFu, id]);
+
+  // 点击关闭按钮
+  const closeFu = useCallback(() => {
+    // 新页面打开回到首页
+    if (isOpen) history.push("/");
+    else closePage!();
+  }, [closePage, isOpen]);
+
+  return (
+    <div className={styles.GoodsInfo}>
+      {audio ? <audio src={baseURL + audio} id="myAudio"></audio> : null}
+
+      {/* 关闭按钮 */}
+      <div className={classNames("G_close", isOpen ? "G_closeLL" : "")}>
+        <img
+          onClick={() => closeFu()}
+          src={isOpen ? homeImg : closeImg}
+          alt=""
+        />
+      </div>
+
+      <div className="G_Main">
+        {/* 标题 */}
+        <div className="G_title">{info.name ? info.name : "-"}</div>
+
+        <div className="G_con">
+          {/* 左侧容器 */}
+          <div className="G_leftMain">
+            {/* 简介 */}
+            <div className="G_left1" hidden={leftAc !== 1}>
+              <div className="G_left1Row">
+                <div className="G_left1Row_la">类别:</div>
+                <div className="G_left1Row_txt">{info.dictTexture}</div>
+              </div>
+              <div className="G_left1Row">
+                <div className="G_left1Row_la">年代:</div>
+                <div className="G_left1Row_txt">{info.dictAge}</div>
+              </div>
+              <div className="G_left1Row">
+                <div className="G_left1Row_la">级别:</div>
+                <div className="G_left1Row_txt">{info.dictLevel}</div>
+              </div>
+              <div className="G_left1Row">
+                <div className="G_left1Row_la">简介:</div>
+                <div className="G_left1Row_txt">
+                  {info.description ? info.description : "(空)"}
+                </div>
+              </div>
+            </div>
+            {/* 知识 */}
+            {leftAc === 2 ? (
+              <Left2 type={info.tagType} />
+            ) : leftAc === 3 ? (
+              <Left3 list={question} />
+            ) : leftAc === 4 ? (
+              <Left4 list={barrage} />
+            ) : null}
+          </div>
+          {/* 右侧容器 */}
+          <div className="G_rightMain">
+            <RightFile list={fileList} />
+          </div>
+        </div>
+
+        {/* 底部容器 */}
+        <div className="G_bottom">
+          <div className="G_bottom_left">
+            {leftArr.map((v, i) => (
+              <div className="G_bottom_left_row" key={v.id}>
+                <div
+                  onClick={() => setLeftAc(v.id)}
+                  className={classNames(
+                    "G_bottom_left_row_1",
+                    leftAc === v.id ? "G_bottom_left_row_1Ac" : ""
+                  )}
+                >
+                  <span>{v.name}</span>
+                </div>
+                <div
+                  className="G_bottom_left_row_2"
+                  hidden={i === leftArr.length - 1}
+                ></div>
+              </div>
+            ))}
+          </div>
+          <div className="G_bottom_right">
+            <div className="G_bottom_right_main">
+              {/* 音频 */}
+              <div className="R_row" hidden={!right1.show}>
+                <img
+                  src={icon1Ac}
+                  alt=""
+                  title="打开音频"
+                  hidden={right1.done}
+                  onClick={() => audioCutFu(true)}
+                />
+                <img
+                  onClick={() => audioCutFu(false)}
+                  src={icon1}
+                  alt=""
+                  title="关闭音频"
+                  hidden={!right1.done}
+                />
+              </div>
+              {/* 弹幕留言 */}
+              <div
+                className="R_row"
+                hidden={!right2.show}
+                onClick={() => setRight2({ show: true, done: true })}
+              >
+                <img src={icon2} alt="" title="打开留言板" />
+              </div>
+              {/* 弹幕开关 */}
+              <div className="R_row" hidden={!right3.show}>
+                <img
+                  src={icon3Ac}
+                  alt=""
+                  title="打开弹幕"
+                  hidden={right3.done}
+                  onClick={() => setRight3({ show: true, done: true })}
+                />
+                <img
+                  src={icon3}
+                  alt=""
+                  title="关闭弹幕"
+                  hidden={!right3.done}
+                  onClick={() => setRight3({ show: true, done: false })}
+                />
+              </div>
+              {/* 点赞 */}
+              <div className="R_row">
+                <img
+                  title="点赞"
+                  src={right4 ? icon4Ac : icon4}
+                  alt=""
+                  onClick={() => likeClickFu()}
+                />
+                <div className="moveImg" hidden={!right4}>
+                  <img src={icon4Ac} alt="" />
+                  +1
+                </div>
+              </div>
+              {/* 分享 */}
+              <div className="R_row">
+                <img
+                  title="分享"
+                  src={right5 ? icon5Ac : icon5}
+                  alt=""
+                  onClick={() => fenXClickFu()}
+                />
+                <img
+                  className="moveImg"
+                  src={icon5Ac}
+                  alt=""
+                  hidden={!right5}
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      {/* 弹幕的盒子 */}
+      {barrageAll.length ? (
+        <div className="barrMove" ref={barrMoveRef} hidden={!right3.done}>
+          <h3>{barrageAll[barrInd].name}</h3>
+          <p>
+            {barrageAll[barrInd].authorName}&nbsp;
+            {barrageAll[barrInd].updateTime}
+            &nbsp;观&nbsp;[{barrageAll[barrInd].goodsName}]&nbsp;有感
+          </p>
+        </div>
+      ) : null}
+
+      {/* 留言板 */}
+      <div
+        className={classNames("R_formBox", right2.done ? "R_formBoxAc" : "")}
+      >
+        {right2.done ? (
+          <Rform
+            goodsId={id}
+            closeFu={() => setRight2({ show: true, done: false })}
+          />
+        ) : null}
+      </div>
+    </div>
+  );
+}
+
+const MemoGoodsInfo = React.memo(GoodsInfo);
+
+export default MemoGoodsInfo;

+ 8 - 2
pc/src/pages/A2Main/GoodsSw/index.module.scss

@@ -90,6 +90,10 @@
                 width: 1px;
                 height: 90px;
               }
+
+              img {
+                object-fit: contain;
+              }
             }
           }
         }
@@ -124,6 +128,7 @@
                 top: -80px;
                 background: linear-gradient(rgba(231, 219, 188, 1), rgba(231, 219, 188, 0));
               }
+
             }
           }
         }
@@ -233,10 +238,11 @@
       left: 0;
       width: 100%;
       height: 100%;
-      background-color: aqua;
       opacity: 0;
       pointer-events: none;
       transition: all .3s;
+      backdrop-filter: blur(10px);
+      padding-top: 30px;
 
       &>div {
         color: #fff;
@@ -244,7 +250,7 @@
     }
 
     .goodsInfoBoxAc {
-      opacity: .6;
+      opacity: 1;
       pointer-events: auto;
     }
   }

+ 12 - 4
pc/src/pages/A2Main/GoodsSw/index.tsx

@@ -13,6 +13,7 @@ import { RootState } from "@/store";
 import ImageLazy from "@/ImageLazy";
 import { Input, Select } from "antd";
 import classNames from "classnames";
+import GoodsInfo from "../GoodsInfo";
 
 function GoodsSw() {
   const dispatch = useDispatch();
@@ -93,7 +94,7 @@ function GoodsSw() {
 
   // -----点击了单个藏品打开的页面----------
   const [open, setOpen] = useState(false);
-  const openRef = useRef(false);
+  const openRef = useRef(0);
 
   // 第一个盒子的总宽度
   const goddsSw1 = useMemo(() => {
@@ -205,10 +206,11 @@ function GoodsSw() {
 
   // 点击藏品
   const clickGoodFu = useCallback((id: number) => {
-    openRef.current = true;
+    openRef.current = id;
+
     setOpen(true);
 
-    console.log(123, id);
+    // console.log(123, id);
   }, []);
 
   // 打开了单个文物详情页,关闭之后的逻辑
@@ -334,7 +336,13 @@ function GoodsSw() {
 
       {/* 当个藏品详情盒子 */}
       <div className={classNames("goodsInfoBox", open ? "goodsInfoBoxAc" : "")}>
-        <div onClick={() => setOpen(false)}>关闭</div>
+        {open ? (
+          <GoodsInfo
+            isOpen={false}
+            id={openRef.current}
+            closePage={() => setOpen(false)}
+          />
+        ) : null}
       </div>
     </div>
   );

+ 12 - 5
pc/src/pages/A2Main/KnowLedge/index.module.scss

@@ -50,19 +50,26 @@
         text-overflow: ellipsis;
         white-space: nowrap;
         cursor: pointer;
-        height: 70px;
-        line-height: 70px;
-        font-size: 20px;
+        height: 60px;
+        line-height: 60px;
+        font-size: 18px;
         color: #fff;
+        border-radius: 0 10px 0 10px;
+        margin-bottom: 10px;
 
         &:hover {
           color: #4a403d;
-          font-size: 22px;
+          font-size: 20px;
           background-color: var(--themeColor);
-          border-radius: 0 10px 0 10px;
 
         }
       }
+
+      .knowConRowAc {
+        color: #4a403d;
+        font-size: 20px;
+        background-color: var(--themeColor);
+      }
     }
   }
 }

+ 11 - 2
pc/src/pages/A2Main/KnowLedge/index.tsx

@@ -32,6 +32,9 @@ function KnowLedge() {
     if (dom) dom.scrollTop = 0;
   }, []);
 
+  // 下面的选中
+  const [likeAc, setLinkAc] = useState(0);
+
   return (
     <div className={styles.KnowLedge}>
       <div className="knowMain">
@@ -54,10 +57,16 @@ function KnowLedge() {
         <div className="knowCon myscroll">
           {data.map((v) => (
             <div
-              className="knowConRow"
+              className={classNames(
+                "knowConRow",
+                likeAc === v.id ? "knowConRowAc" : ""
+              )}
               title={v.name}
               key={v.id}
-              onClick={() => window.open(v.link)}
+              onClick={() => {
+                window.open(v.link);
+                setLinkAc(v.id);
+              }}
             >
               {v.name}
             </div>

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

@@ -114,7 +114,7 @@
 
         .openIcon {
           cursor: pointer;
-          transition: all .2s;
+          transition: all .3s;
           position: absolute;
           top: 15px;
           right: 38px;
@@ -124,6 +124,10 @@
           background-size: 100% 100%;
         }
 
+        .openIconAc {
+          transform: rotate(180deg);
+        }
+
       }
     }
 

+ 5 - 3
pc/src/pages/A2Main/index.tsx

@@ -108,9 +108,11 @@ function A2Main() {
             ))}
             {/* 展开收起按钮 */}
             <div
-              className={classNames("openIcon")}
-              hidden={!type}
-              onClick={() => setType(0)}
+              className={classNames("openIcon", type ? "openIconAc" : "")}
+              onClick={() => {
+                if (type) setType(0);
+                else typeChangeFu(incoArr[0]);
+              }}
             ></div>
           </div>
           {/* 轮播图主体 */}

+ 11 - 0
pc/src/pages/A3Goods/index.module.scss

@@ -0,0 +1,11 @@
+.A3Goods {
+  width: 100%;
+  height: 100%;
+  color: #fff;
+  position: relative;
+  backdrop-filter: blur(10px);
+  padding-top: 30px;
+  background-image: url('../../assets/img/homeBg.jpg');
+  background-size: 100% 100%;
+  // :global {}
+}

+ 27 - 0
pc/src/pages/A3Goods/index.tsx

@@ -0,0 +1,27 @@
+import React, { useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import GoodsInfo from "../A2Main/GoodsInfo";
+import { urlParameter } from "@/utils/history";
+import { useLocation } from "react-router-dom";
+
+function A3Goods() {
+  const [id, setId] = useState(0);
+
+  // 获取地址栏参数
+  const location = useLocation();
+  useEffect(() => {
+    const obj = urlParameter(location.search);
+    if (obj.id) setId(Number(obj.id));
+    else alert("参数错误!");
+  }, [location.search]);
+
+  return (
+    <div className={styles.A3Goods}>
+      {id ? <GoodsInfo id={id} isOpen={true} /> : null}
+    </div>
+  );
+}
+
+const MemoA3Goods = React.memo(A3Goods);
+
+export default MemoA3Goods;

+ 66 - 1
pc/src/store/action/A2Main.ts

@@ -1,6 +1,6 @@
 import http from "@/utils/http";
 import store, { AppDispatch } from "..";
-import { A2SelectType, A2getGoodsDataType } from "@/types";
+import { A2R_fSaveType, A2SelectType, A2getGoodsDataType } from "@/types";
 
 /**
  * 获文物列表数据
@@ -58,3 +58,68 @@ export const A2_APIgetSelectData = (type: "age" | "texture") => {
 export const A2_APIgetKnowData = (type: string) => {
   return http.get(`show/getKnowledge?tagType=${type}`);
 };
+
+/**
+ * 通过id获取详情
+ */
+export const A2_APIgetGoodsInfo = (id: number) => {
+  return http.get(`show/goods/detail/${id}`);
+};
+
+/**
+ * 获取问答列表
+ */
+export const A2_APIgetQuestion = (id: number) => {
+  return http.get(`show/goods/getQuestion/${id}`);
+};
+
+/**
+ * 获取留言列表
+ */
+export const A2_APIgetBarrage = (id: number) => {
+  return http.get(`show/goods/getBarrage/${id}`);
+};
+
+/**
+ * 获取弹幕列表随机 20条
+ */
+export const A2_APIgetBarrageAll = () => {
+  return http.get(`show/barrage/limitList/20`);
+};
+
+/**
+ * 获取弹幕总开关
+ */
+export const A2_APIgetConfigBarrage = () => {
+  return http.get("show/getConfig/barrage");
+};
+
+/**
+ * 文物点赞
+ */
+export const A2_APIgoodsaddStar = (id: number) => {
+  return http.get(`show/goods/addStar/${id}`);
+};
+
+//------------left2
+/**
+ * 获取知识列表
+ */
+export const A2_APIgetKnowledge = (type: string) => {
+  return http.get(`show/goods/getKnowledge/${type}`);
+};
+
+//------------R_FORM
+/**
+ * 获取验证码
+ */
+export const A2_APIgetRandCode = () => {
+  return http.get("show/getRandCode", { responseType: "blob" });
+};
+
+/**
+ * 新增留言(弹幕)
+ */
+export const A2_APIsaveBarrage = (data: A2R_fSaveType) => {
+  return http.post("show/goods/saveBarrage", data);
+};

+ 72 - 0
pc/src/types/store/A2Main.d.ts

@@ -51,3 +51,75 @@ export type A2KnowListType = {
   link: string;
   name: string;
 };
+
+export type A2FileType = {
+  createTime: string;
+  creatorId?: any;
+  creatorName: string;
+  fileName: string;
+  filePath: string;
+  id: number;
+  isFrame?: any;
+  moduleName: string;
+  type: "img" | "model" | "audio" | "video";
+  updateTime: string;
+};
+
+export type A2QuestionType = {
+  answer: string;
+  createTime: string;
+  creatorId: number;
+  creatorName: string;
+  description: string;
+  display: number;
+  goodsId: number;
+  goodsName: string;
+  id: number;
+  pcsAccuracy?: any;
+  pcsAnswer: number;
+  pcsTrue: number;
+  question: string;
+  updateTime: string;
+};
+
+export type A2QuestionResType = {
+  id: number;
+  txt: string;
+  goodsId: number;
+  title: string;
+  ok: string;
+  answer: { val: string; name: string }[];
+  done: boolean;
+  mySelect: string;
+};
+
+export type A2BarrageType = {
+  auditName: string;
+  authorName: string;
+  createTime: string;
+  creatorId: number;
+  creatorName: string;
+  display: number;
+  goodsId: number;
+  goodsName: string;
+  id: number;
+  name: string;
+  status: number;
+  updateTime: string;
+};
+
+export type A2Left2Type = {
+  name: string;
+  arr: {
+    id: number;
+    link: string;
+    name: string;
+  }[];
+};
+
+export type A2R_fSaveType = {
+  authorName: string;
+  goodsId: number;
+  name: string;
+  randCode: string;
+};