feat: 添加断点续读功能,优化用户体验
- 在 chenyunStore 中引入 lastVisitedPlace 和 setLastVisitedPlace 方法,支持记录用户上次访问的场景。 - 在各个场景组件中使用 useEffect 设置 lastVisitedPlace,确保用户在返回时能继续上次访问的地点。 - 更新相关组件,确保在导航时能够正确使用 lastVisitedPlace,提升用户交互体验。 - 在 puzzleStore 中添加保存用户定义的功能,增强数据持久性。
This commit is contained in:
1 parent
76c67b492e
commit
b1d55b9464
17 files changed
+147
-54
No files matched your search
+25
@@ -4,6 +4,13 @@ import router from './router'
|
||||
import { ScaleProvider, useScale } from './contexts/ScaleContext'
|
||||
import { RotateHint } from './components/RotateHint'
|
||||
import { useUserStore } from './store'
|
||||
import { getUserDefine } from '@/lib/utils/api-common'
|
||||
import {
|
||||
usePuzzleStore,
|
||||
deserializePuzzles,
|
||||
PUZZLE_LESSON_ID,
|
||||
PUZZLE_KEY,
|
||||
} from '@/store/puzzleStore'
|
||||
|
||||
/**
|
||||
* 应用内容组件
|
||||
@@ -26,6 +33,24 @@ function AppContent() {
|
||||
}
|
||||
}, [setCourseInfo])
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
try {
|
||||
const raw = await getUserDefine(PUZZLE_LESSON_ID, PUZZLE_KEY)
|
||||
if (!raw) return
|
||||
const data = JSON.parse(raw) as { puzzles?: Record<string, number[]> }
|
||||
if (data.puzzles) {
|
||||
usePuzzleStore.setState((prev) => ({
|
||||
...prev,
|
||||
puzzles: deserializePuzzles(data.puzzles),
|
||||
}))
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to load puzzle store', e)
|
||||
}
|
||||
})()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative w-screen h-screen overflow-hidden">
|
||||
|
||||
@@ -277,7 +277,7 @@ const ResidenceLayoutContent = () => {
|
||||
{hasPuzzleFeature && (
|
||||
<div
|
||||
ref={puzzleBoxRef}
|
||||
className="absolute left-4 bottom-4 z-[1] cursor-pointer"
|
||||
className="absolute left-4 bottom-4 z-1 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (residence) {
|
||||
@@ -336,7 +336,7 @@ const ResidenceLayoutContent = () => {
|
||||
{isAnimationPlaying &&
|
||||
createPortal(
|
||||
<div
|
||||
className="fixed inset-0 z-[100000] pointer-events-auto"
|
||||
className="fixed inset-0 z-100000 pointer-events-auto"
|
||||
style={{ touchAction: 'none' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onTouchStart={(e) => e.preventDefault()}
|
||||
|
||||
@@ -28,19 +28,21 @@ const Chenyun = () => {
|
||||
// starName 固定为 chenyun
|
||||
const starName = 'chenyun'
|
||||
|
||||
const { lastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
console.log('1233')
|
||||
try {
|
||||
const raw = await getUserDefine(CHENYUN_LESSON_ID, CHENYUN_KEY)
|
||||
console.log(raw)
|
||||
if (!raw) return
|
||||
|
||||
const data = JSON.parse(raw) as {
|
||||
visitedExhibits?: Record<string, number[]>
|
||||
isYinyuanSolved?: boolean
|
||||
isMimianSolved?: boolean
|
||||
lastVisitedPlace?: string | null
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
useChenyunStore.setState((prev) => ({
|
||||
...prev,
|
||||
@@ -49,6 +51,8 @@ const Chenyun = () => {
|
||||
data.isYinyuanSolved !== undefined ? data.isYinyuanSolved : prev.isYinyuanSolved,
|
||||
isMimianSolved:
|
||||
data.isMimianSolved !== undefined ? data.isMimianSolved : prev.isMimianSolved,
|
||||
lastVisitedPlace:
|
||||
data.lastVisitedPlace !== undefined ? data.lastVisitedPlace : prev.lastVisitedPlace,
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('Failed to load Chenyun store', error)
|
||||
@@ -79,7 +83,7 @@ const Chenyun = () => {
|
||||
<Image
|
||||
button
|
||||
src={getStaticImage('star/一-解锁')}
|
||||
onClick={() => navigate(`/${moduleName}/${starName}/place1`)}
|
||||
onClick={() => navigate(`/${moduleName}/${starName}/${lastVisitedPlace ?? 'place1'}`)}
|
||||
/>
|
||||
{/* 体悟 */}
|
||||
<Image
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '@/components'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Cursor from '@/components/cursor'
|
||||
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
import { useChenyunStore, saveChenyunStore } from '@/store/chenyunStore'
|
||||
@@ -48,7 +48,11 @@ const ChenyunPlace1 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place1'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
@@ -251,22 +255,22 @@ const ChenyunPlace1 = () => {
|
||||
const LearningIntroduction = ({ handleStartLearning }: { handleStartLearning: () => void }) => {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div className="relative w-full h-full bg-gradient-to-b from-[#f5f1e8] via-[#ede8dc] to-[#e8e0d1] shadow-[0_20px_60px_rgba(0,0,0,0.4)] border-4 border-[#d4c5a9] overflow-hidden">
|
||||
<div className="relative w-full h-full bg-linear-to-b from-[#f5f1e8] via-[#ede8dc] to-[#e8e0d1] shadow-[0_20px_60px_rgba(0,0,0,0.4)] border-4 border-[#d4c5a9] overflow-hidden">
|
||||
{/* 内容层 */}
|
||||
<div className="relative h-full w-full p-16 pb-20 overflow-hidden flex flex-col">
|
||||
{/* 装饰性顶部线条 */}
|
||||
<div className="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-transparent via-[#c9b99b] to-transparent"></div>
|
||||
<div className="absolute top-0 left-0 right-0 h-2 bg-linear-to-r from-transparent via-[#c9b99b] to-transparent"></div>
|
||||
|
||||
{/* 标题 */}
|
||||
<div className="text-center mb-8 flex-shrink-0">
|
||||
<div className="text-center mb-8 shrink-0">
|
||||
<h1 className="text-6xl font-bold text-[#5c4a37] mb-4 tracking-wide">
|
||||
走进故居-对话模块学习说明
|
||||
</h1>
|
||||
<div className="w-32 h-1 bg-gradient-to-r from-transparent via-[#8b7355] to-transparent mx-auto"></div>
|
||||
<div className="w-32 h-1 bg-linear-to-r from-transparent via-[#8b7355] to-transparent mx-auto"></div>
|
||||
</div>
|
||||
|
||||
{/* 介绍文字 */}
|
||||
<div className="mb-8 space-y-4 flex-shrink-0">
|
||||
<div className="mb-8 space-y-4 shrink-0">
|
||||
<p className="text-3xl text-[#6b5d47] leading-relaxed text-center font-medium">
|
||||
同学你好,欢迎来到走进故居-对话学习模块!
|
||||
</p>
|
||||
@@ -312,7 +316,7 @@ const LearningIntroduction = ({ handleStartLearning }: { handleStartLearning: ()
|
||||
key={index}
|
||||
className="flex items-start gap-6 bg-[rgba(255,255,255,0.4)] rounded-xl p-6 border-l-4 border-[#8b7355] mb-4"
|
||||
>
|
||||
<span className="flex-shrink-0 w-12 h-12 bg-gradient-to-br from-[#8b7355] to-[#6b5d47] text-white text-3xl font-bold rounded-full flex items-center justify-center shadow-md">
|
||||
<span className="shrink-0 w-12 h-12 bg-linear-to-br from-[#8b7355] to-[#6b5d47] text-white text-3xl font-bold rounded-full flex items-center justify-center shadow-md">
|
||||
{index + 1}
|
||||
</span>
|
||||
<p className="text-2xl text-[#5c4a37] leading-relaxed flex-1 pt-1">{feature}</p>
|
||||
@@ -322,13 +326,13 @@ const LearningIntroduction = ({ handleStartLearning }: { handleStartLearning: ()
|
||||
</div>
|
||||
|
||||
{/* 开始学习按钮 */}
|
||||
<div className="flex justify-center flex-shrink-0">
|
||||
<div className="flex justify-center shrink-0">
|
||||
<button
|
||||
onClick={handleStartLearning}
|
||||
className="relative cursor-pointer px-20 py-5 bg-gradient-to-r from-[#8b7355] to-[#6b5d47] text-white text-4xl font-bold rounded-2xl shadow-[0_8px_20px_rgba(139,115,85,0.4)] hover:shadow-[0_12px_30px_rgba(139,115,85,0.6)] hover:from-[#9b8365] hover:to-[#7b6d57] transition-all duration-300 active:scale-95 border-2 border-[#a08f75]"
|
||||
className="relative cursor-pointer px-20 py-5 bg-linear-to-r from-[#8b7355] to-[#6b5d47] text-white text-4xl font-bold rounded-2xl shadow-[0_8px_20px_rgba(139,115,85,0.4)] hover:shadow-[0_12px_30px_rgba(139,115,85,0.6)] hover:from-[#9b8365] hover:to-[#7b6d57] transition-all duration-300 active:scale-95 border-2 border-[#a08f75]"
|
||||
>
|
||||
<span className="relative z-10">开始学习</span>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white to-transparent opacity-0 hover:opacity-20 rounded-2xl transition-opacity duration-300"></div>
|
||||
<div className="absolute inset-0 bg-linear-to-r from-transparent via-white to-transparent opacity-0 hover:opacity-20 rounded-2xl transition-opacity duration-300"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ExhibitOverlay, Title, Image, Cursor, GuideSteps, GuideStepsItem } from '@/components'
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
@@ -30,7 +30,11 @@ const ChenyunPlace2 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place2'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
@@ -35,7 +35,11 @@ const ChenyunPlace3 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place3'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
@@ -37,7 +37,11 @@ const ChenyunPlace4 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place4'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
@@ -45,7 +45,11 @@ const ChenyunPlace5 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place5'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ExhibitOverlay, Title, Image, Cursor, GuideSteps, GuideStepsItem } from '@/components'
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
@@ -25,7 +25,11 @@ const ChenyunPlace6 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place6'
|
||||
const { isExhibitVisited, markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { isExhibitVisited, markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
@@ -61,7 +61,11 @@ const ChenyunPlace7 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place7'
|
||||
const { isExhibitVisited, markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
@@ -38,7 +38,11 @@ const ChenyunPlace8 = () => {
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place8'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import {
|
||||
ExhibitOverlay,
|
||||
Title,
|
||||
Image,
|
||||
Cursor,
|
||||
Video,
|
||||
GuideSteps,
|
||||
GuideStepsItem,
|
||||
} from '@/components'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { Title, Image, Cursor, Video, GuideSteps, GuideStepsItem } from '@/components'
|
||||
import { getBackgroundImage, getStaticImage, getPublicPath } from '@/lib/utils'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import type { VideoRef } from '@/components/video'
|
||||
@@ -17,10 +9,6 @@ import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
|
||||
// starName 固定为 chenyun
|
||||
const starName = 'chenyun'
|
||||
|
||||
// 展品 ID 与资源路径映射
|
||||
const exhibitSrcMap: Record<1, string> = {
|
||||
1: 'chenyun/place9/场景9展品1',
|
||||
}
|
||||
const ChenyunPlace9 = () => {
|
||||
const navigate = useNavigate()
|
||||
const { moduleName } = useParams<{
|
||||
@@ -30,11 +18,15 @@ const ChenyunPlace9 = () => {
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation()
|
||||
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | null>(null)
|
||||
// const [activeExhibitId, setActiveExhibitId] = useState<1 | null>(null)
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = 'place9'
|
||||
const { markExhibitVisited, getVisitedExhibits } = useChenyunStore()
|
||||
const { markExhibitVisited, getVisitedExhibits, setLastVisitedPlace } = useChenyunStore()
|
||||
|
||||
useEffect(() => {
|
||||
setLastVisitedPlace(place)
|
||||
}, [setLastVisitedPlace])
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place)
|
||||
@@ -124,7 +116,7 @@ const ChenyunPlace9 = () => {
|
||||
|
||||
{/* 展品1视频播放(全屏) */}
|
||||
{videoVisible && (
|
||||
<div className="fixed inset-0 z-[9999] bg-black">
|
||||
<div className="fixed inset-0 z-9999 bg-black">
|
||||
<Video
|
||||
ref={videoRef}
|
||||
src={getPublicPath('video/chenyun/chenyun_14.mp4')}
|
||||
@@ -172,7 +164,7 @@ const ChenyunPlace9 = () => {
|
||||
</GuideSteps>
|
||||
|
||||
{/* 遮罩层 */}
|
||||
<ExhibitOverlay
|
||||
{/* <ExhibitOverlay
|
||||
visible={activeExhibitId !== null}
|
||||
src={activeExhibitId ? exhibitSrcMap[activeExhibitId] : ''}
|
||||
onClose={() => {
|
||||
@@ -181,7 +173,7 @@ const ChenyunPlace9 = () => {
|
||||
}
|
||||
setActiveExhibitId(null)
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ const ChenyunReflection = () => {
|
||||
// ========== 顶层状态管理 ==========
|
||||
|
||||
/** 当前展开的文件 */
|
||||
const [expandedFile, setExpandedFile] = useState<FileType | null>(null)
|
||||
const [expandedFile, setExpandedFile] = useState<FileType | null>(
|
||||
() => (useChenyunStore.getState().isYinyuanSolved ? 'mimian' : null)
|
||||
)
|
||||
|
||||
/** 当前播放的视频 */
|
||||
const [playingVideo, setPlayingVideo] = useState<FileType | null>(null)
|
||||
@@ -62,7 +64,9 @@ const ChenyunReflection = () => {
|
||||
const [showClueOverlay, setShowClueOverlay] = useState(false)
|
||||
|
||||
/** 欢迎语弹窗(使用 StudyGuideModal 展示入口引导) */
|
||||
const [showWelcomeGuide, setShowWelcomeGuide] = useState(true)
|
||||
const [showWelcomeGuide, setShowWelcomeGuide] = useState(
|
||||
() => !useChenyunStore.getState().isYinyuanSolved
|
||||
)
|
||||
|
||||
/** 第一幕银元之战 - 学习引导弹窗 */
|
||||
const [showYinyuanGuide, setShowYinyuanGuide] = useState(false)
|
||||
|
||||
@@ -44,7 +44,9 @@ export const useYinyuanState = () => {
|
||||
|
||||
// 记录是否已经显示过这些提示(避免重复显示)
|
||||
const [hasShownGuide1, setHasShownGuide1] = useState(false)
|
||||
const [hasShownGuide2, setHasShownGuide2] = useState(false)
|
||||
const [hasShownGuide2, setHasShownGuide2] = useState(
|
||||
() => useChenyunStore.getState().isYinyuanSolved
|
||||
)
|
||||
const [hasShownGuide3, setHasShownGuide3] = useState(false)
|
||||
|
||||
return {
|
||||
|
||||
@@ -23,6 +23,11 @@ interface ChenyunStore {
|
||||
// 重置所有访问记录
|
||||
resetAll: () => void
|
||||
|
||||
// ========== 断点续读 ==========
|
||||
// 上次访问的场景(place1~place9)
|
||||
lastVisitedPlace: string | null
|
||||
setLastVisitedPlace: (place: string) => void
|
||||
|
||||
// ========== 体悟模块状态 ==========
|
||||
// 银元之战是否已解决
|
||||
isYinyuanSolved: boolean
|
||||
@@ -105,6 +110,10 @@ export const useChenyunStore = create<ChenyunStore>()(
|
||||
set({ visitedExhibits: {} })
|
||||
},
|
||||
|
||||
// ========== 断点续读 ==========
|
||||
lastVisitedPlace: null,
|
||||
setLastVisitedPlace: (place: string) => set({ lastVisitedPlace: place }),
|
||||
|
||||
// ========== 体悟模块状态 ==========
|
||||
isYinyuanSolved: false,
|
||||
setYinyuanSolved: (solved: boolean) => {
|
||||
@@ -122,6 +131,7 @@ export const useChenyunStore = create<ChenyunStore>()(
|
||||
visitedExhibits: serializeVisitedExhibits(state.visitedExhibits),
|
||||
isYinyuanSolved: state.isYinyuanSolved,
|
||||
isMimianSolved: state.isMimianSolved,
|
||||
lastVisitedPlace: state.lastVisitedPlace,
|
||||
}),
|
||||
onRehydrateStorage: () => (state) => {
|
||||
if (state) {
|
||||
@@ -134,6 +144,9 @@ export const useChenyunStore = create<ChenyunStore>()(
|
||||
if (state.isMimianSolved === undefined) {
|
||||
state.isMimianSolved = false
|
||||
}
|
||||
if (state.lastVisitedPlace === undefined) {
|
||||
state.lastVisitedPlace = null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -151,6 +164,7 @@ export const saveChenyunStore = (): void => {
|
||||
visitedExhibits: serializeVisitedExhibits(state.visitedExhibits),
|
||||
isYinyuanSolved: state.isYinyuanSolved,
|
||||
isMimianSolved: state.isMimianSolved,
|
||||
lastVisitedPlace: state.lastVisitedPlace,
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import { STORE_NAMES } from './storeNames'
|
||||
import { setUserDefine } from '@/lib/utils/api-common'
|
||||
|
||||
interface PuzzleStore {
|
||||
// 每个故居的已解锁碎片集合,格式:{ residence: Set<pieceId> }
|
||||
@@ -35,7 +36,7 @@ const serializePuzzles = (puzzles: Record<string, Set<number>>) => {
|
||||
}
|
||||
|
||||
// 将数组转换回 Set
|
||||
const deserializePuzzles = (serialized: Record<string, number[]> | undefined) => {
|
||||
export const deserializePuzzles = (serialized: Record<string, number[]> | undefined) => {
|
||||
if (!serialized || typeof serialized !== 'object') {
|
||||
return {}
|
||||
}
|
||||
@@ -83,6 +84,7 @@ export const usePuzzleStore = create<PuzzleStore>()(
|
||||
if (!residencePuzzles.has(pieceId)) {
|
||||
const newPuzzles = { ...fixedPuzzles }
|
||||
newPuzzles[residence] = new Set([...residencePuzzles, pieceId])
|
||||
savePuzzleStore(newPuzzles)
|
||||
set({ puzzles: newPuzzles })
|
||||
}
|
||||
},
|
||||
@@ -156,3 +158,19 @@ export const usePuzzleStore = create<PuzzleStore>()(
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// ========== 易班平台数据保存 ==========
|
||||
export const PUZZLE_LESSON_ID = 'Cheny_neihua'
|
||||
export const PUZZLE_KEY = 24
|
||||
|
||||
export const savePuzzleStore = (puzzles: Record<string, Set<number>>): void => {
|
||||
const payload = {
|
||||
puzzles: serializePuzzles(puzzles),
|
||||
}
|
||||
|
||||
try {
|
||||
setUserDefine(PUZZLE_LESSON_ID, PUZZLE_KEY, JSON.stringify(payload))
|
||||
} catch (error) {
|
||||
console.error('Failed to save Chenyun store', error)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user