473 lines
10 KiB
Vue
473 lines
10 KiB
Vue
<template>
|
||
<div v-if="show" class="guide-modal-overlay" @click="handleOverlayClick">
|
||
<div class="guide-modal-container" @click.stop>
|
||
<!-- 关闭按钮 -->
|
||
<button class="close-button" @click="closeModal" :title="t('header.skipGuide')">
|
||
<span class="close-icon">×</span>
|
||
</button>
|
||
|
||
<!-- 进度指示器 -->
|
||
<div class="progress-indicator">
|
||
<div
|
||
v-for="(step, index) in guideSteps"
|
||
:key="index"
|
||
class="progress-dot"
|
||
:class="{ active: index === currentStep, completed: index < currentStep }"
|
||
></div>
|
||
</div>
|
||
|
||
<!-- 引导内容区域 -->
|
||
<div class="guide-content">
|
||
<!-- 添加轮播容器 -->
|
||
<div class="guide-content-wrapper" :style="{ transform: `translateX(-${currentStep * 100}%)` }">
|
||
<div v-for="(step, index) in guideSteps" :key="index" class="guide-step">
|
||
<!-- 左侧图片区域 -->
|
||
<div class="guide-image-container">
|
||
<div class="image-wrapper">
|
||
<img
|
||
:src="step.image"
|
||
:alt="step.title"
|
||
class="guide-image"
|
||
/>
|
||
<div class="image-decoration"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧文字描述区域 -->
|
||
<div class="guide-text-container">
|
||
<div class="text-content">
|
||
<h2 class="guide-title">{{ step.title }}</h2>
|
||
<p class="guide-description">{{ step.description }}</p>
|
||
|
||
<!-- 额外提示信息 -->
|
||
<div v-if="step.tips" class="guide-tips">
|
||
<div class="tips-icon">💡</div>
|
||
<div class="tips-text">{{ step.tips }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 按钮区域 -->
|
||
<div class="guide-actions">
|
||
<!-- 上一步按钮 -->
|
||
<button
|
||
v-if="currentStep > 0"
|
||
class="action-button secondary"
|
||
@click="prevStep"
|
||
>
|
||
{{ t('header.previous') }}
|
||
</button>
|
||
|
||
<!-- 跳过按钮 -->
|
||
<button
|
||
class="action-button skip"
|
||
@click="skipGuide"
|
||
>
|
||
{{ t('header.skipGuide') }}
|
||
</button>
|
||
|
||
<!-- 下一步/完成按钮 -->
|
||
<button
|
||
class="action-button primary"
|
||
@click="nextStep"
|
||
>
|
||
{{ isLastStep ? t('header.startCreating') : t('header.next') }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步骤指示器 -->
|
||
<div class="step-indicator">
|
||
<span class="step-text">{{ t('header.step') }} {{ currentStep + 1 }} / {{ guideSteps.length }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
import { useI18n } from 'vue-i18n';
|
||
|
||
// 定义props
|
||
const props = defineProps({
|
||
show: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
// 定义emits
|
||
const emit = defineEmits(['close', 'complete']);
|
||
|
||
// 国际化
|
||
const { t } = useI18n();
|
||
|
||
// 当前步骤索引
|
||
const currentStep = ref(0);
|
||
|
||
// 引导步骤数据
|
||
const guideSteps = computed(() => [
|
||
{
|
||
id: 1,
|
||
title: t('guideModal.step1.title'),
|
||
description: t('guideModal.step1.description'),
|
||
image: new URL('@/assets/step/creatProject/step1.png', import.meta.url).href,
|
||
tips: t('guideModal.step1.tips')
|
||
},
|
||
{
|
||
id: 2,
|
||
title: t('guideModal.step2.title'),
|
||
description: t('guideModal.step2.description'),
|
||
image: new URL('@/assets/step/creatProject/step2.png', import.meta.url).href,
|
||
tips: t('guideModal.step2.tips')
|
||
},
|
||
{
|
||
id: 3,
|
||
title: t('guideModal.step3.title'),
|
||
description: t('guideModal.step3.description'),
|
||
image: new URL('@/assets/step/creatProject/step3.png', import.meta.url).href,
|
||
tips: t('guideModal.step3.tips')
|
||
},
|
||
{
|
||
id: 4,
|
||
title: t('guideModal.step4.title'),
|
||
description: t('guideModal.step4.description'),
|
||
image: new URL('@/assets/step/creatProject/step4.png', import.meta.url).href,
|
||
tips: t('guideModal.step4.tips')
|
||
}
|
||
]);
|
||
|
||
// 计算属性:当前步骤数据
|
||
const currentStepData = computed(() => {
|
||
return guideSteps.value[currentStep.value] || {};
|
||
});
|
||
|
||
// 计算属性:是否为最后一步
|
||
const isLastStep = computed(() => {
|
||
return currentStep.value === guideSteps.value.length - 1;
|
||
});
|
||
|
||
// 方法:上一步
|
||
const prevStep = () => {
|
||
if (currentStep.value > 0) {
|
||
currentStep.value--;
|
||
}
|
||
};
|
||
|
||
// 方法:下一步
|
||
const nextStep = () => {
|
||
if (currentStep.value < guideSteps.value.length - 1) {
|
||
currentStep.value++;
|
||
} else {
|
||
// 如果是最后一步,完成引导
|
||
completeGuide();
|
||
}
|
||
};
|
||
|
||
// 方法:关闭弹窗
|
||
const closeModal = () => {
|
||
emit('close');
|
||
};
|
||
|
||
// 方法:完成引导
|
||
const completeGuide = () => {
|
||
emit('complete');
|
||
closeModal();
|
||
};
|
||
|
||
// 方法:跳过引导
|
||
const skipGuide = () => {
|
||
closeModal();
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.guide-modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.4);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
backdrop-filter: blur(3px);
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.guide-modal-container {
|
||
background: linear-gradient(135deg, rgba(107, 70, 193, 0.85) 0%, rgba(147, 51, 234, 0.85) 100%);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 20px;
|
||
width: 90%;
|
||
max-width: 900px;
|
||
max-height: 85vh;
|
||
overflow: hidden;
|
||
position: relative;
|
||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.3);
|
||
display: flex;
|
||
flex-direction: column;
|
||
animation: slideUp 0.4s ease-out;
|
||
color: white;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(30px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.close-button {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background-color: rgba(255, 255, 255, 0.15);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
z-index: 10;
|
||
}
|
||
|
||
.close-button:hover {
|
||
background-color: rgba(255, 255, 255, 0.25);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.close-icon {
|
||
font-size: 24px;
|
||
line-height: 1;
|
||
font-weight: 300;
|
||
}
|
||
|
||
.progress-indicator {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 20px 0 10px;
|
||
gap: 8px;
|
||
}
|
||
|
||
.progress-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background-color: rgba(255, 255, 255, 0.3);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.progress-dot.active {
|
||
width: 24px;
|
||
border-radius: 4px;
|
||
background-color: white;
|
||
}
|
||
|
||
.progress-dot.completed {
|
||
background-color: rgba(255, 255, 255, 0.7);
|
||
}
|
||
|
||
.guide-content {
|
||
display: flex;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
/* 添加轮播容器 */
|
||
.guide-content-wrapper {
|
||
display: flex;
|
||
width: 100%;
|
||
height: 100%;
|
||
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||
}
|
||
|
||
.guide-step {
|
||
min-width: 100%;
|
||
display: flex;
|
||
flex: 1;
|
||
}
|
||
|
||
.guide-image-container {
|
||
flex: 1;
|
||
padding: 0 20px 20px 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.image-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
max-height: 350px;
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.guide-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
transition: transform 0.5s ease;
|
||
}
|
||
|
||
.image-wrapper:hover .guide-image {
|
||
transform: scale(1.03);
|
||
}
|
||
|
||
.image-decoration {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(45deg, rgba(107, 70, 193, 0.2) 0%, rgba(147, 51, 234, 0.2) 100%);
|
||
}
|
||
|
||
.guide-text-container {
|
||
flex: 1;
|
||
padding: 0 40px 20px 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.text-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.guide-title {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
margin: 0 0 16px;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.guide-description {
|
||
font-size: 16px;
|
||
line-height: 1.6;
|
||
margin: 0 0 20px;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.guide-tips {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
background-color: rgba(255, 255, 255, 0.15);
|
||
border-radius: 12px;
|
||
padding: 12px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.tips-icon {
|
||
font-size: 18px;
|
||
margin-right: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tips-text {
|
||
font-size: 14px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.guide-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.action-button {
|
||
padding: 12px 24px;
|
||
border-radius: 8px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.action-button.primary {
|
||
background-color: rgba(255, 255, 255, 0.9);
|
||
color: #6B46C1;
|
||
}
|
||
|
||
.action-button.primary:hover {
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.action-button.secondary {
|
||
background-color: rgba(255, 255, 255, 0.15);
|
||
color: white;
|
||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.action-button.secondary:hover {
|
||
background-color: rgba(255, 255, 255, 0.25);
|
||
}
|
||
|
||
.action-button.skip {
|
||
background-color: transparent;
|
||
color: rgba(255, 255, 255, 0.7);
|
||
margin-right: auto;
|
||
}
|
||
|
||
.action-button.skip:hover {
|
||
color: white;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.step-indicator {
|
||
text-align: center;
|
||
padding: 16px 0;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.step-text {
|
||
font-size: 14px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.guide-step {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.guide-image-container,
|
||
.guide-text-container {
|
||
padding: 0 20px 20px;
|
||
}
|
||
|
||
.guide-title {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.guide-actions {
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.action-button.skip {
|
||
margin-right: 0;
|
||
margin-bottom: 8px;
|
||
}
|
||
}
|
||
</style> |