<template>
|
<div class="demo-controls">
|
<div class="control-buttons">
|
<el-button-group>
|
<el-button :type="isPlaying ? 'warning' : 'primary'" @click="togglePlay">
|
<el-icon v-if="isPlaying"><VideoPause /></el-icon>
|
<el-icon v-else><VideoPlay /></el-icon>
|
{{ isPlaying ? '暂停' : '播放' }}
|
</el-button>
|
<el-button @click="handleReset">
|
<el-icon><RefreshRight /></el-icon>
|
重置
|
</el-button>
|
</el-button-group>
|
|
<div class="speed-control">
|
<span class="speed-label">速度:</span>
|
<el-radio-group v-model="speed" size="small" @change="handleSpeedChange">
|
<el-radio-button label="0.5">慢速</el-radio-button>
|
<el-radio-button label="1">正常</el-radio-button>
|
<el-radio-button label="2">快速</el-radio-button>
|
</el-radio-group>
|
</div>
|
</div>
|
|
<div class="step-control" v-if="steps.length > 0">
|
<span class="step-label">步骤:</span>
|
<el-steps :active="currentStep" align-center>
|
<el-step
|
v-for="(step, index) in steps"
|
:key="index"
|
:title="step.title"
|
:description="step.description"
|
@click.native="handleStepClick(index)"
|
class="clickable-step"
|
/>
|
</el-steps>
|
</div>
|
|
<div class="explanation-box" v-if="currentExplanation">
|
<el-alert
|
:title="currentExplanation.title"
|
type="success"
|
:closable="false"
|
show-icon
|
>
|
<template #default>
|
{{ currentExplanation.content }}
|
</template>
|
</el-alert>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, computed, watch } from 'vue'
|
import { VideoPlay, VideoPause, RefreshRight } from '@element-plus/icons-vue'
|
|
const props = defineProps({
|
steps: {
|
type: Array,
|
default: () => []
|
},
|
explanations: {
|
type: Array,
|
default: () => []
|
}
|
})
|
|
const emit = defineEmits(['play', 'pause', 'reset', 'stepChange', 'speedChange'])
|
|
const isPlaying = ref(false)
|
const speed = ref('1')
|
const currentStep = ref(0)
|
|
const currentExplanation = computed(() => {
|
if (props.explanations && props.explanations[currentStep.value]) {
|
return props.explanations[currentStep.value]
|
}
|
return null
|
})
|
|
const togglePlay = () => {
|
isPlaying.value = !isPlaying.value
|
if (isPlaying.value) {
|
emit('play')
|
} else {
|
emit('pause')
|
}
|
}
|
|
const handleReset = () => {
|
isPlaying.value = false
|
currentStep.value = 0
|
emit('reset')
|
}
|
|
const handleStepClick = (index) => {
|
currentStep.value = index
|
emit('stepChange', index)
|
}
|
|
const handleSpeedChange = (val) => {
|
emit('speedChange', parseFloat(val))
|
}
|
|
const nextStep = () => {
|
if (currentStep.value < props.steps.length - 1) {
|
currentStep.value++
|
emit('stepChange', currentStep.value)
|
}
|
}
|
|
const setStep = (index) => {
|
currentStep.value = index
|
}
|
|
const setIsPlaying = (val) => {
|
isPlaying.value = val
|
}
|
|
defineExpose({
|
nextStep,
|
setStep,
|
setIsPlaying,
|
currentStep
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.demo-controls {
|
background: #fff;
|
padding: 20px;
|
border-radius: 12px;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
margin-bottom: 20px;
|
|
.control-buttons {
|
display: flex;
|
align-items: center;
|
gap: 24px;
|
margin-bottom: 20px;
|
|
.speed-control {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
|
.speed-label {
|
color: #666;
|
font-size: 14px;
|
}
|
}
|
}
|
|
.step-control {
|
margin-bottom: 20px;
|
|
.step-label {
|
display: block;
|
color: #666;
|
font-size: 14px;
|
margin-bottom: 12px;
|
}
|
|
.clickable-step {
|
cursor: pointer;
|
}
|
}
|
|
.explanation-box {
|
animation: fadeIn 0.3s ease;
|
}
|
}
|
|
@keyframes fadeIn {
|
from {
|
opacity: 0;
|
transform: translateY(-10px);
|
}
|
to {
|
opacity: 1;
|
transform: translateY(0);
|
}
|
}
|
</style>
|