feat: 添加思维导图支持,更新引导步骤组件,优化视频播放与学习进度追踪功能
This commit is contained in:
1 parent
e9a1fee27f
commit
c4c9be9d35
98 files changed
+3178
-551
No files matched your search
@@ -37,7 +37,10 @@
|
||||
:total-steps="totalSteps"
|
||||
@continue="handleNext"
|
||||
>
|
||||
{{ currentStepContent }}
|
||||
<!-- 支持组件形式的 content -->
|
||||
<component v-if="isCurrentStepComponent" :is="currentStepContentComponent" />
|
||||
<!-- 支持字符串形式的 content -->
|
||||
<div v-else v-html="currentStepContent"></div>
|
||||
</GuideStepsItem>
|
||||
</template>
|
||||
</GuideDragon>
|
||||
@@ -48,6 +51,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, useSlots, onMounted, onUnmounted, reactive } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
import SpotlightOverlay from './SpotlightOverlay.vue'
|
||||
import GuideDragon from './GuideDragon.vue'
|
||||
import GuideStepsItem from './GuideStepsItem.vue'
|
||||
@@ -55,7 +59,7 @@ import GuideStepsItem from './GuideStepsItem.vue'
|
||||
interface GuideStep {
|
||||
id: string
|
||||
title?: string
|
||||
content: string
|
||||
content: string | Component
|
||||
nextText?: string
|
||||
}
|
||||
|
||||
@@ -78,6 +82,8 @@ interface Props {
|
||||
highlightMode?: 'border' | 'gradient'
|
||||
/** 垂直偏移(仅Y轴),支持 number(px) 或 '10%'/'120px' */
|
||||
positionOffsetY?: number | string
|
||||
/** 是否可见,用于外部控制显示/隐藏 */
|
||||
visible?: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -95,14 +101,21 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
highlightPadding: 8,
|
||||
highlightRadius: 16,
|
||||
highlightMode: 'border',
|
||||
positionOffsetY: '50%'
|
||||
positionOffsetY: '50%',
|
||||
visible: true
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const slots = useSlots()
|
||||
const show = ref(true)
|
||||
const internalShow = ref(true)
|
||||
const currentStepIndex = ref(0)
|
||||
|
||||
// 合并外部 visible 和内部 show 状态
|
||||
// 只有当外部 visible 为 true 且内部 internalShow 也为 true 时才显示
|
||||
const show = computed(() => {
|
||||
return props.visible && internalShow.value
|
||||
})
|
||||
const alignStyle = reactive<{
|
||||
left: string
|
||||
top: string
|
||||
@@ -116,7 +129,12 @@ const alignStyle = reactive<{
|
||||
width: '1920px',
|
||||
height: '1080px'
|
||||
})
|
||||
const highlightSelector = computed(() => props.highlightSelector || null)
|
||||
// 验证并清理选择器,空字符串或只包含空格的选择器视为无效
|
||||
const highlightSelector = computed(() => {
|
||||
if (!props.highlightSelector) return null
|
||||
const trimmed = props.highlightSelector.trim()
|
||||
return trimmed.length > 0 ? trimmed : null
|
||||
})
|
||||
const highlightPadding = computed(() => props.highlightPadding || 0)
|
||||
const highlightRadius = computed(() => props.highlightRadius || 0)
|
||||
|
||||
@@ -148,20 +166,34 @@ const currentStepComponent = computed(() => {
|
||||
return slotSteps.value[currentStepIndex.value] || null
|
||||
})
|
||||
|
||||
// 判断当前步骤的 content 是否为组件
|
||||
const isCurrentStepComponent = computed(() => {
|
||||
if (useSlotMode.value) return false
|
||||
const step = currentStepData.value
|
||||
if (!step) return false
|
||||
return typeof step.content !== 'string'
|
||||
})
|
||||
|
||||
// 当前步骤的内容(字符串形式)
|
||||
const currentStepContent = computed(() => {
|
||||
if (useSlotMode.value) return ''
|
||||
if (isCurrentStepComponent.value) return ''
|
||||
|
||||
const step = currentStepData.value
|
||||
if (!step) return ''
|
||||
|
||||
// if (!step.title) {
|
||||
return `${step.content}`
|
||||
// }
|
||||
return typeof step.content === 'string' ? step.content : ''
|
||||
})
|
||||
|
||||
// return `
|
||||
// <h3 class="text-lg font-bold mb-2">${step.title}</h3>
|
||||
// <div class="leading-relaxed">${step.content}</div>
|
||||
// `
|
||||
// 当前步骤的内容(组件形式)
|
||||
const currentStepContentComponent = computed(() => {
|
||||
if (useSlotMode.value) return null
|
||||
if (!isCurrentStepComponent.value) return null
|
||||
|
||||
const step = currentStepData.value
|
||||
if (!step) return null
|
||||
|
||||
return typeof step.content !== 'string' ? step.content : null
|
||||
})
|
||||
|
||||
const handleNext = () => {
|
||||
@@ -184,7 +216,7 @@ const handleSkip = () => {
|
||||
}
|
||||
|
||||
const handleComplete = () => {
|
||||
show.value = false
|
||||
internalShow.value = false
|
||||
emit('complete')
|
||||
}
|
||||
|
||||
@@ -205,7 +237,7 @@ const goToStep = (index: number) => {
|
||||
|
||||
const resetGuide = () => {
|
||||
currentStepIndex.value = 0
|
||||
show.value = true
|
||||
internalShow.value = true
|
||||
}
|
||||
|
||||
// 键盘导航
|
||||
@@ -309,6 +341,7 @@ defineExpose({
|
||||
inset: 0;
|
||||
z-index: 10001;
|
||||
pointer-events: none;
|
||||
font-family: 'DingTalkJinBuTi';
|
||||
}
|
||||
|
||||
.guide-align {
|
||||
|
||||
Reference in new issue
Block a user