lanxin vor 2 Wochen
Ursprung
Commit
a3fa6c205e

Datei-Diff unterdrückt, da er zu groß ist
+ 194 - 117
public/myData/myData.js


BIN
src/assets/img/A2_shufa_bei.png


BIN
src/assets/img/A2_shufa_bg1.jpg


BIN
src/assets/img/A2_shufa_bg2.jpg


BIN
src/assets/img/A2_shufa_btn.png


BIN
src/assets/img/A2_shufa_lian.png


BIN
src/assets/img/A2_shufa_mzg.png


BIN
src/assets/img/A2_shufa_title_bg.png


BIN
src/assets/img/A2_shufa_txt_bg.png


BIN
src/assets/img/frame_bei1.png


BIN
src/assets/img/frame_bei2.png


BIN
src/assets/img/frame_lian1.png


BIN
src/assets/img/frame_lian2.png


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

@@ -535,3 +535,47 @@ textarea {
     opacity: 0.3;
   }
 }
+@keyframes lian_wang {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translateX(0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translateX(-11857px);
+  }
+}
+@keyframes lian_zou {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5177px, 0);
+  }
+}
+@keyframes bei_shang {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5845px, 0);
+  }
+}
+@keyframes bei_xia {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5845px, 0);
+  }
+}

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

@@ -699,4 +699,56 @@ textarea {
   100% {
     opacity: 0.3;
   }
+}
+
+// 序列帧 琏-王字旁
+@keyframes lian_wang {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translateX(0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translateX(-11857px);
+  }
+}
+
+// 序列帧 琏-走字底
+@keyframes lian_zou {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5177px, 0);
+  }
+}
+
+// 序列帧 碑-上半
+@keyframes bei_shang {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5845px, 0);
+  }
+}
+
+// 序列帧 碑-下半
+@keyframes bei_xia {
+  0% {
+    /* 第一帧:背景位置 (0, 0) */
+    transform: translate(0, 0);
+  }
+  100% {
+    /* 最后一帧:横向偏移 = -(总帧数-1) × 单帧宽度 */
+    /* 10帧:-(10-1)×200 = -1800px */
+    transform: translate(-5845px, 0);
+  }
 }

+ 12 - 1
src/pages/A2yblm/components/ModalTxt/index.module.scss

@@ -118,7 +118,7 @@
         line-height: 24px;
         font-weight: 400;
         color: #504e40;
-        transition: opacity 0.3s ease-in-out;
+        // transition: opacity 0.1s ease-out;
         display: flex;
         flex-direction: column;
         justify-content: center;
@@ -322,6 +322,17 @@
             padding-bottom: 10px;
           }
         }
+        .shufaBtn {
+          width: 180px;
+          height: 50px;
+          background-image: url(../../../../assets/img/A6_sangzang_btn2.png);
+          background-position: center;
+          background-size: 100% 100%;
+          line-height: 50px;
+          font-size: 17px;
+          text-align: center;
+          cursor: pointer;
+        }
       }
     }
   }

+ 71 - 211
src/pages/A2yblm/components/ModalTxt/index.tsx

@@ -7,6 +7,7 @@ import classNames from 'classnames'
 import { modalTxtTab } from './data'
 import { useSelector } from 'react-redux'
 import { RootState } from '@/store'
+import Shufa from '../Shufa'
 import { forwardRef, useImperativeHandle } from 'react'
 
 type Props = {
@@ -21,6 +22,7 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
 
   const [selectedTab, setSelectedTab] = useState(0)
   const [isOpenTrans, setIsOpenTrans] = useState(false)
+  const [isShowShufa, setIsShowShufa] = useState(false)
   const selectedTabRef = useRef(0)
 
   useEffect(() => {
@@ -36,19 +38,10 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
     sonSetStaFu
   }))
 
-  // useEffect(() => {
-  //   console.log(selectedTab, ',============')
-  // }, [selectedTab])
-
   const [activeAId, setActiveAId] = useState<number | null>(null)
   const [showTooltip, setShowTooltip] = useState(-1)
 
-  const originRef = useRef<any>(null)
-  const translateRef = useRef<any>(null)
   const contentRef = useRef<any>(null)
-  const originStartX = useRef<number>(0)
-  const translateStartX = useRef<number>(0)
-  // const contentStartX = useRef<number>(0)
 
   useEffect(() => {
     if (selectedTab !== 0) {
@@ -65,121 +58,6 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
     localStorage.setItem('selectedBeiwen', selectedTab.toString())
   }, [selectedTab])
 
-  useEffect(() => {
-    // const tooltipContent = document.getElementById('tooltipContent')
-    const OriContent = document.getElementById('OriContent')
-    // const introContent = document.getElementById('introContent')
-    const TransContent = document.getElementById('TransContent')
-    // console.log(TransContent, 'TransContent')
-    // // 新增滚动同步逻辑
-    // let isSyncing = false
-
-    // const syncScroll = (source: HTMLElement, target: HTMLElement) => {
-    //   if (!isSyncing) {
-    //     isSyncing = true
-    //     target.scrollTop = source.scrollTop
-    //     setTimeout(() => isSyncing = false, 100)
-    //   }
-    // }
-
-    const handleOriScroll = () => {
-      if (OriContent && TransContent) {
-        // syncScroll(OriContent, TransContent)
-        TransContent.scrollTop = OriContent.scrollTop
-      }
-    }
-
-    const handleTransScroll = () => {
-      if (TransContent && OriContent) {
-        // syncScroll(TransContent, OriContent)
-        OriContent.scrollTop = TransContent.scrollTop
-      }
-    }
-
-    // if (OriContent) {
-    //   OriContent.addEventListener('scroll', handleOriScroll)
-    // }
-    // if (TransContent) {
-    //   TransContent.addEventListener('scroll', handleTransScroll)
-    // }
-
-    // return () => {
-    //   if (OriContent) {
-    //     OriContent.removeEventListener('scroll', handleOriScroll)
-    //   }
-    //   if (TransContent) {
-    //     TransContent.removeEventListener('scroll', handleTransScroll)
-    //   }
-    // }
-    const handleStart = (e: TouchEvent, startX: any) => {
-      startX.current = e.touches[0].clientX
-    }
-    const handlerOri = (e: TouchEvent) => {
-      // e.stopPropagation()
-      const deltaX = e.touches[0].clientX - originStartX.current
-
-      if (OriContent && TransContent) {
-        OriContent.scrollTop += deltaX * 0.06
-        // console.log(OriContent.scrollTop, 'scrollTop')
-        TransContent.scrollTop = OriContent.scrollTop
-      }
-    }
-
-    const handlerTrans = (e: TouchEvent) => {
-      // e.stopPropagation()
-      const deltaX = e.touches[0].clientX - translateStartX.current
-
-      if (TransContent && OriContent) {
-        TransContent.scrollTop += deltaX * 0.06
-        OriContent.scrollTop = TransContent.scrollTop
-      }
-    }
-
-    // 处理触摸开始事件
-
-    if (OriContent) {
-      OriContent.addEventListener('touchstart', e => {
-        handleStart(e, originStartX)
-      })
-      OriContent.addEventListener('touchmove', handlerOri)
-      OriContent.addEventListener('scroll', handleOriScroll)
-    }
-    // if (tooltipContent) {
-    //   tooltipContent.addEventListener('touchmove', handler)
-    //   return () => {
-    //     tooltipContent.removeEventListener('touchmove', handler) // 保持参数一致
-    //   }
-    // }
-    // if (introContent) {
-    //   introContent.addEventListener('touchmove', handler)
-    //   return () => {
-    //     introContent.removeEventListener('touchmove', handler) // 保持参数一致
-    //   }
-    // }
-    if (TransContent) {
-      // console.log(TransContent, 'TransContent')
-      TransContent.addEventListener('touchstart', e => {
-        handleStart(e, translateStartX)
-      })
-      TransContent.addEventListener('touchmove', handlerTrans)
-      TransContent.addEventListener('scroll', handleTransScroll)
-    }
-    if (OriContent && TransContent) {
-      return () => {
-        OriContent.removeEventListener('touchstart', e => {
-          handleStart(e, originStartX)
-        })
-        OriContent.removeEventListener('touchmove', handlerOri)
-        OriContent.removeEventListener('scroll', handleOriScroll)
-        TransContent.removeEventListener('touchstart', e => {
-          handleStart(e, translateStartX)
-        })
-        TransContent.removeEventListener('touchmove', handlerTrans)
-        TransContent.removeEventListener('scroll', handleTransScroll)
-      }
-    }
-  }, [selectedTab])
-
   //动态加入a标签
   const CommentLink = ({
     index,
@@ -358,105 +236,87 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
     '程哲碑碑文,31行楷书,满行45字,<br/>字径约2厘米,带方界格,总计1404字;<br/>无正式碑名,通篇颂德程氏家族的历史功绩。'
 
   return (
-    <div
-      className={classNames(styles.modalTxt, myLangue === 'ZH' ? '' : styles.modalTxtEn)}
-      id='modalTxt'
-    >
-      <div className='modalTxtContainner'>
-
-
-        {!isOpenTrans && <div
-          className='intro'
-          id='introContent'
-          style={{
-            opacity: selectedTab !== 0 ? '1' : '0',
-            height: selectedTab !== 0 ? '78%' : '0%',
-            width: selectedTab !== 0 ? '100%' : '0%'
-          }}
-        >
-          <div className="intro_title songFont">
-            {selectedTab !== 0 && modalTxtTab[selectedTab - 1].name}
-          </div>
-          <div className="intro_txt"> {selectedTab !== 0 && myData.readDetail[selectedTab - 1].intro}</div>
-
-          <div className="intro_btn" onClick={() => setIsOpenTrans(true)}>原文 | 译文</div>
-        </div>}
-
-        {/* {selectedTab !== 0 && (
-          <div className='detailTxt'>
-            <div className='left'>
-              <div className='title'>原文</div>
-              <div className='txt' ref={originRef} id='OriContent'>
-                {CommentText({
-                  str: myData.readDetail[selectedTab - 1].origin,
-                  index: selectedTab - 1
-                })}
-              </div>
+    <>
+      <div
+        className={classNames(styles.modalTxt, myLangue === 'ZH' ? '' : styles.modalTxtEn)}
+        id='modalTxt'
+      >
+        <div className='modalTxtContainner'>
+
+
+          {!isOpenTrans && <div
+            className='intro'
+            id='introContent'
+            style={{
+              opacity: selectedTab !== 0 ? '1' : '0',
+              height: selectedTab !== 0 ? '78%' : '0%',
+              width: selectedTab !== 0 ? '100%' : '0%'
+            }}
+          >
+            <div className="intro_title songFont">
+              {selectedTab !== 0 && modalTxtTab[selectedTab - 1].name}
             </div>
-            <div className='right'>
-              <div className='title'>译文</div>
+            <div className="intro_txt"> {selectedTab !== 0 && myData.readDetail[selectedTab - 1].intro}</div>
+
+            <div className="intro_btn" onClick={() => setIsOpenTrans(true)}>原文 | 译文</div>
+          </div>}
+
+          {selectedTab === 0 && (
+            <div className='content'>
+              <div className='title songFontc'>
+                {myLangue === 'EN' ? 'Overview of the Inscription' : '碑文概述'}
+              </div>
               <div
-                className='txt'
-                ref={translateRef}
-                id='TransContent'
-                dangerouslySetInnerHTML={{ __html: myData.readDetail[selectedTab - 1].translate }}
+                className='text'
+                dangerouslySetInnerHTML={{ __html: myLangue === 'EN' ? gaiShuEn : gaiShu }}
               ></div>
+              <div className="shufaBtn" onClick={() => setIsShowShufa(true)}>书法赏析</div>
             </div>
-          </div>
-        )} */}
+          )}
 
-        {selectedTab === 0 && (
-          <div className='content'>
-            <div className='title songFontc'>
-              {myLangue === 'EN' ? 'Overview of the Inscription' : '碑文概述'}
-            </div>
-            <div
-              className='text'
-              dangerouslySetInnerHTML={{ __html: myLangue === 'EN' ? gaiShuEn : gaiShu }}
-            ></div>
-          </div>
-        )}
-
-        <div className='topBar'>
-          <div className='beie' hidden={selectedTab !== 0}>
-            <img src={require('@/assets/img/beie.png')} alt='' />
-            <div className='txt songFont' onClick={handleBeie}>
-              {myLangue === 'EN' ? 'forehead' : '碑额'}
+          <div className='topBar'>
+            <div className='beie' hidden={selectedTab !== 0}>
+              <img src={require('@/assets/img/beie.png')} alt='' />
+              <div className='txt songFont' onClick={handleBeie}>
+                {myLangue === 'EN' ? 'forehead' : '碑额'}
+              </div>
             </div>
-          </div>
 
-          {modalTxtTab.map((item, index) => (
-            <div
-              key={item.key}
-              className={`tab${index}`}
-              onClick={() => handleTabClick(item.key, myData.readDetail[index]?.mzmtz?.title)}
-            >
-              <img
-                src={require(`@/assets/img/btn_ModalTxt_bg${selectedTab === item.key ? '_ac' : ''
-                  }.png`)}
-                alt=''
-              />
-              <div className={`tabNub songFont ${selectedTab === item.key ? 'tabNubAc' : ''}`}>
-                {item.sonTxt}
-              </div>
+            {modalTxtTab.map((item, index) => (
               <div
-                className={classNames('txt songFont', selectedTab === item.key ? 'txtAc' : '')}
-                style={{ opacity: selectedTab === 0 ? '1' : '0' }}
+                key={item.key}
+                className={`tab${index}`}
+                onClick={() => handleTabClick(item.key, myData.readDetail[index]?.mzmtz?.title)}
               >
-                {myLangue === 'EN' ? item.nameEn : item.name}
+                <img
+                  src={require(`@/assets/img/btn_ModalTxt_bg${selectedTab === item.key ? '_ac' : ''
+                    }.png`)}
+                  alt=''
+                />
+                <div className={`tabNub songFont ${selectedTab === item.key ? 'tabNubAc' : ''}`}>
+                  {item.sonTxt}
+                </div>
+                <div
+                  className={classNames('txt songFont', selectedTab === item.key ? 'txtAc' : '')}
+                  style={{ opacity: selectedTab === 0 ? '1' : '0' }}
+                >
+                  {myLangue === 'EN' ? item.nameEn : item.name}
+                </div>
               </div>
-            </div>
-          ))}
-        </div>
+            ))}
+          </div>
 
-        {(isOpenTrans && selectedTab !== 0) && <div className="translateModal">
-          <div className="txtWithTrans" onTouchMove={() => setShowTooltip(-1)}> {CommentText({
-            str: myData.readDetail[selectedTab - 1].translate_v2,
-            index: selectedTab - 1
-          })}</div>
-        </div>}
+          {(isOpenTrans && selectedTab !== 0) && <div className="translateModal">
+            <div className="txtWithTrans" onTouchMove={() => setShowTooltip(-1)}> {CommentText({
+              str: myData.readDetail[selectedTab - 1].translate_v2,
+              index: selectedTab - 1
+            })}</div>
+          </div>}
+        </div>
       </div>
-    </div>
+      {/* 书法赏析 */}
+      {isShowShufa && <Shufa setIsShowShufa={setIsShowShufa} />}
+    </>
   )
 }
 export default forwardRef(ModalTxt)

+ 336 - 0
src/pages/A2yblm/components/Shufa/index.module.scss

@@ -0,0 +1,336 @@
+.shufa {
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  z-index: 3;
+  top: 0;
+  left: 0;
+  background-color: #ffffffcf;
+  :global {
+    .shufa1 {
+      width: 100%;
+      height: 100%;
+      padding: 20px 30px;
+      position: fixed;
+      top: 0;
+      left: 0;
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+      background-image: url('../../../../assets/img/A2_shufa_bg1.jpg');
+      background-size: 100% 100%;
+      background-repeat: no-repeat;
+      gap: 20px;
+      transition: all 0.6s ease-in-out;
+      .sLeft {
+        width: 30%;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        gap: 8px;
+        .zi {
+          width: 65%;
+          height: 38%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          & > img {
+            width: 100%;
+            height: 100%;
+            object-fit: contain;
+          }
+        }
+        .leftTxt1 {
+          width: 78%;
+          height: 20%;
+          color: rgba(38, 38, 38, 1);
+          font-size: 13px;
+          line-height: 20px;
+        }
+      }
+      .sRight {
+        width: 52%;
+        height: 100%;
+        .rightTitle1 {
+          width: 88%;
+          height: fit-content;
+          color: rgba(94, 52, 34, 1);
+          font-size: 16px;
+          line-height: 22px;
+        }
+        .rightTitleLine1 {
+          transform: translateY(-8px);
+          width: 88%;
+          height: 23px;
+          background: url(../../../../assets/img/A2_shufa_title_bg.png) no-repeat;
+          background-size: 100% 100%;
+        }
+        .rightIntro {
+          margin-top: 40px;
+          width: 88%;
+          height: 37%;
+          color: rgba(69, 68, 55, 1);
+          background: url(../../../../assets/img/A2_shufa_txt_bg.png) no-repeat;
+          background-size: 100% 100%;
+          .rightItem {
+            width: 100%;
+            transform: translateY(-21px);
+            height: 41px;
+            white-space: nowrap;
+            .rLabel {
+              display: inline-block;
+              vertical-align: top;
+              width: fit-content;
+              max-width: 30%;
+              height: 100%;
+              font-size: 16px;
+              line-height: 35px;
+              font-weight: 700;
+            }
+            .rtext {
+              display: inline-block;
+              vertical-align: top;
+              width: fit-content;
+              max-width: 60%;
+              padding-left: 25px;
+              height: 100%;
+              font-size: 16px;
+              line-height: 35px;
+            }
+          }
+        }
+        .rightTxt1 {
+          font-size: 14px;
+          margin-top: 10px;
+          color: rgba(38, 38, 38, 1);
+          width: 88%;
+          height: fit-content;
+          text-align: justify;
+        }
+        .shufa1Btn {
+          background: url(../../../../assets/img/A2_shufa_btn.png) no-repeat;
+          background-size: 100% 100%;
+          width: 120px;
+          height: 45px;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          color: rgba(255, 233, 182, 0.8);
+          font-size: 17px;
+        }
+      }
+    }
+
+    .shufa2 {
+      background: url(../../../../assets/img/A2_shufa_bg2.jpg) no-repeat;
+      background-size: 100% 100%;
+      width: 100%;
+      height: 100%;
+      position: fixed;
+      top: 0;
+      left: 0;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      padding: 20px 0;
+      transition: all 0.6s ease-in-out;
+
+      .lianContainer,
+      .beiContainner {
+        width: 73%;
+        height: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: 20px;
+        .zicontent {
+          width: 48%;
+          height: 100%;
+          display: flex;
+          flex-direction: column;
+          align-items: flex-end;
+          justify-content: center;
+          .txtitem {
+            width: 100%;
+            height: 24px;
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            gap: 3px;
+            color: rgba(69, 68, 55, 1);
+            .txt {
+              font-size: 14px;
+              line-height: 20px;
+              span {
+                font-weight: bold;
+              }
+            }
+            .icon {
+              width: 24px;
+              height: 24px;
+              cursor: pointer;
+              & > img {
+                width: 100%;
+                height: 100%;
+                object-fit: contain;
+                animation: fadeInOut 3s linear infinite;
+              }
+            }
+          }
+          .txtitemAc {
+            color: rgba(175, 135, 100, 1);
+          }
+        }
+        .zi1,
+        .zi2 {
+          width: 167px;
+          height: 167px;
+          overflow: hidden;
+          transition: opacity 0.3s ease-in-out;
+          & > img {
+            width: 100%;
+            height: 100%;
+            object-fit: contain;
+          }
+        }
+        .zi1 {
+          .lian1 {
+            max-width: none;
+            max-height: none;
+            width: auto;
+            height: 167px;
+            object-fit: fill;
+            animation: lian_wang 5s steps(71) forwards;
+          }
+          .lian2 {
+            max-width: none;
+            max-height: none;
+            width: auto;
+            height: 167px;
+            object-fit: fill;
+            animation: lian_zou 3s steps(31) forwards;
+          }
+        }
+        .zi2 {
+          .bei1 {
+            max-width: none;
+            max-height: none;
+            width: auto;
+            height: 167px;
+            object-fit: fill;
+            animation: bei_shang 5s steps(35) forwards;
+          }
+          .bei2 {
+            max-width: none;
+            max-height: none;
+            width: auto;
+            height: 167px;
+            object-fit: fill;
+            animation: bei_xia 3s steps(35) forwards;
+          }
+        }
+      }
+      .beiContainner {
+        .zicontent {
+          .txtitem {
+            justify-content: flex-start;
+          }
+        }
+      }
+    }
+  }
+}
+
+.shufaLine1 {
+  width: 100px;
+  height: 18px;
+
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-312px, -135px);
+  white-space: nowrap;
+  :global {
+    .dot {
+      width: 3px;
+      height: 3px;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+    .leftLine {
+      display: inline-block;
+      vertical-align: top;
+      width: 40px;
+      height: 100%;
+      color: rgba(255, 233, 182, 1);
+      font-size: 9px;
+      border-left: 2px dashed rgba(255, 233, 182, 1);
+      border-bottom: 2px dashed rgba(255, 233, 182, 1);
+      .dot {
+        transform: translate(-54px, -25px);
+      }
+    }
+    .rightLine {
+      display: inline-block;
+      vertical-align: top;
+      font-size: 9px;
+      width: 38px;
+      height: 100%;
+      color: rgba(124, 75, 54, 1);
+      border-bottom: 2px dashed rgba(124, 75, 54, 1);
+      .dot {
+        transform: translate(27px, 2px);
+      }
+    }
+  }
+}
+
+.shufaLine2 {
+  width: 100px;
+  height: 119px;
+
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-312px, 35px);
+  white-space: nowrap;
+  :global {
+    .dot {
+      width: 3px;
+      height: 3px;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+    .leftLine {
+      display: inline-block;
+      vertical-align: top;
+      width: 40px;
+      height: 100%;
+      color: rgba(255, 233, 182, 1);
+      font-size: 9px;
+      border-left: 2px dashed rgba(255, 233, 182, 1);
+      border-top: 2px dashed rgba(255, 233, 182, 1);
+      .dot {
+        transform: translate(-54px, 53px);
+      }
+    }
+    .rightLine {
+      display: inline-block;
+      vertical-align: top;
+      font-size: 9px;
+      width: 38px;
+      height: 100%;
+      color: rgba(124, 75, 54, 1);
+      border-top: 2px dashed rgba(124, 75, 54, 1);
+      .dot {
+        transform: translate(27px, -65px);
+      }
+    }
+  }
+}

+ 139 - 0
src/pages/A2yblm/components/Shufa/index.tsx

@@ -0,0 +1,139 @@
+import React, { useState } from 'react'
+import styles from './index.module.scss'
+import Zback from '@/components/Zback'
+import { useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import lian1 from '@/assets/img/frame_lian1.png'
+import lian2 from '@/assets/img/frame_lian2.png'
+import bei1 from '@/assets/img/frame_bei1.png'
+import bei2 from '@/assets/img/frame_bei2.png'
+
+
+type ActiveItem = 'lian1' | 'lian2' | 'bei1' | 'bei2' | ''
+function Shufa({ setIsShowShufa }: { setIsShowShufa: (isShowShufa: boolean) => void }) {
+  const [showName, setName] = useState('shufa1')
+  const [activeItem, setActiveItem] = useState<ActiveItem>('')
+  const { myData, myLangue } = useSelector((state: RootState) => state.A0Layout)
+  const Line = ({ index }: { index: number }) => {
+    return (
+      <div className={index % 2 !== 0 ? styles.shufaLine1 : styles.shufaLine2}>
+        <div className='leftLine'>
+          <div className='dot'>●</div>
+        </div>
+        <div className='rightLine'>
+          <div className='dot'>●</div>
+        </div>
+      </div>
+    )
+  }
+
+  const backClick = () => {
+    if (showName === 'shufa2') {
+      setActiveItem('')
+      setName('shufa1')
+    }
+    if (showName === 'shufa1') setIsShowShufa(false)
+  }
+
+  return (
+    <div className={styles.shufa} style={{ zIndex: showName === 'shufa1' ? 3 : 4 }}>
+      <Zback clickFu={backClick} />
+      <div
+        className='shufa1'
+        style={{
+          opacity: showName === 'shufa1' ? 1 : 0,
+          pointerEvents: showName === 'shufa1' ? 'auto' : 'none'
+        }}
+      >
+        <div className='sLeft'>
+          <div className='zi'>
+            <img src={require('@/assets/img/A2_shufa_lian.png')} alt='' />
+          </div>
+          <div className='zi'>
+            <img src={require('@/assets/img/A2_shufa_bei.png')} alt='' />
+          </div>
+          <div className='leftTxt1'>{myData?.shufa?.lIntro}</div>
+        </div>
+        <div className='sRight'>
+          <div className='rightTitle1'>{myData?.shufa?.rIntro}</div>
+          <div className='rightTitleLine1' />
+          <div className='rightIntro'>
+            {myData?.shufa?.rItems?.map((item, index) => (
+              <div className='rightItem' key={index}>
+                <div className='rLabel'>{item?.label}</div>
+                <div className='rtext'>{item?.txt}</div>
+              </div>
+            ))}
+          </div>
+          <div className='rightTxt1'>{myData?.shufa?.rTxt}</div>
+          <div className='shufa1Btn' onClick={() => setName('shufa2')}>
+            字形赏析
+          </div>
+        </div>
+        <Line index={1} />
+        <Line index={2} />
+      </div>
+
+      <div
+        className='shufa2'
+        style={{
+          opacity: showName === 'shufa2' ? 1 : 0,
+          pointerEvents: showName === 'shufa2' ? 'auto' : 'none'
+        }}
+      >
+        {/* 琏 */}
+        <div className='lianContainer'>
+          <div className='zicontent'>
+            {myData?.shufa2?.lian?.map((item, index) => (
+              <div
+                className={`txtitem ${activeItem === `lian${index}` ? 'txtitemAc' : ''}`}
+                key={index}
+                onClick={() =>
+                  index > 0 && index < 3 && setActiveItem(`lian${index}` as ActiveItem)
+                }
+              >
+                <div className='txt' dangerouslySetInnerHTML={{ __html: item }} />
+                <div
+                  className='icon'
+                  style={{ visibility: index === 0 || index === 3 ? 'hidden' : 'visible' }}
+                >
+                  <img src={require('@/assets/img/A7base3_icon.png')} alt='' />
+                </div>
+              </div>
+            ))}
+          </div>
+          <div className={`zi1`} >
+            <img className={activeItem} src={activeItem === 'lian1' ? lian1 : activeItem === 'lian2' ? lian2 : require('@/assets/img/A2_shufa_lian.png')} alt="" />
+          </div>
+        </div>
+        {/* 碑 */}
+        <div className='beiContainner'>
+          <div className={`zi2 ${activeItem}`} >
+            <img className={activeItem} src={activeItem === 'bei1' ? bei1 : activeItem === 'bei2' ? bei2 : require('@/assets/img/A2_shufa_bei.png')} alt="" />
+          </div>
+          <div className='zicontent'>
+            {myData?.shufa2?.bei?.map((item, index) => (
+              <div
+                className={`txtitem ${activeItem === `bei${index}` ? 'txtitemAc' : ''}`}
+                key={index}
+                onClick={() => index > 0 && index < 3 && setActiveItem(`bei${index}` as ActiveItem)}
+              >
+                <div
+                  className='icon'
+                  style={{ visibility: index === 0 || index === 3 ? 'hidden' : 'visible' }}
+                >
+                  <img src={require('@/assets/img/A7base3_icon.png')} alt='' />
+                </div>
+                <div className='txt' dangerouslySetInnerHTML={{ __html: item }} />
+              </div>
+            ))}
+          </div>
+        </div>
+      </div>
+    </div>
+  )
+}
+
+const MemoShufa = React.memo(Shufa)
+
+export default MemoShufa

+ 10 - 9
src/pages/A2yblm/index.tsx

@@ -145,18 +145,10 @@ function A2yblm() {
         </div>
       </div>
 
-      {/* 返回,文物鉴赏,全文鉴赏 */}
+      {/* 返回 */}
 
       <Zback clickFu={() => backToBase()} />
 
-      <div className='wenwu' onClick={gotoQuanwenOrWenwu}>
-        <img
-          src={require(`@/assets/img/A2_${currentTab === 'tab3' ? 'quanwen' : 'wenwu'}${myLangue === 'EN' ? 'En' : ''
-            }.png`)}
-          alt=''
-        />
-      </div>
-
       {/* 墓志铭体制之变例 */}
       {isShowMzmTitle && (
         <div className='extra' onClick={() => setIsOpenMzm(true)}>
@@ -222,6 +214,15 @@ function A2yblm() {
       {/* menu界面 */}
       <MenuSider activeTab={0} />
 
+      {/* 文物鉴赏,全文鉴赏  */}
+      <div className='wenwu' onClick={gotoQuanwenOrWenwu}>
+        <img
+          src={require(`@/assets/img/A2_${currentTab === 'tab3' ? 'quanwen' : 'wenwu'}${myLangue === 'EN' ? 'En' : ''
+            }.png`)}
+          alt=''
+        />
+      </div>
+
       {/* 图像赏析的tag 使用的是索引对应*/}
       <div className='tagInfo' style={{ display: isShowTag ? 'flex' : 'none' }}>
         <div className='top'>

+ 10 - 0
src/types/declaration.d.ts

@@ -33,6 +33,16 @@ type MyDataType = {
     translate: string
     translate_v2: string
   }[]
+  shufa: {
+    lIntro: string
+    rIntro: string
+    rItems: { label: string; txt: string }[]
+    rTxt: string
+  }
+  shufa2: {
+    lian: string[]
+    bei: string[]
+  }
   // 发现之谜
   discover: {
     txt1Items: { title: string }[]