Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
239 lines
7.4 KiB
TypeScript
239 lines
7.4 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { computed, ref } from 'vue'
|
|
import { moduleStepMap } from '@/config/moduleStepMap'
|
|
|
|
export const INTERVIEW_LESSON_ID = 'fangtan_rwqjk'
|
|
export const INTERVIEW_KEY = 3
|
|
|
|
interface InterviewSavePayload {
|
|
viewed?: Record<string, boolean>
|
|
isGuideInterviewTask?: boolean
|
|
isGuideInterviewReturn?: boolean
|
|
isGuideInterviewStage2?: boolean
|
|
isGuideInterviewStage3?: boolean
|
|
isInterviewUnlock?: boolean
|
|
InterviewUnlockProgress?: number
|
|
interviewMenuVisible?: boolean
|
|
guideShown?: Record<string, boolean>
|
|
practiceScrollPage?: number
|
|
}
|
|
|
|
async function getYibanApi(): Promise<typeof import('@/utils/yiban') | null> {
|
|
try {
|
|
return await import('@/utils/yiban')
|
|
} catch (error) {
|
|
console.error('Failed to load Yiban API', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* I store - manages Interview information
|
|
*/
|
|
export const useInterviewStore = defineStore(
|
|
'Interview',
|
|
() => {
|
|
//State
|
|
|
|
/**
|
|
* 已查看(停留≥阈值)记录
|
|
* key 推荐用 route.name
|
|
*/
|
|
const viewed = ref<Record<string, boolean>>({})
|
|
|
|
//Interview-任务取景框引导
|
|
const isGuideInterviewTask = ref<boolean>(true)
|
|
//Interview-思路提点完成回到首页的一次性引导
|
|
const isGuideInterviewReturn = ref<boolean>(true)
|
|
//Interview-中期拍摄阶段菜单一次性引导
|
|
const isGuideInterviewStage2 = ref<boolean>(true)
|
|
//Interview-后期剪辑阶段菜单一次性引导
|
|
const isGuideInterviewStage3 = ref<boolean>(true)
|
|
//Interview-是否解锁完成前置(观看视频)
|
|
const isInterviewUnlock = ref<boolean>(false)
|
|
//Interview-发现与记录解锁进度
|
|
const InterviewUnlockProgress = ref<number>(0)
|
|
//Interview-菜单 Overlay 是否可见
|
|
const interviewMenuVisible = ref<boolean>(false)
|
|
const guideShown = ref<Record<string, boolean>>({})
|
|
const practiceScrollPage = ref<number>(0)
|
|
|
|
//Actions
|
|
function saveInterviewStore(): void {
|
|
const payload: InterviewSavePayload = {
|
|
viewed: viewed.value,
|
|
isGuideInterviewTask: isGuideInterviewTask.value,
|
|
isGuideInterviewReturn: isGuideInterviewReturn.value,
|
|
isGuideInterviewStage2: isGuideInterviewStage2.value,
|
|
isGuideInterviewStage3: isGuideInterviewStage3.value,
|
|
isInterviewUnlock: isInterviewUnlock.value,
|
|
InterviewUnlockProgress: InterviewUnlockProgress.value,
|
|
interviewMenuVisible: interviewMenuVisible.value,
|
|
guideShown: guideShown.value,
|
|
practiceScrollPage: practiceScrollPage.value,
|
|
}
|
|
|
|
getYibanApi()
|
|
.then((api) =>
|
|
api?.setUserDefine(
|
|
INTERVIEW_LESSON_ID,
|
|
INTERVIEW_KEY,
|
|
JSON.stringify(payload),
|
|
),
|
|
)
|
|
.catch((error) =>
|
|
console.error('Failed to save Interview store', error),
|
|
)
|
|
}
|
|
|
|
async function loadInterviewStore(): Promise<void> {
|
|
try {
|
|
const api = await getYibanApi()
|
|
if (!api) return
|
|
|
|
const raw = await api.getUserDefine(INTERVIEW_LESSON_ID, INTERVIEW_KEY)
|
|
if (!raw) return
|
|
|
|
const data = JSON.parse(raw) as InterviewSavePayload
|
|
viewed.value = data.viewed ?? {}
|
|
isGuideInterviewTask.value = data.isGuideInterviewTask ?? true
|
|
isGuideInterviewReturn.value = data.isGuideInterviewReturn ?? true
|
|
isGuideInterviewStage2.value = data.isGuideInterviewStage2 ?? true
|
|
isGuideInterviewStage3.value = data.isGuideInterviewStage3 ?? true
|
|
isInterviewUnlock.value = data.isInterviewUnlock ?? false
|
|
InterviewUnlockProgress.value = data.InterviewUnlockProgress ?? 0
|
|
interviewMenuVisible.value = data.interviewMenuVisible ?? false
|
|
guideShown.value = data.guideShown ?? {}
|
|
practiceScrollPage.value = data.practiceScrollPage ?? 0
|
|
} catch (error) {
|
|
console.error('Failed to load Interview store', error)
|
|
}
|
|
}
|
|
|
|
function markViewed(key: string): void {
|
|
if (!key) return
|
|
viewed.value[key] = true
|
|
InterviewUnlockProgress.value = Math.max(
|
|
InterviewUnlockProgress.value,
|
|
Math.min(completedStep.value + 1, 4),
|
|
)
|
|
saveInterviewStore()
|
|
}
|
|
|
|
// 基于“子页面是否已查看”的大步骤完成度(1..3)
|
|
// disabled 子项不计入(在这里直接不放进列表)
|
|
const stepRouteNames = moduleStepMap.interview
|
|
|
|
const completedStep = computed<number>(() => {
|
|
const isDone = (step: 1 | 2 | 3) =>
|
|
stepRouteNames[step].every((name) => viewed.value[name] === true)
|
|
|
|
if (isDone(1) && isDone(2) && isDone(3)) return 3
|
|
if (isDone(1) && isDone(2)) return 2
|
|
if (isDone(1)) return 1
|
|
return 0
|
|
})
|
|
|
|
const progressPercent = computed<number>(() => {
|
|
const allRoutes = (Object.values(stepRouteNames) as string[][]).flat()
|
|
if (allRoutes.length === 0) return 0
|
|
const viewedCount = allRoutes.filter((name) => viewed.value[name]).length
|
|
return Math.round((viewedCount / allRoutes.length) * 100)
|
|
})
|
|
|
|
function setIsGuideInterviewTask(value: boolean): void {
|
|
isGuideInterviewTask.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setIsGuideInterviewReturn(value: boolean): void {
|
|
isGuideInterviewReturn.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setIsGuideInterviewStage2(value: boolean): void {
|
|
isGuideInterviewStage2.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setIsGuideInterviewStage3(value: boolean): void {
|
|
isGuideInterviewStage3.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setIsInterviewUnlock(value: boolean): void {
|
|
isInterviewUnlock.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setInterviewUnlockProgress(value: number): void {
|
|
const maxUnlockedStep = Math.min(completedStep.value + 1, 4)
|
|
InterviewUnlockProgress.value = Math.min(value, maxUnlockedStep)
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setInterviewMenuVisible(value: boolean): void {
|
|
interviewMenuVisible.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function markGuideShown(key: string): void {
|
|
if (!key) return
|
|
guideShown.value[key] = true
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function setPracticeScrollPage(value: number): void {
|
|
practiceScrollPage.value = value
|
|
saveInterviewStore()
|
|
}
|
|
|
|
function resetInterviewState(): void {
|
|
viewed.value = {}
|
|
isGuideInterviewTask.value = true
|
|
isGuideInterviewReturn.value = true
|
|
isGuideInterviewStage2.value = true
|
|
isGuideInterviewStage3.value = true
|
|
isInterviewUnlock.value = false
|
|
InterviewUnlockProgress.value = 0
|
|
interviewMenuVisible.value = false
|
|
guideShown.value = {}
|
|
practiceScrollPage.value = 0
|
|
saveInterviewStore()
|
|
}
|
|
|
|
return {
|
|
//State
|
|
markViewed,
|
|
setIsGuideInterviewTask,
|
|
setIsGuideInterviewReturn,
|
|
setIsGuideInterviewStage2,
|
|
setIsGuideInterviewStage3,
|
|
setIsInterviewUnlock,
|
|
setInterviewUnlockProgress,
|
|
setInterviewMenuVisible,
|
|
markGuideShown,
|
|
setPracticeScrollPage,
|
|
resetInterviewState,
|
|
loadInterviewStore,
|
|
saveInterviewStore,
|
|
//Actions
|
|
completedStep,
|
|
progressPercent,
|
|
viewed,
|
|
isGuideInterviewTask,
|
|
isGuideInterviewReturn,
|
|
isGuideInterviewStage2,
|
|
isGuideInterviewStage3,
|
|
isInterviewUnlock,
|
|
InterviewUnlockProgress,
|
|
interviewMenuVisible,
|
|
guideShown,
|
|
practiceScrollPage,
|
|
}
|
|
},
|
|
{
|
|
persist: true,
|
|
},
|
|
)
|