shaogen1995 1 рік тому
батько
коміт
3eca24b3ff

+ 44 - 0
package-lock.json

@@ -19,6 +19,7 @@
         "antd": "^5.8.3",
         "antd-mobile": "^5.30.0",
         "axios": "^1.1.3",
+        "lodash": "^4.17.21",
         "react": "^18.2.0",
         "react-dom": "^18.2.0",
         "react-redux": "^8.0.4",
@@ -29,11 +30,13 @@
         "redux-devtools-extension": "^2.13.9",
         "redux-thunk": "^2.4.1",
         "sass": "^1.55.0",
+        "swiper": "^9.1.0",
         "typescript": "^4.8.4",
         "web-vitals": "^2.1.4"
       },
       "devDependencies": {
         "@types/history": "^5.0.0",
+        "@types/lodash": "^4.14.198",
         "@types/react-router-dom": "^5.3.3",
         "customize-cra": "^1.0.0",
         "react-app-rewired": "^2.2.1"
@@ -3946,6 +3949,12 @@
       "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz",
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
     },
+    "node_modules/@types/lodash": {
+      "version": "4.14.198",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.198.tgz",
+      "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==",
+      "dev": true
+    },
     "node_modules/@types/mime": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
@@ -15356,6 +15365,11 @@
       "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
     },
+    "node_modules/ssr-window": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-4.0.2.tgz",
+      "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
+    },
     "node_modules/stable": {
       "version": "0.1.8",
       "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz",
@@ -15715,6 +15729,17 @@
         "boolbase": "~1.0.0"
       }
     },
+    "node_modules/swiper": {
+      "version": "9.4.1",
+      "resolved": "https://registry.npmmirror.com/swiper/-/swiper-9.4.1.tgz",
+      "integrity": "sha512-1nT2T8EzUpZ0FagEqaN/YAhRj33F2x/lN6cyB0/xoYJDMf8KwTFT3hMOeoB8Tg4o3+P/CKqskP+WX0Df046fqA==",
+      "dependencies": {
+        "ssr-window": "^4.0.2"
+      },
+      "engines": {
+        "node": ">= 4.7.0"
+      }
+    },
     "node_modules/symbol-tree": {
       "version": "3.2.4",
       "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -20009,6 +20034,12 @@
       "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz",
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
     },
+    "@types/lodash": {
+      "version": "4.14.198",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.198.tgz",
+      "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==",
+      "dev": true
+    },
     "@types/mime": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz",
@@ -28777,6 +28808,11 @@
       "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
     },
+    "ssr-window": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-4.0.2.tgz",
+      "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
+    },
     "stable": {
       "version": "0.1.8",
       "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz",
@@ -29071,6 +29107,14 @@
         }
       }
     },
+    "swiper": {
+      "version": "9.4.1",
+      "resolved": "https://registry.npmmirror.com/swiper/-/swiper-9.4.1.tgz",
+      "integrity": "sha512-1nT2T8EzUpZ0FagEqaN/YAhRj33F2x/lN6cyB0/xoYJDMf8KwTFT3hMOeoB8Tg4o3+P/CKqskP+WX0Df046fqA==",
+      "requires": {
+        "ssr-window": "^4.0.2"
+      }
+    },
     "symbol-tree": {
       "version": "3.2.4",
       "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz",

+ 3 - 0
package.json

@@ -14,6 +14,7 @@
     "antd": "^5.8.3",
     "antd-mobile": "^5.30.0",
     "axios": "^1.1.3",
+    "lodash": "^4.17.21",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "react-redux": "^8.0.4",
@@ -24,6 +25,7 @@
     "redux-devtools-extension": "^2.13.9",
     "redux-thunk": "^2.4.1",
     "sass": "^1.55.0",
+    "swiper": "^9.1.0",
     "typescript": "^4.8.4",
     "web-vitals": "^2.1.4"
   },
@@ -53,6 +55,7 @@
   },
   "devDependencies": {
     "@types/history": "^5.0.0",
+    "@types/lodash": "^4.14.198",
     "@types/react-router-dom": "^5.3.3",
     "customize-cra": "^1.0.0",
     "react-app-rewired": "^2.2.1"

+ 3 - 3
public/model.html

@@ -32,14 +32,14 @@
   <div class="bacBox"></div>
   <script>
     let number = getQueryVariable("m");
-
+    let num = getQueryVariable("n");
     // console.log('ppppppppp',number);
 
     window.autoRotate = true; // 是否自动旋转
 
     // 打包配置
-    fdage.embed( number, {
-    // fdage.embed('https://hnbwg.4dage.com' + number, {
+    // fdage.embed( number, {
+    fdage.embed(`http://192.168.20.55:8080/3Goods/${number}/main${num}.4dage`, {
       transparentBackground: true,
       width: 800,
       height: 600,

+ 15 - 1
src/App.tsx

@@ -10,7 +10,11 @@ import { useSelector } from "react-redux";
 import store, { RootState } from "./store";
 import MessageCom from "./components/Message";
 import LookDom from "./components/LookDom";
+import NotFound from "./components/NotFound";
 const A1Home = React.lazy(() => import("./pages/A1Home"));
+const A2VrPage = React.lazy(() => import("./pages/A2VrPage"));
+const A3Goods = React.lazy(() => import("./pages/A3Goods"));
+const A4Intro = React.lazy(() => import("./pages/A4Intro"));
 
 export default function App() {
   // 从仓库中获取查看图片的信息
@@ -24,7 +28,17 @@ export default function App() {
       <Router history={history}>
         <React.Suspense fallback={<SpinLoding />}>
           <Switch>
-            <Route path="/" component={A1Home} />
+            {/* 首页 */}
+            <Route path="/" exact component={A1Home} />
+            {/* 实景vr页面 */}
+            <Route path="/vr" component={A2VrPage} />
+            {/* 文物赏析页面 */}
+            <Route path="/goods" component={A3Goods} />
+            {/* 场馆介绍页面 */}
+            <Route path="/intro" component={A4Intro} />
+
+            {/* 找不到页面 */}
+            <Route path="*" component={NotFound} />
           </Switch>
         </React.Suspense>
       </Router>

BIN
src/assets/img/back.png


BIN
src/assets/img/name1.png


BIN
src/assets/img/name2.png


+ 1 - 1
src/assets/styles/base.css

@@ -41,7 +41,7 @@ textarea {
 }
 /* 主题色 */
 :root {
-  --themeColor: #00b3ec;
+  --themeColor: #bd130d;
 }
 /* 找不到页面 */
 .noFindPage {

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

@@ -50,7 +50,7 @@ textarea {
 
 /* 主题色 */
 :root {
-  --themeColor: #00b3ec;
+  --themeColor: #bd130d;
 }
 
 
@@ -82,7 +82,7 @@ textarea {
   overflow: auto;
   overflow-y: overlay;
 
-  &>div{
+  &>div {
     width: 100%;
     height: 100%;
   }

+ 10 - 6
src/components/NotFound/index.tsx

@@ -1,4 +1,5 @@
-import { Result } from "antd";
+import history from "@/utils/history";
+import { Button, Result } from "antd";
 import { useEffect, useRef } from "react";
 
 export default function NotFound() {
@@ -16,11 +17,14 @@ export default function NotFound() {
 
   return (
     <div className="noFindPage">
-      <Result
-        status="404"
-        title="404"
-        subTitle="找不到页面,或没有权限!"
-      />
+      <Result status="404" title="404" subTitle="找不到页面" />
+      <div style={{ display: "flex", justifyContent: "center" }}>
+        <Button type="primary" onClick={() => history.push("/")}>
+          首页
+        </Button>
+        &emsp;
+        <Button onClick={() => history.go(-1)}>返回</Button>
+      </div>
     </div>
   );
 }

+ 6 - 1
src/index.tsx

@@ -1,4 +1,5 @@
 // import 'default-passive-events';
+import { baseURL } from "./utils/http";
 
 import App from "./App";
 import store from "./store/index";
@@ -20,12 +21,16 @@ import locale from "antd/locale/zh_CN";
 const container = document.getElementById("root") as HTMLElement;
 const root = createRoot(container);
 
+console.log("静态资源地址:", baseURL);
+// @ts-ignore
+store.dispatch({ type: "layout/setDataAll", payload: dataAll });
+
 root.render(
   <ConfigProvider
     locale={locale}
     theme={{
       token: {
-        colorPrimary: "#00b3ec",
+        colorPrimary: "#bd130d",
       },
     }}
   >

+ 38 - 8
src/pages/A1Home/index.module.scss

@@ -1,30 +1,35 @@
-.A1Home{
+.A1Home {
   background-size: 100% 100%;
   position: relative;
-  :global{
-    .nameImg{
+
+  :global {
+    .nameImg {
       position: absolute;
       z-index: 10;
       left: 50px;
       top: 30px;
     }
-    .txtImg{
+
+    .txtImg {
       position: absolute;
       z-index: 10;
-      top: 180px;
+      top: 230px;
       left: 100px;
       width: 80%;
     }
-    .txt2Img{
+
+    .txt2Img {
       position: absolute;
       z-index: 10;
       bottom: 45px;
       left: 50px;
     }
-    .txt2Iccmg{
+
+    .txt2Iccmg {
       height: 100%;
     }
-    .rightBacBox{
+
+    .rightBacBox {
       position: absolute;
       z-index: 10;
       right: 0;
@@ -32,6 +37,31 @@
       height: 100%;
       width: 306px;
       background-size: 100% 100%;
+      padding: 20px 10px 20px 145px;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-around;
+
+      &>div {
+        cursor: pointer;
+        width: 150px;
+        height: 150px;
+        border: 1px solid red;
+        border-radius: 50%;
+        overflow: hidden;
+
+        &>img {
+          width: 100%;
+          height: 100%;
+          object-fit: contain;
+          transition: all .2s;
+        }
+        &:hover{
+          &>img{
+            transform: scale(1.1);
+          }
+        }
+      }
     }
   }
 }

+ 14 - 1
src/pages/A1Home/index.tsx

@@ -1,6 +1,13 @@
 import React from "react";
 import styles from "./index.module.scss";
 import { baseURL } from "@/utils/http";
+import history from "@/utils/history";
+
+const rightList = [
+  { id: 1, name: "实景VR", path: "/vr" },
+  { id: 2, name: "文物赏析", path: "/goods" },
+  { id: 3, name: "场馆介绍", path: "/intro" },
+];
 
 function A1Home() {
   return (
@@ -14,7 +21,13 @@ function A1Home() {
       <div
         className="rightBacBox"
         style={{ backgroundImage: `url(${baseURL}/Home/pc/rightBac.png)` }}
-      ></div>
+      >
+        {rightList.map((v) => (
+          <div onClick={() => history.push(v.path)} key={v.id}>
+            <img src={`${baseURL}/Home/pc/icon${v.id}.png`} alt="" />
+          </div>
+        ))}
+      </div>
     </div>
   );
 }

+ 21 - 0
src/pages/A2VrPage/index.module.scss

@@ -0,0 +1,21 @@
+.A2VrPage{
+  position: relative;
+  :global{
+    iframe{
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 2;
+      width: 100%;
+      height: 100%;
+    }
+    .vrBack{
+      cursor: pointer;
+      position: absolute;
+      top: 10px;
+      left: 40px;
+      z-index: 3;
+      width: 40px;
+    }
+  }
+}

+ 25 - 0
src/pages/A2VrPage/index.tsx

@@ -0,0 +1,25 @@
+/* eslint-disable jsx-a11y/iframe-has-title */
+import React from "react";
+import styles from "./index.module.scss";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+import backImg from "@/assets/img/back.png";
+import history from "@/utils/history";
+
+function A2VrPage() {
+  const data = useSelector((state: RootState) => state.A0Layout.dataAll.Home);
+
+  return (
+    <div className={styles.A2VrPage}>
+      {data.vr ? <iframe src={data.vr} frameBorder="0"></iframe> : null}
+      {/* 返回按钮 */}
+      <div className="vrBack" onClick={() => history.push("/")}>
+        <img src={backImg} alt="" />
+      </div>
+    </div>
+  );
+}
+
+const MemoA2VrPage = React.memo(A2VrPage);
+
+export default MemoA2VrPage;

+ 164 - 0
src/pages/A3Goods/GoodsInfo/index.module.scss

@@ -0,0 +1,164 @@
+.GoodsInfo {
+  position: absolute;
+  z-index: 99;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-size: 100% 100%;
+  padding: 140px 125px 180px;
+  overflow: hidden;
+
+  :global {
+    .A3IBack {
+      width: 48px;
+      cursor: pointer;
+      position: absolute;
+      z-index: 3;
+      top: 20px;
+      left: 40px;
+    }
+
+    .A3Imain {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: space-between;
+
+      .A3Ileft {
+        width: 54%;
+        background-color: #8e9596;
+        border-radius: 30px;
+        position: relative;
+
+        iframe {
+          border-radius: 30px;
+          width: 100%;
+          height: 100%;
+        }
+
+        .A3IleftBtn {
+          position: absolute;
+          bottom: 15px;
+          right: 12px;
+          z-index: 10;
+          display: flex;
+
+          &>div {
+            width: 40px;
+            height: 40px;
+            border-radius: 50%;
+            background-color: rgba(0, 0, 0, .6);
+            margin-left: 18px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            color: #fff;
+            font-size: 24px;
+            cursor: pointer;
+          }
+        }
+
+        .A3ItoBtn {
+          cursor: pointer;
+          position: absolute;
+          z-index: 10;
+          top: 50%;
+          transform: translateY(-50%);
+          left: -50px;
+          width: 41px;
+          height: 81px;
+          background-size: 100% 100%;
+
+        }
+
+        .A3ItoRight {
+          left: auto;
+          right: -50px;
+        }
+
+        .noneToBtn {
+          opacity: .5;
+          pointer-events: none;
+        }
+      }
+
+      // 全屏
+      .A3IleftFull {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        z-index: 40;
+
+        iframe {
+          border-radius: 0;
+        }
+      }
+
+      .A3IRight {
+        width: 42%;
+        padding: 40px 20px 0;
+
+        .A3IRtit {
+          display: flex;
+
+          &>img {
+            width: 180px;
+            position: relative;
+            top: -24px;
+          }
+
+          &>div {
+            color: #FBF1AD;
+            font-size: 26px;
+            font-weight: 700;
+            margin: 0 20px;
+            width: calc(100% - 400px);
+            text-align: center;
+            letter-spacing: 4px;
+          }
+        }
+
+        .A3IRtit2 {
+          color: #FBF1AD;
+          font-size: 22px;
+          text-align: center;
+          letter-spacing: 4px;
+          margin-bottom: 40px;
+        }
+
+        .A3IRtitRow {
+          padding: 0 0 0 220px;
+          position: relative;
+          margin-top: 30px;
+          font-size: 18px;
+          color: #fff;
+          letter-spacing: 2px;
+
+          .A3IRtitRowYuan {
+            position: absolute;
+            left: 190px;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 14px;
+            height: 14px;
+            border-radius: 50%;
+            border: 1px solid #fff;
+
+            &>div {
+              position: relative;
+              top: 2px;
+              left: 2px;
+              width: 8px;
+              height: 8px;
+              background-color: #fff;
+              border-radius: 50%;
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 207 - 0
src/pages/A3Goods/GoodsInfo/index.tsx

@@ -0,0 +1,207 @@
+/* eslint-disable jsx-a11y/iframe-has-title */
+import React, { useMemo, useRef, useState } from "react";
+import styles from "./index.module.scss";
+import { Goods } from "@/types";
+import { baseURL } from "@/utils/http";
+import { type1Arr1 } from "../data";
+import backImg from "@/assets/img/back.png";
+import {
+  PlusOutlined,
+  MinusOutlined,
+  FullscreenOutlined,
+  FullscreenExitOutlined,
+} from "@ant-design/icons";
+import classNames from "classnames";
+
+import name1Img from "@/assets/img/name1.png";
+import name2Img from "@/assets/img/name2.png";
+
+type Props = {
+  info: Goods;
+  colseFu: () => void;
+  type: string;
+};
+
+function GoodsInfo({ info, colseFu, type }: Props) {
+  // 背景图
+  const imgBac = useMemo(() => {
+    const info = type1Arr1.find((v) => v.name === type);
+    if (info) return `info${info.id}Bac.jpg`;
+    else return "";
+  }, [type]);
+
+  // 看看有几个模型
+  const modelNum = useMemo(() => {
+    const arr = [1];
+
+    if (info.modelNum && info.modelNum > 1) {
+      for (let i = 0; i < info.modelNum; i++) {
+        if (i !== 0) arr.push(i + 1);
+      }
+    }
+    return arr;
+  }, [info.modelNum]);
+
+  // 当前显示的模型
+  const [modelShow, setModelShow] = useState(1);
+
+  // 模型的全屏
+  const [full, setFull] = useState(false);
+
+  // 控制模型放大缩小和复位
+  const ifrBoxRef = useRef<any>(null);
+  const modelChangeFu = (val: number) => {
+    const dom = ifrBoxRef.current;
+
+    if (dom && dom.contentWindow && dom.contentWindow.webview) {
+      if (val === 1) dom.contentWindow.webview.zoomIn(); // 放大
+      else if (val === 2) dom.contentWindow.webview.zoomOut(); // 缩小
+      else dom.contentWindow.webview.resetView(); // 复位
+    }
+  };
+
+  return (
+    <div
+      className={styles.GoodsInfo}
+      style={{
+        backgroundImage: `url(${baseURL}/3Goods/pc/${imgBac})`,
+      }}
+    >
+      <img onClick={colseFu} className="A3IBack" src={backImg} alt="" />
+
+      <div className="A3Imain">
+        {/* 左边模型 */}
+        <div
+          className={classNames("A3Ileft", full ? "A3IleftFull" : "")}
+          key={modelShow}
+        >
+          {modelNum.map((v) =>
+            modelShow === v ? (
+              <iframe
+                key={v}
+                ref={ifrBoxRef}
+                src={`model.html?m=${info.id}&n=${v}`}
+                frameBorder="no"
+              ></iframe>
+            ) : null
+          )}
+
+          {/* 左右按钮 */}
+          <div
+            onClick={() => setModelShow(modelShow - 1)}
+            hidden={modelNum.length <= 1}
+            style={{ backgroundImage: `url(${baseURL}/4Intro/pc/left.png)` }}
+            className={classNames(
+              "A3ItoBtn",
+              "A3ItoLeft",
+              modelShow === 1 ? "noneToBtn" : ""
+            )}
+          ></div>
+          <div
+            onClick={() => setModelShow(modelShow + 1)}
+            hidden={modelNum.length <= 1}
+            style={{ backgroundImage: `url(${baseURL}/4Intro/pc/right.png)` }}
+            className={classNames(
+              "A3ItoBtn",
+              "A3ItoRight",
+              modelShow === modelNum.length ? "noneToBtn" : ""
+            )}
+          ></div>
+
+          {/* 右下角的按钮 */}
+          <div className="A3IleftBtn">
+            <div onClick={() => modelChangeFu(1)}>
+              <PlusOutlined rev={undefined} />
+            </div>
+            <div onClick={() => modelChangeFu(2)}>
+              <MinusOutlined rev={undefined} />
+            </div>
+            {full ? (
+              <div onClick={() => setFull(false)}>
+                <FullscreenExitOutlined rev={undefined} />
+              </div>
+            ) : (
+              <div onClick={() => setFull(true)}>
+                <FullscreenOutlined rev={undefined} />
+              </div>
+            )}
+          </div>
+        </div>
+
+        {/* 右边简介 */}
+        <div className="A3IRight">
+          <div className="A3IRtit">
+            <img src={name1Img} alt="" />
+            <div> {info.name}</div>
+
+            <img src={name2Img} alt="" />
+          </div>
+          <div className="A3IRtit2">存放于{info.type1}</div>
+
+          {info.num ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              编号:{info.num}
+            </div>
+          ) : null}
+
+          {info.type2 ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              类别:{info.type2}
+            </div>
+          ) : null}
+
+          {info.age ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              年代:{info.age}
+            </div>
+          ) : null}
+          {info.grain ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              质地:{info.grain}
+            </div>
+          ) : null}
+          {info.size ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              尺寸:{info.size}
+            </div>
+          ) : null}
+          {info.level ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              级别:{info.level}
+            </div>
+          ) : null}
+          {info.state ? (
+            <div className="A3IRtitRow">
+              <div className="A3IRtitRowYuan">
+                <div></div>
+              </div>
+              状态:{info.state}
+            </div>
+          ) : null}
+        </div>
+      </div>
+    </div>
+  );
+}
+
+const MemoGoodsInfo = React.memo(GoodsInfo);
+
+export default MemoGoodsInfo;

+ 7 - 0
src/pages/A3Goods/data.ts

@@ -0,0 +1,7 @@
+export const type1Arr1 = [
+  { id: 0, name: "全部" },
+  { id: 1, name: "董必武纪念馆" },
+  { id: 2, name: "李先念纪念馆" },
+  { id: 3, name: "历史馆" },
+  { id: 4, name: "烈士馆" },
+];

+ 244 - 0
src/pages/A3Goods/index.module.scss

@@ -0,0 +1,244 @@
+.A3Goods {
+  background-size: 100% 100%;
+
+  :global {
+    .A3top {
+      width: 100%;
+      height: 160px;
+      background-size: 100% 100%;
+      position: relative;
+      padding: 18px 40px 0 118px;
+      display: flex;
+      justify-content: space-between;
+
+      .A3Back {
+        width: 48px;
+        cursor: pointer;
+        position: absolute;
+        z-index: 3;
+        top: 20px;
+        left: 40px;
+      }
+
+      .A3leftBox {
+        border-radius: 25px;
+        width: 702px;
+        height: 50px;
+        display: flex;
+        background-color: rgba(255, 255, 255, .9);
+
+        &>div {
+          cursor: pointer;
+          height: 50px;
+          line-height: 48px;
+          padding: 0 30px;
+          font-size: 20px;
+          font-weight: 700;
+          color: var(--themeColor);
+        }
+
+        .active {
+          background-color: #f1cead;
+          border-radius: 30px;
+          border: 1px solid var(--themeColor);
+        }
+      }
+
+      .A3RightBox {
+        width: 630px;
+        display: flex;
+
+        .A3RightBox1 {
+          height: 50px;
+
+          .ant-select {
+            height: 50px;
+
+            .ant-select-selector {
+
+              height: 100%;
+              background-color: rgba(255, 255, 255, .9);
+              border-radius: 25px;
+
+              .ant-select-selection-item {
+                line-height: 48px;
+                font-size: 20px;
+                font-weight: 700;
+                color: var(--themeColor);
+                text-align: center;
+              }
+            }
+
+            .ant-select-arrow {
+              line-height: 48px;
+              font-size: 20px;
+              font-weight: 700;
+              color: var(--themeColor);
+            }
+          }
+        }
+
+        .A3RightBox2 {
+          margin-left: 30px;
+          width: 400px;
+          height: 50px;
+          position: relative;
+
+          input::-webkit-input-placeholder {
+            /* WebKit browsers */
+            color: var(--themeColor);
+            opacity: .8;
+          }
+
+          input:-moz-placeholder {
+            /* Mozilla Firefox 4 to 18 */
+            color: var(--themeColor);
+            opacity: .8;
+          }
+
+          input::-moz-placeholder {
+            /* Mozilla Firefox 19+ */
+            color: var(--themeColor);
+            opacity: .8;
+          }
+
+          input:-ms-input-placeholder {
+            /* Internet Explorer 10+ */
+            color: var(--themeColor);
+            opacity: .8;
+          }
+
+          .ant-input {
+            font-weight: 700;
+            height: 50px;
+            line-height: 48px;
+            font-size: 20px;
+            padding-right: 40px;
+            padding-left: 20px;
+            border-radius: 25px;
+            background-color: rgba(255, 255, 255, .9);
+            color: var(--themeColor);
+          }
+
+          .A3searchIcon {
+            position: absolute;
+            top: 0;
+            right: 0;
+            width: 40px;
+            height: 100%;
+            cursor: pointer;
+            text-align: center;
+            line-height: 48px;
+            font-size: 20px;
+            color: var(--themeColor);
+          }
+
+        }
+      }
+    }
+
+
+    .noRow {
+      width: 100%;
+      height: calc(100% - 160px);
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 40px;
+      color: #fff;
+      padding-bottom: 80px;
+    }
+
+    .A3Main {
+      width: 100%;
+      height: calc(100% - 160px);
+      padding-bottom: 100px;
+      display: flex;
+      flex-wrap: wrap;
+      padding-left: 2%;
+      position: relative;
+
+      .A3Row {
+        width: 23%;
+        height: 48%;
+        margin-right: 2%;
+        margin-bottom: 2%;
+        position: relative;
+        border-radius: 6px;
+        overflow: hidden;
+        background-color: #fff;
+
+        &:nth-of-type(4n) {
+          margin-right: 0;
+        }
+
+        &>img {
+          width: 100%;
+          height: 100%;
+          object-fit: cover;
+        }
+
+        .A3RowHo {
+          opacity: 0;
+          transition: all 0.8s;
+          cursor: pointer;
+          position: absolute;
+          top: 0;
+          left: 0;
+          z-index: 30;
+          width: 100%;
+          height: 100%;
+          background-color: rgba(0, 0, 0, .7);
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          font-size: 18px;
+          color: #fff;
+          padding: 20px;
+          text-align: center;
+
+          &:hover {
+            opacity: 1;
+          }
+        }
+      }
+
+      .A3page {
+        position: absolute;
+        left: 0;
+        bottom: 30px;
+        width: 100%;
+        display: flex;
+        justify-content: center;
+
+        .ant-pagination-item a {
+          color: #fff;
+        }
+
+        .ant-pagination-item-active a {
+          color: var(--themeColor);
+        }
+
+        .ant-pagination-prev button {
+          color: #fff !important;
+        }
+
+        .ant-pagination-next button {
+          color: #fff !important;
+        }
+
+        .ant-pagination-disabled {
+          opacity: .5;
+        }
+
+        .ant-pagination-options-quick-jumper {
+          color: #fff;
+
+          &>input {
+            color: var(--themeColor);
+          }
+        }
+      }
+    }
+  }
+}

+ 211 - 0
src/pages/A3Goods/index.tsx

@@ -0,0 +1,211 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+import backImg from "@/assets/img/back.png";
+import { baseURL } from "@/utils/http";
+import history from "@/utils/history";
+import classNames from "classnames";
+import { Input, Pagination, Select } from "antd";
+import { SearchOutlined } from "@ant-design/icons";
+import ImageLazy from "@/components/ImageLazy";
+import _ from "lodash";
+import GoodsInfo from "./GoodsInfo";
+import { Goods } from "@/types";
+import { type1Arr1 } from "./data";
+
+function A3Goods() {
+  const dataTemp = useSelector(
+    (state: RootState) => state.A0Layout.dataAll.goods
+  );
+
+  const data = useMemo(() => {
+    if (dataTemp) return dataTemp;
+    else return [];
+  }, [dataTemp]);
+
+  // 左侧馆的筛选
+  const [type1, setType1] = useState("全部");
+
+  // 背景图
+  const imgBac = useMemo(() => {
+    const info = type1Arr1.find((v) => v.name === type1);
+    if (info) return `tab${info.id}Bac.jpg`;
+    else return "";
+  }, [type1]);
+
+  // 右侧类别的筛选
+  const [type2, setType2] = useState("全部类别");
+
+  // 右侧类别的下拉框
+  const type2Arr = useMemo(() => {
+    const arr = ["全部类别"];
+    data.forEach((v) => {
+      if (!arr.includes(v.type2)) arr.push(v.type2);
+    });
+    return arr;
+  }, [data]);
+
+  // 输入框
+  const inputRef = useRef<any>(null);
+  const [searchKey, setSearchKey] = useState(-1);
+
+  // 页码
+  const [page, setPage] = useState(1);
+
+  // 在页面渲染的数据
+  const dataRes = useMemo(() => {
+    console.log(searchKey);
+
+    // 所有数据
+    let arr = [...data];
+
+    // 筛选 左侧
+    if (type1 !== "全部") arr = arr.filter((v) => v.type1 === type1);
+
+    // 筛选 右侧
+    if (type2 !== "全部类别") arr = arr.filter((v) => v.type2 === type2);
+
+    if (inputRef.current) {
+      const val = inputRef.current.input.value.trim();
+      arr = arr.filter((v) => v.name.includes(val) || !val);
+    }
+    // 分页(8条一页)
+    const arrs = _.chunk(arr, 8);
+
+    return { arrs, total: arr.length };
+  }, [data, searchKey, type1, type2]);
+
+  // 筛选和输入框变化的时候,page变成1
+  useEffect(() => {
+    setPage(1);
+  }, [type1, type2, searchKey]);
+
+  // 分页器
+  const pageChangeFu = useCallback((pageNum: number) => {
+    setPage(pageNum);
+  }, []);
+
+  // 查看文物详情
+  const [goodsInfo, setGoodsInfo] = useState({} as Goods);
+
+  return (
+    <div
+      className={styles.A3Goods}
+      style={{
+        backgroundImage: `url(${baseURL}/3Goods/pc/${imgBac})`,
+      }}
+    >
+      {/* 顶部 */}
+      <div
+        className="A3top"
+        style={{ backgroundImage: `url(${baseURL}/3Goods/pc/top.png)` }}
+      >
+        <img
+          onClick={() => history.push("/")}
+          className="A3Back"
+          src={backImg}
+          alt=""
+        />
+
+        {/* 左侧筛选 */}
+        <div className="A3leftBox">
+          {type1Arr1.map((v) => (
+            <div
+              onClick={() => setType1(v.name)}
+              className={classNames(v.name === type1 ? "active" : "")}
+              key={v.id}
+            >
+              {v.name}
+            </div>
+          ))}
+        </div>
+
+        {/* 右侧筛选 */}
+        <div className="A3RightBox">
+          <div className="A3RightBox1">
+            <Select
+              style={{ width: 200 }}
+              value={type2}
+              onChange={(e) => setType2(e)}
+              options={type2Arr.map((v) => ({ value: v, label: v }))}
+            />
+          </div>
+          <div
+            className="A3RightBox2"
+            onKeyUp={(e) => {
+              if (e.key === "Enter") setSearchKey(Date.now());
+            }}
+          >
+            <Input ref={inputRef} placeholder="搜索文物" maxLength={15} />
+            {/* 搜索图标 */}
+            <div
+              className="A3searchIcon"
+              onClick={() => setSearchKey(Date.now())}
+            >
+              <SearchOutlined rev={undefined} />
+            </div>
+          </div>
+        </div>
+      </div>
+      {/* 主体 */}
+      {dataRes.arrs &&
+      dataRes.arrs[page - 1] &&
+      dataRes.arrs[page - 1].length ? (
+        <div className="A3Main">
+          {dataRes.arrs[page - 1].map((v) => (
+            <div className="A3Row" key={v.id}>
+              <ImageLazy
+                src={`${baseURL}/3Goods/${v.id}/main.jpg`}
+                width="100%"
+                height="100%"
+                noLook
+                offline
+              />
+              <div
+                className="A3RowHo"
+                title={v.name}
+                onClick={() => setGoodsInfo(v)}
+              >
+                {v.name}
+              </div>
+            </div>
+          ))}
+          {/* 分页器 */}
+          <div className="A3page">
+            <Pagination
+              showQuickJumper
+              current={page}
+              total={dataRes.total}
+              pageSize={10}
+              hideOnSinglePage={true}
+              onChange={pageChangeFu}
+              showSizeChanger={false}
+            />
+          </div>
+        </div>
+      ) : (
+        <div className="noRow">暂无数据</div>
+      )}
+
+      {/* 查看文物详情 */}
+      {goodsInfo.id ? (
+        <GoodsInfo
+          type={type1}
+          info={goodsInfo}
+          colseFu={() => setGoodsInfo({} as Goods)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoA3Goods = React.memo(A3Goods);
+
+export default MemoA3Goods;

+ 125 - 0
src/pages/A4Intro/index.module.scss

@@ -0,0 +1,125 @@
+.A4Intro {
+  position: relative;
+  background-size: 100% 100%;
+
+  :global {
+    .introBack {
+      position: absolute;
+      z-index: 3;
+      top: 20px;
+      left: 30px;
+      cursor: pointer;
+    }
+
+    .introTxt1 {
+      position: absolute;
+      z-index: 3;
+      top: 54px;
+      left: 200px;
+    }
+
+    .txtBox {
+      width: 976px;
+      height: 622px;
+      background-size: 100% 100%;
+      position: absolute;
+      top: 52px;
+      left: 578px;
+      padding: 120px 110px 120px 120px;
+
+      .txtBoxMain {
+        height: 100%;
+        padding-right: 10px;
+        overflow-y: auto;
+        font-size: 16px;
+        letter-spacing: 4px;
+        line-height: 24px;
+      }
+    }
+
+    .introSwBox {
+      position: absolute;
+      width: 1350px;
+      z-index: 3;
+      bottom: 40px;
+      height: 170px;
+      left: 248px;
+      display: flex;
+      align-items: center;
+
+      .introSw1 {
+        width: 262px;
+        height: 80px;
+        margin-right: 18px;
+        background-size: 100% 100%;
+        font-size: 30px;
+        line-height: 80px;
+        text-align: center;
+        font-weight: 700;
+        color: var(--themeColor);
+      }
+
+      .introSw2 {
+        position: relative;
+        width: calc(100% - 280px);
+        height: 120px;
+        padding: 0 60px;
+
+        .swiper {
+          .swiper-slide {
+            border: 3px solid #fff;
+            border-radius: 4px;
+
+            .swImg {
+              cursor: pointer;
+            }
+          }
+
+
+        }
+
+        .swiper-button-prev {
+          position: absolute;
+          left: 4px;
+          width: 41px;
+          height: 81px;
+          background-size: 100% 100%;
+          top: 50%;
+          transform: translateY(-50%);
+          margin-top: 0;
+
+          &::after {
+            opacity: 0;
+          }
+        }
+
+        .swiper-button-next {
+          position: absolute;
+          right: 4px;
+          width: 41px;
+          height: 81px;
+          background-size: 100% 100%;
+          top: 50%;
+          transform: translateY(-50%);
+          margin-top: 0;
+
+          &::after {
+            opacity: 0;
+          }
+        }
+
+        .sw-disable {
+          opacity: .5;
+          pointer-events: none;
+          width: 41px;
+          height: 81px;
+          background-size: 100% 100%;
+          top: 50%;
+          transform: translateY(-50%);
+          margin-top: 0;
+        }
+
+      }
+    }
+  }
+}

+ 111 - 0
src/pages/A4Intro/index.tsx

@@ -0,0 +1,111 @@
+import React, { useMemo } from "react";
+import styles from "./index.module.scss";
+import backImg from "@/assets/img/back.png";
+import history from "@/utils/history";
+import { baseURL } from "@/utils/http";
+import { useSelector } from "react-redux";
+import store, { RootState } from "@/store";
+
+// 轮播图
+import { Swiper, SwiperSlide } from "swiper/react";
+import { Navigation } from "swiper";
+import "swiper/css";
+import "swiper/css/navigation";
+
+function A4Intro() {
+  const data = useSelector((state: RootState) => state.A0Layout.dataAll.Home);
+
+  const dataRes = data.introduce || {};
+
+  const imgArr = useMemo(() => {
+    const num = dataRes.imgNum || 0;
+
+    const dom: number[] = [];
+
+    for (let i = 0; i < num; i++) {
+      dom.push(i);
+    }
+    return dom;
+  }, [dataRes.imgNum]);
+
+  return (
+    <div
+      className={styles.A4Intro}
+      style={{ backgroundImage: `url(${baseURL}/4Intro/pc/bac.jpg)` }}
+    >
+      {/* 返回按钮 */}
+      <div className="introBack" onClick={() => history.push("/")}>
+        <img src={backImg} alt="" />
+      </div>
+
+      <img className="introTxt1" src={`${baseURL}/4Intro/pc/txt1.png`} alt="" />
+
+      <div
+        className="txtBox"
+        style={{ backgroundImage: `url(${baseURL}/4Intro/pc/txtBac.png)` }}
+      >
+        <div
+          className="txtBoxMain mySorrl"
+          dangerouslySetInnerHTML={{ __html: dataRes.txt }}
+        ></div>
+      </div>
+
+      {/* 底部轮播图 */}
+      <div className="introSwBox">
+        <div
+          className="introSw1"
+          style={{ backgroundImage: `url(${baseURL}/4Intro/pc/txt2.png)` }}
+        >
+          场 馆 风 采
+        </div>
+        <div className="introSw2">
+          <Swiper
+            modules={[Navigation]}
+            spaceBetween={30}
+            slidesPerView={4}
+            navigation={{
+              nextEl: ".swiper-button-next",
+              prevEl: ".swiper-button-prev",
+              disabledClass: "sw-disable", // 当导航按钮变为不可用时添加的class,也就是当swiper索引为0时上一张没有prevEl的class类名就会添加一个disable,也就是.swiper-button-prev .disable
+            }}
+            // onSlideChange={() => console.log("slide change")}
+            // onSwiper={(swiper) => console.log(swiper)}
+          >
+            {imgArr.map((v) => (
+              <SwiperSlide key={v}>
+                <img
+                  onClick={() =>
+                    store.dispatch({
+                      type: "layout/lookBigImg",
+                      payload: {
+                        show: true,
+                        url: `${baseURL}/4Intro/pc/${v + 1}.jpg`,
+                      },
+                    })
+                  }
+                  className="swImg"
+                  src={`${baseURL}/4Intro/pc/${v + 1}.jpg`}
+                  alt=""
+                />
+              </SwiperSlide>
+            ))}
+          </Swiper>
+
+          {/* 左右按钮 */}
+          <div
+            className="swiper-button-prev"
+            style={{ backgroundImage: `url(${baseURL}/4Intro/pc/left.png)` }}
+          ></div>
+          <div
+            className="swiper-button-next"
+            style={{ backgroundImage: `url(${baseURL}/4Intro/pc/right.png)` }}
+          ></div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+const MemoA4Intro = React.memo(A4Intro);
+
+export default MemoA4Intro;

+ 15 - 1
src/types/api/layot.d.ts

@@ -4,13 +4,27 @@ export type LookDomType = {
   flag?: boolean;
 };
 
+export type Goods = {
+  id: number;
+  name: string;
+  type1: string;
+  type2: string;
+  num: string;
+  age: string;
+  grain: string;
+  size: string;
+  level: string;
+  state: string;
+  modelNum?:number
+};
+
 export type DataAllType = {
   Home: {
     vr: string;
-    goods: {};
     introduce: {
       txt: string;
       imgNum: number;
     };
   };
+  goods: Goods[];
 };

+ 1 - 4
src/utils/http.ts

@@ -1,7 +1,4 @@
-import store from "@/store";
-
 // 打包配置=》静态资源里面的data.js
 // @ts-ignore
 export const baseURL = baseUrlRes;
-// @ts-ignore
-store.dispatch({ type: "layout/setDataAll", payload: dataAll });
+