shaogen1995 2 år sedan
förälder
incheckning
2a074e41b6

+ 0 - 4
src/App.tsx

@@ -9,7 +9,6 @@ import { Image } from "antd";
 import { useSelector } from "react-redux";
 import { useSelector } from "react-redux";
 import store, { RootState } from "./store";
 import store, { RootState } from "./store";
 import MessageCom from "./components/Message";
 import MessageCom from "./components/Message";
-import LookDom from "./components/LookDom";
 import NotFound from "./components/NotFound";
 import NotFound from "./components/NotFound";
 const A1Home = React.lazy(() => import("./pages/A1Home"));
 const A1Home = React.lazy(() => import("./pages/A1Home"));
 const A2VrPage = React.lazy(() => import("./pages/A2VrPage"));
 const A2VrPage = React.lazy(() => import("./pages/A2VrPage"));
@@ -61,9 +60,6 @@ export default function App() {
         }}
         }}
       />
       />
 
 
-      {/* 查看视频音频模型 */}
-      <LookDom />
-
       {/* antd 轻提示 ---兼容360浏览器 */}
       {/* antd 轻提示 ---兼容360浏览器 */}
       <MessageCom />
       <MessageCom />
     </>
     </>

+ 95 - 0
src/AppM.tsx

@@ -0,0 +1,95 @@
+import "@/assets/styles/base.css";
+// 关于路由
+import React, { useCallback, useEffect, useRef } from "react";
+import { Router, Route, Switch } from "react-router-dom";
+import history from "./utils/history";
+import SpinLoding from "./components/SpinLoding";
+import AsyncSpinLoding from "./components/AsyncSpinLoding";
+import { useSelector } from "react-redux";
+import store, { RootState } from "./store";
+import NotFound from "./components/NotFound";
+import { ImageViewer } from "antd-mobile";
+import screenImg from "@/assets/img/landtip.png";
+
+const A1HomeM = React.lazy(() => import("./pages/A1HomeM"));
+const A2VrPage = React.lazy(() => import("./pages/A2VrPage"));
+const A3GoodsM = React.lazy(() => import("./pages/A3GoodsM"));
+const A4IntroM = React.lazy(() => import("./pages/A4IntroM"));
+
+export default function App() {
+  // 从仓库中获取查看图片的信息
+  const lookBigImg = useSelector(
+    (state: RootState) => state.A0Layout.lookBigImg
+  );
+
+  const setFullFu = useCallback(() => {
+    clearTimeout(time.current);
+    time.current = window.setTimeout(() => {
+      const dom: HTMLDivElement | null = document.querySelector("#root");
+      if (dom) {
+        dom.style.height = document.documentElement.clientHeight + "px";
+      }
+    }, 100);
+  }, []);
+
+  const time = useRef(-1);
+
+  useEffect(() => {
+    const dom: HTMLDivElement | null = document.querySelector("#root");
+    if (dom) {
+      dom.style.width = "100vw";
+      dom.style.minHeight = "auto";
+      dom.style.height = document.documentElement.clientHeight + "px";
+      dom.style.minWidth = "auto";
+    }
+    window.addEventListener("resize", setFullFu, true);
+
+    return () => {
+      window.removeEventListener("resize", setFullFu);
+    };
+  }, [setFullFu]);
+
+  return (
+    <>
+      {/* 关于路由 */}
+      <Router history={history}>
+        <React.Suspense fallback={<SpinLoding />}>
+          <Switch>
+            {/* 首页 */}
+            <Route path="/" exact component={A1HomeM} />
+            {/* 实景vr页面 */}
+            <Route path="/vr" component={A2VrPage} />
+            {/* 文物赏析页面 */}
+            <Route path="/goods" component={A3GoodsM} />
+            {/* 场馆介绍页面 */}
+            <Route path="/intro" component={A4IntroM} />
+            {/* 找不到页面 */}
+            <Route path="*" component={NotFound} />
+          </Switch>
+        </React.Suspense>
+      </Router>
+
+      {/* 发送请求的加载组件 */}
+      <AsyncSpinLoding />
+
+      {/* 所有图片点击预览查看大图 */}
+      <ImageViewer
+        image={lookBigImg.url}
+        visible={lookBigImg.show}
+        onClose={() => {
+          // 清除仓库信息
+          store.dispatch({
+            type: "layout/lookBigImg",
+            payload: { url: "", show: false },
+          });
+        }}
+      />
+
+      {/* 横屏提示 */}
+      <div id="ScreenChange">
+        <img src={screenImg} alt="" />
+        <p>请在竖屏模式浏览</p>
+      </div>
+    </>
+  );
+}

BIN
src/assets/img/landtip.png


BIN
src/assets/img/look.png


BIN
src/assets/img/toTop.png


+ 38 - 0
src/assets/styles/base.css

@@ -92,3 +92,41 @@ textarea {
   border-radius: 10px;
   border-radius: 10px;
   background: transparent;
   background: transparent;
 }
 }
+.AppM {
+  width: 100vw;
+  max-width: 500px;
+  margin: 0 auto;
+  overflow: hidden;
+}
+#ScreenChange {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 10000;
+  background-color: rgba(0, 0, 0, 0.8);
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  opacity: 0;
+  pointer-events: none;
+  transition: all 0.5s;
+}
+#ScreenChange > img {
+  width: 200px;
+}
+#ScreenChange > p {
+  margin-top: 20px;
+  color: #fff;
+  font-size: 18px;
+  height: 40px;
+}
+/*横屏*/
+@media screen and (orientation: landscape) {
+  #ScreenChange {
+    opacity: 1;
+    pointer-events: auto;
+  }
+}

+ 45 - 0
src/assets/styles/base.less

@@ -126,4 +126,49 @@ textarea {
   -webkit-box-shadow: inset 0 0 5px transparent;
   -webkit-box-shadow: inset 0 0 5px transparent;
   border-radius: 10px;
   border-radius: 10px;
   background: transparent;
   background: transparent;
+}
+
+
+.AppM {
+  width: 100vw;
+  max-width: 500px;
+  margin: 0 auto;
+  overflow: hidden;
+}
+
+// 横屏 竖屏的切换
+#ScreenChange {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 10000;
+  background-color: rgba(0, 0, 0, .8);
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  opacity: 0;
+  pointer-events: none;
+  transition: all .5s;
+
+  &>img {
+    width: 200px;
+  }
+
+  &>p {
+    margin-top: 20px;
+    color: #fff;
+    font-size: 18px;
+    height: 40px;
+  }
+}
+
+/*横屏*/
+@media screen and (orientation: landscape) {
+  #ScreenChange {
+    opacity: 1;
+    pointer-events: auto;
+  }
 }
 }

+ 43 - 19
src/index.tsx

@@ -17,6 +17,8 @@ import {
 
 
 import "dayjs/locale/zh-cn";
 import "dayjs/locale/zh-cn";
 import locale from "antd/locale/zh_CN";
 import locale from "antd/locale/zh_CN";
+import { isMobiileFu } from "./utils/history";
+import AppM from "./AppM";
 
 
 const container = document.getElementById("root") as HTMLElement;
 const container = document.getElementById("root") as HTMLElement;
 const root = createRoot(container);
 const root = createRoot(container);
@@ -25,22 +27,44 @@ console.log("静态资源地址:", baseURL);
 // @ts-ignore
 // @ts-ignore
 store.dispatch({ type: "layout/setDataAll", payload: dataAll });
 store.dispatch({ type: "layout/setDataAll", payload: dataAll });
 
 
-root.render(
-  <ConfigProvider
-    locale={locale}
-    theme={{
-      token: {
-        colorPrimary: "#bd130d",
-      },
-    }}
-  >
-    <Provider store={store}>
-      <StyleProvider
-        hashPriority="high"
-        transformers={[legacyLogicalPropertiesTransformer]}
-      >
-        <App />
-      </StyleProvider>
-    </Provider>
-  </ConfigProvider>
-);
+if (isMobiileFu()) {
+  root.render(
+    <ConfigProvider
+      locale={locale}
+      theme={{
+        token: {
+          colorPrimary: "#bd130d",
+        },
+      }}
+    >
+      <Provider store={store}>
+        <StyleProvider
+          hashPriority="high"
+          transformers={[legacyLogicalPropertiesTransformer]}
+        >
+          <AppM />
+        </StyleProvider>
+      </Provider>
+    </ConfigProvider>
+  );
+} else {
+  root.render(
+    <ConfigProvider
+      locale={locale}
+      theme={{
+        token: {
+          colorPrimary: "#bd130d",
+        },
+      }}
+    >
+      <Provider store={store}>
+        <StyleProvider
+          hashPriority="high"
+          transformers={[legacyLogicalPropertiesTransformer]}
+        >
+          <App />
+        </StyleProvider>
+      </Provider>
+    </ConfigProvider>
+  );
+}

+ 50 - 0
src/pages/A1HomeM/index.module.scss

@@ -0,0 +1,50 @@
+.A1HomeM {
+  background-size: 100% 100%;
+  position: relative;
+
+  :global {
+    .nameImg {
+      position: absolute;
+      z-index: 3;
+      top: 10px;
+      left: 10px;
+      width: 40px;
+    }
+
+    .txtImg {
+      position: absolute;
+      z-index: 3;
+      top: 70px;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 94%;
+      margin: 0 auto;
+    }
+
+    .txt2Img {
+      position: absolute;
+      z-index: 3;
+      bottom: 150px;
+      left: 20px;
+      width: 80%;
+    }
+
+    .rightBacBox {
+      position: absolute;
+      left: 0;
+      width: 100%;
+      bottom: 0;
+      height: 150px;
+      background-size: 100% 100%;
+      display: flex;
+      align-items: flex-end;
+      justify-content: space-around;
+      padding-bottom: 10px;
+
+      &>div {
+        width: 80px;
+        height: 80px;
+      }
+    }
+  }
+}

+ 38 - 0
src/pages/A1HomeM/index.tsx

@@ -0,0 +1,38 @@
+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 A1HomeM() {
+  return (
+    <div
+      className={styles.A1HomeM}
+      style={{ backgroundImage: `url(${baseURL}/Home/mobile/bac.jpg)` }}
+    >
+      <img className="nameImg" src={`${baseURL}/Home/pc/name.png`} alt="" />
+      <img className="txtImg" src={`${baseURL}/Home/mobile/txt.png`} alt="" />
+      <img className="txt2Img" src={`${baseURL}/Home/mobile/txt2.png`} alt="" />
+
+      <div
+        className="rightBacBox"
+        style={{ backgroundImage: `url(${baseURL}/Home/mobile/rightBac.png)` }}
+      >
+        {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>
+  );
+}
+
+const MemoA1HomeM = React.memo(A1HomeM);
+
+export default MemoA1HomeM;

+ 173 - 0
src/pages/A3GoodsM/GoodsInfoM/index.module.scss

@@ -0,0 +1,173 @@
+.GoodsInfoM {
+  position: absolute;
+  z-index: 99;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-size: 100% 100%;
+  padding: 56px 15px 15px 15px;
+  overflow: hidden;
+
+  :global {
+
+    .GoodsInfoMmian {
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+
+      &::-webkit-scrollbar {
+        width: 2px;
+      }
+
+    }
+
+    .A3IBack {
+      position: absolute;
+      z-index: 30;
+      top: 15px;
+      left: 15px;
+      width: 40px;
+    }
+
+    .A3IRtit {
+      display: flex;
+      position: relative;
+
+      &>img {
+        width: 100px;
+        position: absolute;
+        top: -10px;
+        left: 0;
+      }
+
+      .A3IRtitimg {
+        left: auto;
+        right: 0px;
+      }
+
+      &>div {
+        color: #FBF1AD;
+        font-size: 20px;
+        font-weight: 700;
+        width: 100%;
+        text-align: center;
+        letter-spacing: 2px;
+        padding: 0 110px;
+      }
+    }
+
+    .A3IRtit2 {
+      color: #FBF1AD;
+      font-size: 18px;
+      text-align: center;
+      letter-spacing: 2px;
+      margin-top: 5px;
+      margin-bottom: 20px;
+    }
+
+    .ifrBox {
+      width: calc(100% - 10px);
+      height: 300px;
+      padding: 0 30px;
+      position: relative;
+
+      .ifrBoxson {
+        width: 100%;
+        height: 100%;
+        background-color: #8e9596;
+        position: relative;
+
+        iframe {
+          width: 100%;
+          height: 100%;
+        }
+
+        .A3IleftBtn {
+          position: absolute;
+          bottom: 10px;
+          right: 10px;
+          z-index: 10;
+          display: flex;
+
+          &>div {
+            width: 30px;
+            height: 30px;
+            border-radius: 50%;
+            background-color: rgba(0, 0, 0, .6);
+            margin-left: 10px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            color: #fff;
+            font-size: 20px;
+            cursor: pointer;
+          }
+        }
+      }
+
+      .A3IleftFull {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        z-index: 40;
+      }
+
+      .A3ItoBtn {
+        cursor: pointer;
+        position: absolute;
+        z-index: 10;
+        top: 50%;
+        transform: translateY(-50%);
+        left: 0px;
+        width: 30px;
+        height: 60px;
+        background-size: 100% 100%;
+
+      }
+
+      .A3ItoRight {
+        left: auto;
+        right: 0px;
+      }
+
+      .noneToBtn {
+        opacity: .5;
+        pointer-events: none;
+      }
+    }
+
+    .A3IRtitRow {
+      padding: 0 20px 0 50px;
+      position: relative;
+      margin-top: 15px;
+      font-size: 16px;
+      color: var(--themeColor);
+      letter-spacing: 2px;
+
+      .A3IRtitRowYuan {
+        position: absolute;
+        left: 30px;
+        top: 50%;
+        transform: translateY(-50%);
+        width: 14px;
+        height: 14px;
+        border-radius: 50%;
+        border: 1px solid var(--themeColor);
+
+        &>div {
+          position: relative;
+          top: 2px;
+          left: 2px;
+          width: 8px;
+          height: 8px;
+          background-color: var(--themeColor);
+          border-radius: 50%;
+        }
+      }
+    }
+
+  }
+}

+ 209 - 0
src/pages/A3GoodsM/GoodsInfoM/index.tsx

@@ -0,0 +1,209 @@
+/* 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 { type1Arr1M } from "../data";
+import { baseURL } from "@/utils/http";
+import backImg from "@/assets/img/back.png";
+import name1Img from "@/assets/img/name1.png";
+import name2Img from "@/assets/img/name2.png";
+import classNames from "classnames";
+import {
+  PlusOutlined,
+  MinusOutlined,
+  FullscreenOutlined,
+  FullscreenExitOutlined,
+} from "@ant-design/icons";
+
+type Props = {
+  info: Goods;
+  colseFu: () => void;
+  type: string;
+};
+
+function GoodsInfoM({ info, colseFu, type }: Props) {
+  // 背景图
+  const imgBac = useMemo(() => {
+    const info = type1Arr1M.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.GoodsInfoM}
+      style={{
+        backgroundImage: `url(${baseURL}/3Goods/mobile/${imgBac})`,
+      }}
+    >
+      <div className="GoodsInfoMmian mySorrl">
+        {/* 返回  */}
+        <img onClick={colseFu} className="A3IBack" src={backImg} alt="" />
+
+        {/* 标题 */}
+        <div className="A3IRtit">
+          <img src={name1Img} alt="" />
+          <div> {info.name}</div>
+
+          <img className="A3IRtitimg" src={name2Img} alt="" />
+        </div>
+        <div className="A3IRtit2">存放于{info.type1}</div>
+
+        {/* 模型盒子 */}
+
+        <div className="ifrBox">
+          <div
+            className={classNames("ifrBoxson", 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 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
+            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>
+
+        {/* 下面简介 */}
+        {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>
+  );
+}
+
+const MemoGoodsInfoM = React.memo(GoodsInfoM);
+
+export default MemoGoodsInfoM;

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

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

+ 178 - 0
src/pages/A3GoodsM/index.module.scss

@@ -0,0 +1,178 @@
+.A3GoodsM {
+  background-size: 100% 100%;
+
+  :global {
+    .A3Gtop {
+      width: 100%;
+      height: 130px;
+      background-size: 100% 100%;
+      padding: 10px 15px;
+      position: relative;
+
+      .A3Back {
+        position: absolute;
+        z-index: 10;
+        left: 10px;
+        top: 10px;
+        width: 34px;
+      }
+
+      .A3RightBox2 {
+        padding-left: 50px;
+        width: 100%;
+        height: 32px;
+        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: 32px;
+          line-height: 30px;
+          font-size: 16px;
+          padding-right: 40px;
+          padding-left: 20px;
+          border-radius: 18px;
+          background-color: rgba(255, 255, 255, .8);
+          color: var(--themeColor);
+        }
+
+        .A3searchIcon {
+          position: absolute;
+          top: 0;
+          right: 0;
+          width: 40px;
+          height: 100%;
+          text-align: center;
+          line-height: 30px;
+          font-size: 20px;
+          color: var(--themeColor);
+        }
+
+      }
+
+      .A3Gtop1 {
+        margin-top: 10px;
+        display: flex;
+        justify-content: space-between;
+        height: 32px;
+
+        .A3Gtop1_1 {
+          width: 48%;
+          height: 32px;
+
+          .ant-select {
+            height: 32px;
+
+            .ant-select-selector {
+
+              height: 100%;
+              background-color: rgba(255, 255, 255, .8);
+              border-radius: 18px;
+
+              .ant-select-selection-item {
+                line-height: 30px;
+                font-size: 16px;
+                font-weight: 700;
+                color: var(--themeColor);
+                text-align: center;
+              }
+            }
+
+            .ant-select-arrow {
+              line-height: 30px;
+              font-size: 16px;
+              font-weight: 700;
+              color: var(--themeColor);
+            }
+          }
+        }
+      }
+
+    }
+
+    .noRow{
+      width: 100%;
+      height: calc(100% - 130px);
+      padding-bottom: 200px;
+      color: #fff;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 20px;
+      letter-spacing: 4px;
+    }
+
+    .A3Main {
+      width: 100%;
+      height: calc(100% - 130px);
+      overflow-y: auto;
+      display: flex;
+      flex-wrap: wrap;
+      padding: 0 15px 15px;
+
+      .A3Row {
+        width: 48%;
+        height: 200px;
+        background-color: #fff;
+        border-radius: 6px;
+        margin-right: 4%;
+        margin-bottom: 4%;
+        padding: 8px;
+
+        &:nth-of-type(2n) {
+          margin-right: 0;
+        }
+
+        .A3RowHo {
+          margin-top: 6px;
+          font-weight: 700;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+
+        .lookImg {
+          text-align: right;
+
+          &>img {
+            width: 60px;
+          }
+        }
+      }
+    }
+
+    .A3ToTop{
+      position: fixed;
+      z-index: 10;
+      bottom: 20px;
+      right: 10px;
+      width: 40px;
+      &>img{
+        width: 100%;
+      }
+    }
+  }
+}

+ 224 - 0
src/pages/A3GoodsM/index.tsx

@@ -0,0 +1,224 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import { baseURL } from "@/utils/http";
+import { type1Arr1M } from "./data";
+import { Input, Select } from "antd";
+import { SearchOutlined } from "@ant-design/icons";
+import backImg from "@/assets/img/back.png";
+import history from "@/utils/history";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store";
+import ImageLazy from "@/components/ImageLazy";
+import { Goods } from "@/types";
+// import lookImg from "@/assets/img/look.png";
+import toTopImg from "@/assets/img/toTop.png";
+import GoodsInfoM from "./GoodsInfoM";
+
+function A3GoodsM() {
+  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 = type1Arr1M.find((v) => v.name === type1);
+    if (info) return `tab${info.id}Bac.jpg`;
+    else return "";
+  }, [type1]);
+
+  // 输入框
+  const inputRef = useRef<any>(null);
+  const [searchKey, setSearchKey] = useState(-1);
+
+  // 右侧类别的筛选
+  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 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);
+    }
+
+    return arr;
+  }, [data, searchKey, type1, type2]);
+
+  // 每次变化的时候 滚动到顶部
+  const fullTopFu = useCallback(() => {
+    if (scrollRef.current) {
+      scrollRef.current.scrollTop = 0;
+    }
+  }, []);
+
+  useEffect(() => {
+    fullTopFu();
+  }, [fullTopFu, searchKey, type1, type2]);
+
+  // 查看文物详情
+  const [goodsInfo, setGoodsInfo] = useState({} as Goods);
+
+  // 手机打开输入框的时候 下面主体变形
+  useEffect(() => {
+    const num = document.documentElement.clientHeight;
+    const dom: HTMLDivElement | null = document.querySelector("#A3GoodsM");
+    if (dom) dom.style.height = num + "px";
+  }, []);
+
+  // 滚动距离
+
+  const [scrollShow, setScrollShow] = useState(false);
+
+  const scrollRef = useRef<HTMLDivElement>(null);
+
+  const scrollFu = useCallback(() => {
+    if (scrollRef.current) {
+      const num = scrollRef.current.scrollTop;
+      setScrollShow(num >= 400);
+    }
+  }, []);
+
+  useEffect(() => {
+    window.addEventListener("scroll", scrollFu, true);
+
+    return () => {
+      window.removeEventListener("scroll", scrollFu);
+    };
+  }, [scrollFu]);
+
+  return (
+    <div
+      id="A3GoodsM"
+      className={styles.A3GoodsM}
+      style={{
+        backgroundImage: `url(${baseURL}/3Goods/mobile/${imgBac})`,
+      }}
+    >
+      <div
+        className="A3Gtop"
+        style={{ backgroundImage: `url(${baseURL}/3Goods/mobile/top.png)` }}
+      >
+        <img
+          onClick={() => history.push("/")}
+          className="A3Back"
+          src={backImg}
+          alt=""
+        />
+
+        {/* 输入框 */}
+        <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 className="A3Gtop1">
+          <div className="A3Gtop1_1">
+            <Select
+              style={{ width: "100%" }}
+              value={type1}
+              onChange={(e) => setType1(e)}
+              options={type1Arr1M.map((v) => ({
+                value: v.name,
+                label: v.name,
+              }))}
+            />
+          </div>
+          <div className="A3Gtop1_1">
+            <Select
+              style={{ width: "100%" }}
+              value={type2}
+              onChange={(e) => setType2(e)}
+              options={type2Arr.map((v) => ({ value: v, label: v }))}
+            />
+          </div>
+        </div>
+      </div>
+
+      {/* 主体 */}
+      {dataRes && dataRes.length ? (
+        <div className="A3Main" ref={scrollRef}>
+          {dataRes.map((v) => (
+            <div className="A3Row" key={v.id} onClick={() => setGoodsInfo(v)}>
+              <ImageLazy
+                src={`${baseURL}/3Goods/${v.id}/main.jpg`}
+                width="100%"
+                height={160}
+                noLook
+                offline
+              />
+              <div className="A3RowHo">{v.name}</div>
+              {/* 查看的小图片 */}
+              {/* <div className="lookImg">
+                <img src={lookImg} alt="" />
+              </div> */}
+            </div>
+          ))}
+        </div>
+      ) : (
+        <div className="noRow">暂无数据</div>
+      )}
+
+      {/* 返回顶部 */}
+      <div className="A3ToTop" hidden={!scrollShow} onClick={fullTopFu}>
+        <img src={toTopImg} alt="" />
+      </div>
+
+      {/* 查看文物详情 */}
+      {goodsInfo.id ? (
+        <GoodsInfoM
+          type={type1}
+          info={goodsInfo}
+          colseFu={() => setGoodsInfo({} as Goods)}
+        />
+      ) : null}
+    </div>
+  );
+}
+
+const MemoA3GoodsM = React.memo(A3GoodsM);
+
+export default MemoA3GoodsM;

+ 113 - 0
src/pages/A4IntroM/index.module.scss

@@ -0,0 +1,113 @@
+.A4IntroM{
+  background-size: 100% 100%;
+  padding: 20px;
+  :global{
+    .introBack{
+      width: 40px;
+    }
+    .txtBox{
+      width: 100%;
+      height: calc(100% - 220px);
+      background-size: 100% 100%;
+      padding: 30% 12% 15% 15%;
+      position: relative;
+      .txtBoxTit{
+        position: absolute;
+        width: 100%;
+        top: 30px;
+        left: 0;
+        text-align: center;
+        &>img{
+          width: 160px;
+        }
+      }
+      .txtBoxMain{
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        padding-right: 20px;
+        letter-spacing: 2px;
+        padding-bottom: 10px;
+        &::-webkit-scrollbar{
+          width: 2px;
+        }
+      }
+    }
+    .introSw1{
+      margin: 10px auto 8px;
+      width: 160px;
+      height: 40px;
+      background-size: 100% 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 18px;
+      font-weight: 700;
+      color: var(--themeColor);
+    }
+    .introSw2{
+      width: 100%;
+      height: 130px;
+      padding: 0 60px;
+      position: relative;
+      .swiper {
+        height: 100%;
+        .swiper-slide {
+          border: 3px solid #fff;
+          border-radius: 4px;
+
+          .swImg {
+            width: 100%;
+            height: 100%;
+          }
+        }
+
+
+      }
+      .swiper-button-prev {
+        position: absolute;
+        outline: none;
+        cursor: default;
+        left: 4px;
+        width: 30px;
+        height: 60px;
+        background-size: 100% 100%;
+        top: 50%;
+        transform: translateY(-50%);
+        margin-top: 0;
+
+        &::after {
+          opacity: 0;
+        }
+      }
+
+      .swiper-button-next {
+        position: absolute;
+        outline: none;
+        cursor: default;
+        right: 4px;
+        width: 30px;
+        height: 60px;
+        background-size: 100% 100%;
+        top: 50%;
+        transform: translateY(-50%);
+        margin-top: 0;
+
+        &::after {
+          opacity: 0;
+        }
+      }
+
+      .sw-disable {
+        opacity: .5;
+        pointer-events: none;
+        width: 30px;
+        height: 60px;
+        background-size: 100% 100%;
+        top: 50%;
+        transform: translateY(-50%);
+        margin-top: 0;
+      }
+    }
+  }
+}

+ 114 - 0
src/pages/A4IntroM/index.tsx

@@ -0,0 +1,114 @@
+import React, { useMemo } from "react";
+import styles from "./index.module.scss";
+import { useSelector } from "react-redux";
+import store, { RootState } from "@/store";
+import { baseURL } from "@/utils/http";
+import backImg from "@/assets/img/back.png";
+import history from "@/utils/history";
+
+// 轮播图
+import { Swiper, SwiperSlide } from "swiper/react";
+import { Navigation } from "swiper";
+import "swiper/css";
+import "swiper/css/navigation";
+
+function A4IntroM() {
+  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.A4IntroM}
+      style={{ backgroundImage: `url(${baseURL}/4Intro/mobile/bac.jpg)` }}
+    >
+      {/* 返回按钮 */}
+      <div className="introBack" onClick={() => history.push("/")}>
+        <img src={backImg} alt="" />
+      </div>
+
+      {/* 文字主体 */}
+      <div
+        className="txtBox"
+        style={{ backgroundImage: `url(${baseURL}/4Intro/mobile/txtBac.png)` }}
+      >
+        {/* 场馆介绍 */}
+        <div className="txtBoxTit">
+          <img src={`${baseURL}/4Intro/mobile/txt1.png`} alt="" />
+        </div>
+
+        <div
+          className="txtBoxMain mySorrl"
+          dangerouslySetInnerHTML={{ __html: dataRes.txt }}
+        ></div>
+      </div>
+
+      {/* 轮播图 */}
+      <div
+        className="introSw1"
+        style={{ backgroundImage: `url(${baseURL}/4Intro/pc/txt2.png)` }}
+      >
+        场 馆 风 采
+      </div>
+
+      <div className="introSw2">
+        <Swiper
+          modules={[Navigation]}
+          spaceBetween={0}
+          slidesPerView={1}
+          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>
+  );
+}
+
+const MemoA4IntroM = React.memo(A4IntroM);
+
+export default MemoA4IntroM;

+ 10 - 1
src/utils/history.ts

@@ -14,4 +14,13 @@ export const urlParameter = (data: string) => {
     });
     });
     return params;
     return params;
   } else return {};
   } else return {};
-};
+};
+
+// 判断是手机端还是pc端
+export const isMobiileFu = () => {
+  if (window.navigator.userAgent.match(
+    /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+  )) {
+    return true
+  } else return false
+}