lanxin 1 месяц назад
Родитель
Сommit
f665f1fd93

BIN
public/myData/img/devingBG.png


Разница между файлами не показана из-за своего большого размера
+ 1550 - 0
public/myData/myDataEN.js


+ 25 - 2
src/App.tsx

@@ -4,11 +4,12 @@ import React, { useCallback, useEffect, useRef } from 'react'
 import { Router, Route, Switch } from 'react-router-dom'
 import history, { callIframeFu } from './utils/history'
 import SpinLoding from './components/SpinLoding'
-
+import store, { RootState } from './store'
+import { useSelector } from 'react-redux'
 import NotFound from '@/components/NotFound'
-import store from './store'
 import { baseURL, isLoc, myData } from './utils/http'
 import AsyncSpinLoding from './components/AsyncSpinLoding'
+import { Image } from 'antd'
 
 // import Vconsole from 'vconsole'
 // new Vconsole()
@@ -36,6 +37,8 @@ declare global {
   }
 }
 
+
+
 let tempW = document.documentElement.clientWidth
 let tempH = document.documentElement.clientHeight
 
@@ -60,6 +63,9 @@ export default function App() {
     }
   }, [])
 
+  const lookBigImg = useSelector((state: RootState) => state.A0Layout.lookBigImg)
+
+
   // 根元素
   const rootRef = useRef<any>(null)
 
@@ -222,6 +228,23 @@ export default function App() {
       {/* 发送请求的加载组件 */}
       <AsyncSpinLoding />
 
+      {/* 所有图片点击预览查看大图 */}
+      {lookBigImg.show ? (
+        <Image
+          preview={{
+            visible: lookBigImg.show,
+            src: lookBigImg.url,
+            onVisibleChange: value => {
+              // 清除仓库信息
+              store.dispatch({
+                type: 'layout/lookBigImg',
+                payload: { url: '', show: false }
+              })
+            }
+          }}
+        />
+      ) : null}
+
     </>
   )
 }

Разница между файлами не показана из-за своего большого размера
+ 330 - 338
src/assets/img/Graph.svg


BIN
src/assets/img/devingBG.png


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

@@ -217,6 +217,13 @@ textarea {
   .adm-toast-wrap {
     transform: rotate(90deg) !important;
   }
+  .ant-image-preview-wrap {
+    transform: rotate(90deg) !important;
+  }
+  .ant-image-preview-footer {
+    transform: scale(0.7) rotate(90deg) translate(-178%, -50%);
+    transform-origin: left bottom;
+  }
   #root .ant-tooltip {
     transform: rotate(0) !important;
     inset: 24.331px auto auto 399.25px !important;

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

@@ -281,6 +281,17 @@ textarea {
     transform: rotate(90deg) !important;
   }
 
+  .ant-image-preview-wrap {
+    transform: rotate(90deg) !important;
+  }
+
+  .ant-image-preview-footer {
+    transform: scale(0.7) rotate(90deg) translate(-178%, -50%);
+    transform-origin: left bottom;
+  }
+
+
+
   #root .ant-tooltip {
     transform: rotate(0) !important;
     inset: 24.331px auto auto 399.25px !important;

+ 34 - 0
src/components/IsDev/index.module.scss

@@ -0,0 +1,34 @@
+.isDev {
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  z-index: 100;
+  top: 0;
+  left: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: url('../../assets/img/devingBG.png') no-repeat center center;
+  background-size: 100% 100%;
+  :global {
+    .back {
+      width: 60px;
+      height: 30px;
+      position: absolute;
+      z-index: 3;
+      top: 3%;
+      left: 4%;
+      cursor: pointer;
+
+      & > img {
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+    .deving {
+      font-size: 24px;
+      font-weight: 500;
+      color: rgba(93, 96, 96, 1);
+    }
+  }
+}

+ 17 - 0
src/components/IsDev/index.tsx

@@ -0,0 +1,17 @@
+import React from "react";
+import styles from "./index.module.scss";
+function IsDev({ closeFn }: { closeFn: () => void }) {
+
+  return (
+    <div className={styles.isDev}>
+      <div className='deving'>加急开发中!!</div>
+      <div className='back' onClick={() => closeFn()}>
+        <img src={require('@/assets/img/btn_back.png')} alt='' />
+      </div>
+    </div>
+  )
+}
+
+const MemoIsDev = React.memo(IsDev);
+
+export default MemoIsDev;

+ 0 - 214
src/components/ZHotOld/index.module.scss

@@ -1,214 +0,0 @@
-.Hot2 {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  z-index: 20;
-  transition: all 0.3s;
-  display: flex;
-  background-color: rgba(0, 0, 0, 0.6);
-  backdrop-filter: blur(2px);
-
-  :global {
-    .h2Main {
-      width: 50%;
-      height: 100%;
-      background-size: 100% 100%;
-      padding: 3% 3% 1% 3%;
-      // 标题
-      .h2Titele {
-        padding: 0 50px;
-        position: relative;
-        display: inline-block;
-        color: #eacf60;
-        font-size: 30px;
-        & > img {
-          position: absolute;
-          top: 60%;
-          left: 0;
-          transform: translateY(-50%);
-          width: 40px;
-          height: auto;
-        }
-        .h2TimgR {
-          left: auto;
-          right: 0;
-        }
-      }
-      // 图文
-      .h2TuWen {
-        margin: 10px 0;
-        width: 100%;
-        height: calc(100% - 150px);
-        overflow-y: auto;
-
-        border-bottom: 1px solid transparent;
-
-        &::-webkit-scrollbar {
-          display: none;
-        }
-
-        .H2model {
-          width: 100%;
-          height: 200px;
-          .H2modelSon {
-            iframe {
-              width: 100%;
-              height: 100%;
-            }
-          }
-
-          video {
-            object-fit: cover;
-            width: 100%;
-            height: 100%;
-          }
-        }
-
-        .adm-image {
-          width: 90%;
-          margin: 0 auto;
-          height: auto;
-          min-height: 80px;
-          margin-bottom: 10px;
-          .adm-image-tip {
-            min-height: 80px;
-          }
-          img {
-            width: 100%;
-            height: 100%;
-            object-fit: contain !important;
-            max-height: 180px;
-          }
-        }
-        .h2txt {
-          font-size: 14px;
-          line-height: 24px;
-          color: #fffddc;
-          p {
-            font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB',
-              'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
-            margin-bottom: 10px;
-          }
-          h3 {
-            color: #eacf60;
-            font-family: 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB',
-              'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif !important;
-            margin-bottom: 8px;
-          }
-        }
-      }
-      .h2TuWenDuo {
-        height: calc(100% - 115px);
-        margin-bottom: 15px;
-      }
-
-      // 底部
-      .h2FlooBox {
-        width: 100%;
-        height: 40px;
-        display: flex;
-        justify-content: center;
-        margin-bottom: 10px;
-        .h2Floo {
-          min-width: 200px;
-          text-align: center;
-          max-width: 80%;
-          height: 100%;
-          overflow-x: auto;
-          white-space: nowrap;
-          display: inline-block;
-          border-radius: 4px;
-          background: linear-gradient(
-            to right,
-            rgba(0, 0, 0, 0.2),
-            rgba(0, 0, 0, 0.5),
-            rgba(0, 0, 0, 0.2)
-          );
-          &::-webkit-scrollbar {
-            display: none;
-          }
-          .h2FlooRow {
-            padding: 0 10px;
-            display: inline-block;
-            cursor: pointer;
-            font-size: 16px;
-            color: #fffddc;
-            opacity: 0.8;
-            line-height: 38px;
-          }
-          .h2FlooRowShow {
-            font-weight: 700;
-            opacity: 1;
-            color: #eacf60;
-          }
-        }
-      }
-
-      // 返回按钮
-      #BtnRight {
-        position: relative;
-        width: 36px;
-        height: 36px;
-        left: 50%;
-        transform: translateX(-50%);
-        bottom: 0px;
-      }
-    }
-    .h2Right {
-      width: 50%;
-      height: 100%;
-    }
-
-    // 屏幕>=1200
-    @media screen and (min-width: 1200px) {
-      .h2Main {
-        width: 40%;
-        .h2Titele {
-          font-size: 22px;
-        }
-        .h2TuWen {
-          .h2txt {
-            h3 {
-              font-size: 12px;
-            }
-            p {
-              font-size: 10px;
-              line-height: 20px;
-              margin-bottom: 5px;
-            }
-          }
-          // .adm-image {
-          //   max-width: 60%;
-          // }
-        }
-        .h2FlooBox {
-          height: 30px;
-          .h2Floo {
-            max-width: 100%;
-            min-width: 100px;
-
-            .h2FlooRow {
-              line-height: 28px;
-              font-size: 10px;
-              padding: 0 8px;
-            }
-          }
-        }
-      }
-      .h2Right {
-        width: 60%;
-      }
-    }
-  }
-}
-
-.Hot2Ji {
-  background: linear-gradient(
-    to right,
-    rgba(43, 16, 16, 1),
-    rgba(43, 16, 16, 0.9),
-    rgba(43, 16, 16, 0.2)
-  );
-}

+ 0 - 185
src/components/ZHotOld/index.tsx

@@ -1,185 +0,0 @@
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import styles from './index.module.scss'
-import { VisitHotDataType } from '@/types'
-import classNames from 'classnames'
-import LazyImg from '@/components/LazyImg'
-import { baseURL, isPc } from '@/utils/http'
-import BtnRight from '@/components/BtnRight'
-import { RootState } from '@/store'
-import { useSelector } from 'react-redux'
-
-type Props = {
-  closeFu: () => void
-  data: VisitHotDataType
-  name: string
-  isJi?: boolean
-}
-
-function Hot2({ closeFu, data, name, isJi }: Props) {
-  // 底部选中
-  const [flooInd, setFlooInd] = useState(0)
-
-  const acData = useMemo(() => {
-    return data[flooInd] || []
-  }, [data, flooInd])
-
-  // 点击切换底部
-  const flooIndFu = useCallback((ind: number) => {
-    const dom = document.querySelector('.h2TuWen')
-    if (dom) dom.scrollTop = 0
-    setFlooInd(ind)
-  }, [])
-
-  const videoRef = useRef<HTMLVideoElement>(null)
-
-  useEffect(() => {
-    if (acData.type && acData.type === 'video') {
-      setTimeout(() => {
-        if (videoRef.current) videoRef.current.play()
-      }, 100)
-    }
-  }, [acData.type])
-
-  const rootStyle = useSelector((state: RootState) => state.A0Layout.style)
-
-  const [modelSize, setModelSize] = useState({ ww: 0, hh: 0, wwB: 0, hhB: 0 })
-
-  useEffect(() => {
-    const dom = document.querySelector('.H2model')
-    if (dom) {
-      // console.log(
-      //   'cccc',
-      //   dom.clientWidth,
-      //   dom.clientHeight,
-      //   Number((dom.clientWidth * rootStyle.sizeW).toFixed(2)),
-      //   Number((dom.clientHeight * rootStyle.sizeH).toFixed(2))
-      // )
-
-      setModelSize({
-        ww: Number((dom.clientWidth * rootStyle.sizeW).toFixed(2)),
-        hh: Number((dom.clientHeight * rootStyle.sizeH).toFixed(2)),
-        wwB: dom.clientWidth,
-        hhB: dom.clientHeight
-      })
-    }
-  }, [rootStyle.sizeH, rootStyle.sizeW])
-
-  const [isFlag, setIsFlag] = useState(false)
-
-  const xianRef = useRef<HTMLDivElement>(null)
-  const mousemoveFu = useCallback(
-    (ev: any, flag?: boolean) => {
-      if (xianRef.current) {
-        if (flag && !isFlag) {
-          const nowMove = xianRef.current.scrollLeft
-          // 滚轮
-          let num = 50
-          if (ev.deltaY < 0) num = -num
-          xianRef.current.scrollLeft = nowMove + num
-        } else if (isFlag) {
-          const nowMove = xianRef.current.scrollLeft
-
-          // 鼠标按住移动
-          xianRef.current.scrollLeft = nowMove - ev.movementX
-        }
-      }
-    },
-    [isFlag]
-  )
-
-  return (
-    <div id='HotOpCss' className={classNames(styles.Hot2, isJi ? styles.Hot2Ji : '')} style={{}}>
-      <div className='h2Main' style={{ backgroundImage: `url(${baseURL}visit/hot2bj.png)` }}>
-        {/* 标题 */}
-        <div className='h2Titele'>
-          <img src={`${baseURL}visit/img-yun1.png`} alt='' />
-          <img className='h2TimgR' src={`${baseURL}visit/img-yun2.png`} alt='' />
-          {name}
-        </div>
-
-        <div className={classNames('h2TuWen', data.length <= 1 ? 'h2TuWenDuo' : '')}>
-          {/* 图片 */}
-          {acData.type ? (
-            <div className='H2model' style={{ height: acData.txt ? '200px' : '280px' }}>
-              {acData.type === 'model' ? (
-                <div
-                  className='H2modelSon'
-                  style={
-                    isPc && Object.keys(rootStyle).length && modelSize.ww
-                      ? {
-                          position: 'relative',
-                          width: modelSize.ww + 'px',
-                          height: modelSize.hh + 'px',
-                          transform: `translate(${-(modelSize.ww - modelSize.wwB) / 2}px, ${
-                            -(modelSize.hh - modelSize.hhB) / 2
-                          }px) scale(${1 / rootStyle.sizeW}, ${1 / rootStyle.sizeH})`
-                        }
-                      : { width: '100%', height: '100%' }
-                  }
-                >
-                  <iframe
-                    title={name}
-                    src={`${baseURL}modelLoding/model.html?u=${acData.imgArr[0]}`}
-                    frameBorder='0'
-                  ></iframe>
-                </div>
-              ) : (
-                <video
-                  ref={videoRef}
-                  src={baseURL + acData.imgArr[0]}
-                  controls
-                  playsInline
-                  muted
-                  webkit-playsinline='true'
-                  x5-video-player-type='h5'
-                ></video>
-              )}
-            </div>
-          ) : (
-            acData.imgArr.map((url, index) => <LazyImg src={baseURL + url} key={index} />)
-          )}
-
-          {/* 文字 */}
-          <div className='h2txt' dangerouslySetInnerHTML={{ __html: acData.txt }}></div>
-        </div>
-
-        {/* 底部 */}
-        {data.length <= 1 ? null : (
-          <div className='h2FlooBox'>
-            <div
-              className='h2Floo'
-              style={{ cursor: isFlag ? 'move' : 'default' }}
-              ref={xianRef}
-              onMouseDown={() => setIsFlag(true)}
-              onMouseUp={() => setIsFlag(false)}
-              onMouseLeave={() => setIsFlag(false)}
-              onMouseMove={e => mousemoveFu(e)}
-              onWheel={e => mousemoveFu(e, true)}
-            >
-              {data.map((item, index) => (
-                <div
-                  onClick={() => flooIndFu(index)}
-                  className={classNames(
-                    'h2FlooRow sizeNo',
-                    flooInd === index ? 'h2FlooRowShow' : ''
-                  )}
-                  key={index}
-                >
-                  {item.name}
-                </div>
-              ))}
-            </div>
-          </div>
-        )}
-
-        {/* 返回按钮 */}
-        <BtnRight imgName='back' clickSon={() => closeFu()} title='返回' />
-      </div>
-      <div className='h2Right' onClick={closeFu}></div>
-    </div>
-  )
-}
-
-const MemoHot2 = React.memo(Hot2)
-
-export default MemoHot2

+ 38 - 0
src/i18n.ts

@@ -0,0 +1,38 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+
+// 异步加载语言包(核心)
+const loadLocale = async ({locale}:{locale:string}) => {
+  let messages;
+  if (locale === 'zh-CN') {
+    // 动态加载 public/data.js
+    const module = await import('./utils/http');
+    messages = module.myData;
+  } else if (locale === 'en-EN') {
+    const module = await import('./utils/http');
+    messages = module.myData;
+  }
+  // 注入到 i18n
+  i18n.addResourceBundle(locale, 'translation', messages);
+  // 切换语言
+  i18n.changeLanguage(locale);
+  return messages;
+};
+
+// 初始化加载默认语言
+loadLocale({ locale: 'zh-CN' });
+
+i18n
+  .use(initReactI18next)
+  .init({
+    resources: {},
+    lng: 'zh-CN', // 默认中文
+    fallbackLng: 'zh-CN',
+    interpolation: {
+      escapeValue: false
+    }
+  });
+
+export default i18n;
+
+export {};

+ 14 - 18
src/pages/A0base/index.module.scss

@@ -166,12 +166,12 @@
       color: rgba(177, 150, 123, 1);
       cursor: pointer;
 
-      &>img {
+      & > img {
         height: 30px;
         object-fit: contain;
       }
 
-      &>.guideVideoTitle {
+      & > .guideVideoTitle {
         height: 12px;
         line-height: 8px;
         font-size: 12px;
@@ -181,7 +181,6 @@
   }
 }
 
-
 // ------------移动端---------------
 
 .A0baseMo {
@@ -197,7 +196,7 @@
     }
 
     .guideVideo {
-      &>img {
+      & > img {
         width: 44px;
         object-fit: cover;
       }
@@ -207,38 +206,35 @@
         font-size: 16px;
       }
     }
-    .A0baseContainner .content{
+    .A0baseContainner .content {
       width: 68%;
-      .title{
+      .title {
         width: 160px;
         font-size: 26px;
       }
-      .text{
+      .text {
         color: black;
         font-size: 18px;
         line-height: 24px;
         letter-spacing: 2px;
         text-align: justify;
       }
-      .btn{
+      .btn {
         font-size: 18px;
         padding-top: 30px;
       }
     }
 
-
-
     .interact {
       width: 100px;
       height: 200px;
-      .inter_content_active{
-      .inter{
-        margin: 4px 0;
-        height: 24px;
-        font-size: 16px;
+      .inter_content_active {
+        .inter {
+          margin: 4px 0;
+          height: 24px;
+          font-size: 16px;
+        }
       }
     }
-    }
   }
-
-}
+}

+ 18 - 8
src/pages/A0base/index.tsx

@@ -2,10 +2,14 @@ import React, { useState } from 'react'
 import styles from './index.module.scss'
 import { isPc, myData } from '@/utils/http'
 import classNames from 'classnames'
+import IsDev from '@/components/IsDev'
+
+
 
 function A0base() {
   const [currentBase, setCurrentBase] = useState(0)
   const [isOpenInteract, setIsOpenInteract] = useState(false)
+  const [isOpenGuideVideo, setIsOpenGuideVideo] = useState(false)
 
   const goto = (e: React.MouseEvent, path: string) => {
     e.preventDefault()
@@ -14,7 +18,7 @@ function A0base() {
   }
 
   return (
-    <div className={classNames(isPc?'':styles.A0baseMo,styles.A0base)}>
+    <div className={classNames(isPc ? '' : styles.A0baseMo, styles.A0base)}>
       <div className='A0baseContainner'>
         <div className='content'>
           <div className='title myFont'>{myData.baseInfo[currentBase].title}</div>
@@ -37,25 +41,31 @@ function A0base() {
       {/* 互动 */}
       <div className='interact'>
         <div className={`inter_content ${isOpenInteract ? 'inter_content_active' : ''}`}>
-          <div className='inter'>碟影智绘</div>
-          <div className='inter'>展览图谱</div>
-          <div className='inter'>AI问答</div>
-        </div>
+          <div className='inter' onClick={() => setIsOpenGuideVideo(true)} > 碑影智绘</div>
+          <div className='inter' onClick={() => setIsOpenGuideVideo(true)} > 展览图谱</div>
+          <div className='inter' onClick={() => setIsOpenGuideVideo(true)}> AI问答</div>
+        </div >
         <div className='icon' onClick={() => setIsOpenInteract(!isOpenInteract)}>
           <img src={require('@/assets/img/interaction.png')} alt='' />
         </div>
-      </div>
+      </div >
 
       <div className='home' onClick={e => goto(e, '/')}>
         <img src={require('@/assets/img/home.png')} alt='' />
       </div>
 
       {/* 先导片 */}
-      <div className='guideVideo'>
+      <div className='guideVideo' onClick={() => setIsOpenGuideVideo(true)}>
         <img src={require('@/assets/img/guideVideo.png')} alt='' />
         <div className='guideVideoTitle'>先导片</div>
       </div>
-    </div>
+
+      {
+        isOpenGuideVideo && (
+          <IsDev closeFn={() => setIsOpenGuideVideo(false)} />
+        )
+      }
+    </div >
   )
 }
 

+ 9 - 16
src/pages/A2yblm/index.module.scss

@@ -51,7 +51,7 @@
       left: 4%;
       cursor: pointer;
 
-      &>img {
+      & > img {
         height: 100%;
         object-fit: contain;
       }
@@ -67,7 +67,7 @@
       left: 4%;
       cursor: pointer;
 
-      &>img {
+      & > img {
         height: 100%;
         object-fit: contain;
       }
@@ -92,7 +92,7 @@
         width: 20px;
         height: 100%;
 
-        &>img {
+        & > img {
           height: 100%;
           object-fit: contain;
         }
@@ -140,7 +140,7 @@
           height: 50px;
           cursor: pointer;
 
-          &>img {
+          & > img {
             height: 100%;
             object-fit: contain;
           }
@@ -155,7 +155,7 @@
         color: rgba(255, 255, 255, 1);
         letter-spacing: 4px;
 
-        &>p {
+        & > p {
           padding-bottom: 15px;
         }
       }
@@ -250,7 +250,7 @@
           align-items: center;
           justify-content: center;
 
-          &>img {
+          & > img {
             width: 100%;
             height: 100%;
             object-fit: contain;
@@ -265,8 +265,7 @@
         padding-right: 6px;
         overflow-y: auto;
 
-
-        &>div {
+        & > div {
           color: #595547;
           width: 100%;
           height: 100%;
@@ -282,7 +281,6 @@
   }
 }
 
-
 // ---------移动端
 .A2yblmMo {
   :global {
@@ -326,19 +324,16 @@
       .content {
         height: 240px;
 
-        &>div {
+        & > div {
           width: 100%;
           height: 100%;
           line-height: 18px;
 
           font-size: 16px;
-
         }
-
       }
     }
 
-
     .extra {
       width: 180px;
       height: 40px;
@@ -362,14 +357,12 @@
           width: 80px;
           height: 80px;
         }
-
       }
 
       .content {
         font-size: 18px;
         line-height: 24px;
-       
       }
     }
   }
-}
+}

+ 3 - 0
src/pages/A5wenwu/index.tsx

@@ -10,6 +10,9 @@ function A5wenwu() {
 
   useEffect(() => {
     setIsShowGesture(true)
+    setTimeout(() => {
+      setIsShowGesture(false)
+    }, 3000)
   }, [])
 
   const handleTabClick = (tab: string) => {

+ 7 - 1
src/pages/A6ybwx/A6_1_zxys/index.tsx

@@ -1,6 +1,7 @@
 import React, { useState } from 'react'
 import styles from './index.module.scss'
 import { myData } from '@/utils/http'
+import store from '@/store'
 
 function Zaoxiang() {
   const [currentType, setCurrentType] = useState('')
@@ -77,7 +78,12 @@ function Zaoxiang() {
                     <img src={item.src} draggable='false' alt='' />
                   </div>
                   <div className='txt'>{item.title}</div>
-                  <div className='icon3'>
+                  <div className='icon3' onClick={() =>
+                    store.dispatch({
+                      type: 'layout/lookBigImg',
+                      payload: { url: item.src, show: true }
+                    })
+                  }>
                     <img
                       src={require('@/assets/img/A6_zaoxiang_foxiang_zoomIn.png')}
                       draggable='false'

+ 6 - 1
src/pages/A6ybwx/Genealogy/components/Graph/index.module.scss

@@ -67,8 +67,13 @@
   background: rgba(0, 0, 0, 0.5);
   border: 1px solid rgba(255, 233, 182, 0.5);
   overflow: hidden;
-
+  touch-action: none !important;
+  pointer-events: auto !important;
+  user-select: none;
   .viewport {
+    touch-action: none !important;
+    pointer-events: auto !important;
+    user-select: none;
     position: absolute;
     border: 1px solid #ffe9b6;
     cursor: move;

+ 42 - 2
src/pages/A6ybwx/Genealogy/components/Graph/index.tsx

@@ -3,6 +3,7 @@ import styles from './index.module.scss'
 import { NodeTurnRight, NodeRight, NodeBottom, RightLineDash, NodeActive, NodeTurnBottomRight } from '../Utils'
 import { useDrag } from '@use-gesture/react'
 import { myData } from '@/utils/http'
+import { isMobiileFu } from '@/utils/history'
 
 const MAIN_CONTENT_WIDTH = 1920
 const MAIN_CONTENT_HEIGHT = 945
@@ -188,6 +189,27 @@ function Graph({ setCurrentNodeIndex }: { setCurrentNodeIndex: (index: number) =
     setOffsetY(-clampedY / miniMapScale)
   })
 
+  const bindMobile = useDrag(({ offset: [x, y] }) => {
+    // 计算视口最大移动范围
+    const maxX = contentSize.width * miniMapScale - MAIN_CONTENT_WIDTH * MINIMAP_SCALE - 2
+    const maxY = contentSize.height * miniMapScale - MAIN_CONTENT_HEIGHT * MINIMAP_SCALE - 4
+    // 钳制坐标范围
+    const clampedX = Math.max(0, Math.min(x, maxX))
+    const clampedY = Math.max(0, Math.min(y, maxY))
+    setOffsetX(-clampedX / miniMapScale)
+    setOffsetY(-clampedY / miniMapScale)
+  }, {
+    // 移动端专用配置
+    pointer: { touch: true },
+    pointerEvents: true,
+    // 阻止触摸滚动
+    preventScroll: true,
+    // 延迟触发防止误触
+    delay: 100,
+    // filterTouches: (e: TouchEvent) => e.target === e.currentTarget
+  })
+
+
   const handleNameClick = (index: number) => {
     console.log(index, '------------')
     setCurrentNodeIndex(index)
@@ -211,6 +233,24 @@ function Graph({ setCurrentNodeIndex }: { setCurrentNodeIndex: (index: number) =
   )
 
 
+  const NodeClickData = React.memo(() =>
+    <>
+      {myData.genealogyData.map((item, index) => {
+        let res
+        if (item.type === 'active') res = <NodeActive key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} className='nodeActiveG' />
+        if (item.type === 'nodeRight_n') res = <NodeRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='normal' />
+        if (item.type === 'nodeRight_a') res = <NodeRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='active' />
+        if (item.type === 'nodeRight_f') res = <NodeRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='false' />
+        if (item.type === 'nodeBottom_n') res = <NodeBottom key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='normal' />
+        if (item.type === 'nodeTurnRight_n') res = <NodeTurnRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='normal' />
+        if (item.type === 'nodeTurnRight_a') res = <NodeTurnRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='active' />
+        if (item.type === 'nodeTurnBottomRight_a') res = <NodeTurnBottomRight key={index} data={item} style={{ transform: `translate(${item.position.x}px, ${item.position.y}px)` }} type='active' />
+        return res
+      })}
+    </>
+  )
+
+
 
   return (
     <>
@@ -246,7 +286,7 @@ function Graph({ setCurrentNodeIndex }: { setCurrentNodeIndex: (index: number) =
       <div className={styles.miniMap}>
         <div
           className={styles.viewport}
-          {...bind()}
+          {...(isMobiileFu() ? bindMobile() : bind())}
           style={{
             transform: `translate(${-offsetX * miniMapScale}px, ${-offsetY * miniMapScale}px)`,
             width: `${MAIN_CONTENT_WIDTH * MINIMAP_SCALE}px`,
@@ -261,7 +301,7 @@ function Graph({ setCurrentNodeIndex }: { setCurrentNodeIndex: (index: number) =
             height: `${contentSize.height}px`
           }}
         >
-          <NodeData />
+          <NodeClickData />
         </div>
       </div>
     </>

+ 9 - 3
src/pages/A6ybwx/Genealogy/index.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React, { useEffect, useState } from 'react'
 import styles from './index.module.scss'
 import Graph from './components/Graph'
 import MemuSider from '@/components/MenuSider'
@@ -7,6 +7,12 @@ function Genealogy({ setGotoTab }: { setGotoTab: (tab: number) => void }) {
   const [isShowIntro, setIsShowIntro] = useState(true)
   const [currentNodeIndex, setCurrentNodeIndex] = useState(-1)
   const [isShowGesture, setIsShowGesture] = useState(true)
+
+  useEffect(() => {
+    if (!isShowIntro) setTimeout(() => {
+      setIsShowGesture(false)
+    }, 3000)
+  }, [isShowIntro])
   return (
     <div className={styles.Genealogy}>
       <div className='back' onClick={() => setGotoTab(0)}>
@@ -17,8 +23,8 @@ function Genealogy({ setGotoTab }: { setGotoTab: (tab: number) => void }) {
       </div>
       <MemuSider activeTab={1} />
 
-      {/* <Graph setCurrentNodeIndex={setCurrentNodeIndex} /> */}
-      <SvgGraph />
+      <Graph setCurrentNodeIndex={setCurrentNodeIndex} />
+      {/* <SvgGraph /> */}
 
       {isShowGesture && <div className={styles.gesture} onClick={() => setIsShowGesture(false)}>
         <img src={require('@/assets/img/A6_gen_gesture.png')} draggable='false' alt='' />

+ 4 - 4
src/store/reducer/index.ts

@@ -1,13 +1,13 @@
 // 导入合并reducer的依赖
-import { combineReducers } from "redux";
+import { combineReducers } from 'redux'
 
 // 导入 登录 模块的 reducer
-import A0Layout from "./layout";
+import A0Layout from './layout'
 
 // 合并 reducer
 const rootReducer = combineReducers({
   A0Layout,
-});
+})
 
 // 默认导出
-export default rootReducer;
+export default rootReducer

+ 59 - 27
src/store/reducer/layout.ts

@@ -1,43 +1,75 @@
+import { LookDomType } from "@/types";
+import { MessageType } from "@/utils/message";
+
 // 初始化状态
 const initState = {
   isHH: false,
-  style: {},
-  // 点赞数量
-  likeNum: 0,
-  // 打开分享
-  shareShow: false,
-  // 打开留言板
-  msgShow: false,
-  // 操作指引视频
-  guideVideo: ''
-}
+   style: {},
+  // 所有图片点击预览查看大图
+  lookBigImg: {
+    url: "",
+    show: false,
+  },
+  // 查看视频、音频、模型
+  lookDom: {
+    src: "",
+    type: "",
+  } as LookDomType,
+
+  // antd轻提示(兼容360浏览器)
+  message: {
+    txt: "",
+    type: "info",
+    duration: 3,
+  } as MessageType,
+  // 上传文件点击取消
+  closeUpFile: {
+    fu: () => {},
+    state: false,
+  },
+};
 
 // 定义 action 类型
 type LayoutActionType =
-  | { type: 'layout/isHH'; payload: boolean }
-  | { type: 'layout/style'; payload: any }
-  | { type: 'layout/likeNum'; payload: number }
-  | { type: 'layout/shareShow'; payload: boolean }
-  | { type: 'layout/msgShow'; payload: boolean }
-  | { type: 'layout/guideVideo'; payload: string }
+| { type: 'layout/isHH'; payload: boolean }
+| { type: 'layout/style'; payload: any }
+  | { type: "layout/lookBigImg"; payload: { url: string; show: boolean } }
+  | { type: "layout/lookDom"; payload: LookDomType }
+  | { type: "layout/message"; payload: MessageType }
+  | {
+      type: "layout/closeUpFile";
+      payload: {
+        fu: () => void;
+        state: boolean;
+      };
+    };
 
 // 频道 reducer
-export default function layoutReducer(state = initState, action: LayoutActionType) {
+export default function layoutReducer(
+  state = initState,
+  action: LayoutActionType
+) {
   switch (action.type) {
-    // 是横屏还是竖屏
     case 'layout/isHH':
       return { ...state, isHH: action.payload }
     case 'layout/style':
       return { ...state, style: action.payload }
-    case 'layout/likeNum':
-      return { ...state, likeNum: action.payload }
-    case 'layout/shareShow':
-      return { ...state, shareShow: action.payload }
-    case 'layout/msgShow':
-      return { ...state, msgShow: action.payload }
-    case 'layout/guideVideo':
-      return { ...state, guideVideo: action.payload }
+    // 所有图片点击预览查看大图
+    case "layout/lookBigImg":
+      return { ...state, lookBigImg: action.payload };
+    // 查看视频
+    case "layout/lookDom":
+      return { ...state, lookDom: action.payload };
+
+    // antd轻提示(兼容360浏览器)
+    case "layout/message":
+      return { ...state, message: action.payload };
+    // 上传文件点击取消
+    case "layout/closeUpFile":
+      return { ...state, closeUpFile: action.payload };
     default:
-      return state
+      return state;
   }
 }
+
+

+ 50 - 0
src/utils/message.ts

@@ -0,0 +1,50 @@
+import store from "@/store";
+
+export type MessageType = {
+  txt: string;
+  type: "info" | "success" | "error" | "warning";
+  duration: number;
+};
+
+export const MessageFu = {
+  info: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "info",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  success: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "success",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  error: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "error",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+  warning: (txt: string, duration?: number) => {
+    store.dispatch({
+      type: "layout/message",
+      payload: {
+        txt,
+        type: "warning",
+        duration: duration === undefined ? 3 : duration,
+      },
+    });
+  },
+};