Merge branch 'main' of https://gitee.com/zzyyymm/super_video
No files matched your search
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
@@ -6,9 +6,12 @@
|
||||
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.GUIDE_DRAGON_BG) }"></div> -->
|
||||
|
||||
<!-- 导学小龙UI区域 -->
|
||||
<div class="guide-ui absolute left-16 top-1/2 transform -translate-y-1/2">
|
||||
<div class="guide-ui absolute left-16" :style="positionStyle">
|
||||
<!-- 主UI面板 -->
|
||||
<div class="guide-panel" :style="{ backgroundImage: getBackgroundImage('components/box') }">
|
||||
<div
|
||||
class="guide-panel bg-no-repeat"
|
||||
:style="{ backgroundImage: getBackgroundImage('components/box') }"
|
||||
>
|
||||
<!-- 标题区域 -->
|
||||
|
||||
<ImgButton
|
||||
@@ -48,7 +51,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 小龙角色区域 -->
|
||||
<div class="dragon-character absolute right-16 top-1/2 transform -translate-y-1/2">
|
||||
<div class="dragon-character absolute right-16" :style="positionStyle">
|
||||
<div class="dragon-container">
|
||||
<!-- 小龙角色图片 -->
|
||||
<img :src="getStaticImage(IMAGE_PATHS.GUIDE_PERSON)" alt="导学小龙" class="dragon-image" />
|
||||
@@ -60,6 +63,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { getStaticImage, getBackgroundImage, IMAGE_PATHS } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
|
||||
@@ -70,6 +74,8 @@ interface Props {
|
||||
currentStep?: number
|
||||
/** 总步骤数 */
|
||||
totalSteps?: number
|
||||
/** 垂直偏移(仅Y轴),支持 number(px) 或 '10%'/'120px' */
|
||||
positionOffsetY?: number | string
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -78,10 +84,11 @@ interface Emits {
|
||||
(e: 'complete'): void
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showSkip: true,
|
||||
currentStep: 1,
|
||||
totalSteps: 1
|
||||
totalSteps: 1,
|
||||
positionOffsetY: '50%'
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
@@ -99,6 +106,21 @@ const handleSkip = () => {
|
||||
const handleContinue = () => {
|
||||
emit('next')
|
||||
}
|
||||
|
||||
// 根据 position 计算垂直定位类
|
||||
const normalizeOffset = (value: number | string) => {
|
||||
if (typeof value === 'number') return `${value}px`
|
||||
return value
|
||||
}
|
||||
|
||||
const positionStyle = computed(() => {
|
||||
const y = normalizeOffset(props.positionOffsetY)
|
||||
// 百分比:按中心定位;px:按像素直接定位
|
||||
if (typeof props.positionOffsetY === 'string' && props.positionOffsetY.includes('%')) {
|
||||
return { top: y, transform: 'translateY(-50%)' }
|
||||
}
|
||||
return { top: y, transform: 'translateY(0)' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -166,11 +188,11 @@ const handleContinue = () => {
|
||||
}
|
||||
|
||||
.content-text {
|
||||
@apply text-white leading-relaxed font-bold text-4xl flex-1;
|
||||
@apply text-white leading-normal font-bold text-4xl flex-1;
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
@apply absolute bottom-6 right-6;
|
||||
@apply absolute bottom-6 right-10;
|
||||
}
|
||||
|
||||
.progress-dots {
|
||||
@@ -239,17 +261,17 @@ const handleContinue = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.dragon-character {
|
||||
.dragon-container {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(-50%) translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-50%) translateY(-10px);
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,7 @@
|
||||
:show-skip="currentStep < totalSteps"
|
||||
:current-step="currentStep"
|
||||
:total-steps="totalSteps"
|
||||
:position-offset-y="props.positionOffsetY"
|
||||
@next="handleNext"
|
||||
@skip="handleSkip"
|
||||
@complete="handleComplete"
|
||||
@@ -27,8 +28,16 @@
|
||||
:total-steps="totalSteps"
|
||||
/>
|
||||
</div>
|
||||
<!-- 兼容原有的配置形式 -->
|
||||
<div v-else v-html="currentStepContent"></div>
|
||||
<!-- 兼容原有的配置形式:也包裹 GuideStepsItem,获得继续按钮 -->
|
||||
<GuideStepsItem
|
||||
v-else
|
||||
:is-last-step="currentStep >= totalSteps"
|
||||
:current-step="currentStep"
|
||||
:total-steps="totalSteps"
|
||||
@continue="handleNext"
|
||||
>
|
||||
{{ currentStepContent }}
|
||||
</GuideStepsItem>
|
||||
</template>
|
||||
</GuideDragon>
|
||||
</div>
|
||||
@@ -40,6 +49,7 @@
|
||||
import { ref, computed, useSlots, onMounted, onUnmounted, reactive } from 'vue'
|
||||
import SpotlightOverlay from './SpotlightOverlay.vue'
|
||||
import GuideDragon from './GuideDragon.vue'
|
||||
import GuideStepsItem from './GuideStepsItem.vue'
|
||||
|
||||
interface GuideStep {
|
||||
id: string
|
||||
@@ -63,6 +73,8 @@ interface Props {
|
||||
highlightPadding?: number
|
||||
/** 高亮圆角,默认16 */
|
||||
highlightRadius?: number
|
||||
/** 垂直偏移(仅Y轴),支持 number(px) 或 '10%'/'120px' */
|
||||
positionOffsetY?: number | string
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -78,7 +90,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
autoPlayInterval: 0,
|
||||
highlightSelector: null,
|
||||
highlightPadding: 8,
|
||||
highlightRadius: 16
|
||||
highlightRadius: 16,
|
||||
positionOffsetY: '50%'
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
@@ -138,7 +151,7 @@ const currentStepContent = computed(() => {
|
||||
if (!step) return ''
|
||||
|
||||
if (!step.title) {
|
||||
return `<p>${step.content}</p>`
|
||||
return `${step.content}`
|
||||
}
|
||||
|
||||
return `
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span class="guide-steps-item">
|
||||
<slot />
|
||||
<ImgButton
|
||||
v-if="!isLastStep"
|
||||
v-if="currentStep !== totalSteps"
|
||||
:image="IMAGE_PATHS.BUTTONS.CONTINUE"
|
||||
class="continue-button"
|
||||
@click="handleContinue"
|
||||
|
||||
@@ -79,7 +79,7 @@ const borderRadius = props.borderRadius
|
||||
.spotlight-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
pointer-events: auto;
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface GuideDragonProps {
|
||||
showSkip?: boolean
|
||||
currentStep?: number
|
||||
totalSteps?: number
|
||||
/** 垂直偏移(仅Y轴),支持 number(px) 或 '10%'/'120px' */
|
||||
positionOffsetY?: number | string
|
||||
}
|
||||
|
||||
// 引导步骤组件Props类型
|
||||
@@ -19,6 +21,8 @@ export interface GuideStepsProps {
|
||||
autoStart?: boolean
|
||||
keyboardNavigation?: boolean
|
||||
autoPlayInterval?: number
|
||||
/** 垂直偏移(仅Y轴),支持 number(px) 或 '10%'/'120px' */
|
||||
positionOffsetY?: number | string
|
||||
}
|
||||
|
||||
// 引导组件事件类型
|
||||
|
||||
@@ -44,7 +44,14 @@ const routes: RouteRecordRaw[] = [
|
||||
title: '发现与记录'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: '/video',
|
||||
name: 'Video',
|
||||
component: () => import('@/views/vlog/video/index.vue'),
|
||||
meta: {
|
||||
title: '视频'
|
||||
}
|
||||
},
|
||||
//中期拍摄
|
||||
{
|
||||
path: '/midterm',
|
||||
|
||||
@@ -112,6 +112,7 @@ export const IMAGE_PATHS = {
|
||||
START: 'home/按钮/开始',
|
||||
COMPLETE: 'home/按钮/完成',
|
||||
CANCEL: 'home/按钮/取消',
|
||||
CLOSE: 'home/按钮/关闭',
|
||||
//继续 continue
|
||||
CONTINUE: 'components/continue',
|
||||
//跳过 escape
|
||||
@@ -123,6 +124,13 @@ export const IMAGE_PATHS = {
|
||||
//开始拍摄
|
||||
START_FILMING: 'home/guide/action-btn'
|
||||
},
|
||||
|
||||
//components
|
||||
COMPONENTS: {
|
||||
CLOSE: 'components/close',
|
||||
//返回
|
||||
BACK: 'components/back'
|
||||
},
|
||||
// ai工具组镜头按钮
|
||||
AI_TOOLS_BUTTONS: {
|
||||
BACK: 'home/ai工具组镜头/返回',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
|
||||
import { getBackgroundImage, getStaticImage, IMAGE_PATHS } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { ModuleSwitcher } from './components/ModuleSwitcher'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
@@ -121,6 +121,11 @@ onMounted(() => {
|
||||
class="absolute top-60 right-1/2 translate-x-1/2"
|
||||
@click="handleCloseGuide"
|
||||
/>
|
||||
<img
|
||||
class="absolute bottom-0 left-1/2 -translate-x-1/2"
|
||||
:src="getStaticImage('home/新手导航')"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- <GuideSteps @complete="handleComplete" @skip="handleSkip">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<img src="@/assets/images/equipment/选中按钮.png" alt="" />
|
||||
<img src="@/assets/images/equipment/手机-亮.png" alt="" />
|
||||
</span>
|
||||
<span>手机支架</span>
|
||||
</div>
|
||||
<!-- 设备准备按钮 -->
|
||||
<div class="equipment-bt">
|
||||
@@ -21,14 +22,58 @@
|
||||
:class="hiddenbar ? 'equipment-unfold1' : 'equipment-unfold2'"
|
||||
@click="hiddenbarclick()"
|
||||
>
|
||||
<img src="@/assets/images/equipment/按钮-展开.png" alt="" />
|
||||
<img
|
||||
:src="
|
||||
hiddenbar
|
||||
? '/src/assets/images/equipment/按钮-展开.png'
|
||||
: '/src/assets/images/equipment/按钮-收缩.png'
|
||||
"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="equipment-layout">
|
||||
<div class="equipment-home">
|
||||
<div
|
||||
class="equipment-leftbutton"
|
||||
@click="prevPhone"
|
||||
:style="{ visibility: currentIndex > 0 ? 'visible' : 'hidden' }"
|
||||
>
|
||||
<img src="@/assets/images/equipment/按钮-左滑.png" alt="" />
|
||||
</div>
|
||||
<div class="equipment-phoneImg">
|
||||
<img :src="phoneList[currentIndex].phoneImg" alt="" />
|
||||
</div>
|
||||
<div
|
||||
class="equipment-rightbutton"
|
||||
@click="nextPhone"
|
||||
:style="{ visibility: currentIndex < phoneList.length - 1 ? 'visible' : 'hidden' }"
|
||||
>
|
||||
<img src="@/assets/images/equipment/按钮-右滑.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="equipment-purpose">
|
||||
<div class="plate">
|
||||
<img src="@/assets/images/equipment/浮空屏.png" alt="" />
|
||||
</div>
|
||||
<div class="title">
|
||||
<img src="@/assets/images/equipment/标题-安卓小米.png" alt="" />
|
||||
<!-- <img src="@/assets/images/equipment/标题-苹果.png" alt=""> -->
|
||||
</div>
|
||||
<div class="text">
|
||||
<img src="@/assets/images/equipment/内容-安卓小米.png" alt="" />
|
||||
<!-- <img src="@/assets/images/equipment/内容-苹果.png" alt=""> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引导步骤 -->
|
||||
<GuideSteps
|
||||
@complete="handleComplete"
|
||||
@skip="handleSkip"
|
||||
:keyboard-navigation="true"
|
||||
position-offset-y="85%"
|
||||
:steps="steps"
|
||||
:highlight-selector="currentHighlight"
|
||||
:highlight-padding="12"
|
||||
:highlight-radius="20"
|
||||
@step-change="handleStepChange"
|
||||
>
|
||||
<GuideStepsItem>
|
||||
<img src="@/assets/images/equipment/设备准备引导1.png" alt="" />
|
||||
@@ -41,33 +86,73 @@
|
||||
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
|
||||
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
|
||||
|
||||
import { ref } from 'vue'
|
||||
const hiddenbar = ref()
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const steps = [
|
||||
{ id: 'step1', content: '先认识 Vlog:点击右侧「1.vlog初识」继续' },
|
||||
{ id: 'step2', content: '这是主要内容区域的边框' }
|
||||
]
|
||||
const stepIndex = ref(0)
|
||||
const hiddenbar = ref(false)
|
||||
const currentIndex = ref(0) // 当前显示的图片索引
|
||||
|
||||
const phoneList = ref([
|
||||
{ phoneImg: '/src/assets/images/equipment/设备-手机1.png' },
|
||||
{ phoneImg: '/src/assets/images/equipment/设备-手机2.png' }
|
||||
])
|
||||
|
||||
const hiddenbarclick = () => {
|
||||
hiddenbar.value = !hiddenbar.value
|
||||
}
|
||||
|
||||
const handleComplete = () => {
|
||||
console.log('引导完成')
|
||||
// 下一张图片
|
||||
const nextPhone = () => {
|
||||
if (currentIndex.value < phoneList.value.length - 1) {
|
||||
currentIndex.value++
|
||||
}
|
||||
}
|
||||
|
||||
const handleSkip = () => {
|
||||
console.log('跳过引导')
|
||||
// 上一张图片
|
||||
const prevPhone = () => {
|
||||
if (currentIndex.value > 0) {
|
||||
currentIndex.value--
|
||||
}
|
||||
}
|
||||
|
||||
interface SimpleStep {
|
||||
id: string
|
||||
content: string
|
||||
}
|
||||
|
||||
const handleStepChange = (_step: SimpleStep | null, index: number) => {
|
||||
stepIndex.value = index
|
||||
}
|
||||
|
||||
const currentHighlight = computed(() =>
|
||||
stepIndex.value === 0 ? '.img-switch-item--vlog' : '.work-border'
|
||||
)
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
<style lang="less">
|
||||
.equipment-index {
|
||||
// display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.equipment-bg {
|
||||
display: flex;
|
||||
// display: flex;
|
||||
}
|
||||
|
||||
.equipment-layout {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.equipment-bar {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
|
||||
.equipment-bt {
|
||||
position: absolute;
|
||||
@@ -83,29 +168,27 @@ const handleSkip = () => {
|
||||
|
||||
.equipment-unfold1 {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
right: -110px;
|
||||
top: 490px;
|
||||
left: -41px;
|
||||
width: 150px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
/* 确保图片按比例缩放 */
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-unfold2 {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
right: -69px;
|
||||
top: 490px;
|
||||
left: 268px;
|
||||
width: 150px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
/* 确保图片按比例缩放 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,5 +202,88 @@ const handleSkip = () => {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.guide-ui {
|
||||
top: 80% !important;
|
||||
}
|
||||
|
||||
.dragon-character {
|
||||
top: 80% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-home {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.equipment-leftbutton {
|
||||
position: absolute;
|
||||
top: 511px;
|
||||
left: 350px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.equipment-phoneImg {
|
||||
position: absolute;
|
||||
top: 313px;
|
||||
left: 500px;
|
||||
width: 446px;
|
||||
height: 484px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-rightbutton {
|
||||
position: absolute;
|
||||
top: 511px;
|
||||
right: 770px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.equipment-purpose {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 1200px;
|
||||
}
|
||||
|
||||
.text {
|
||||
position: absolute;
|
||||
top: 74px;
|
||||
left: 1292px;
|
||||
width: 364px;
|
||||
}
|
||||
|
||||
.plate {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 1200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,11 @@ const handleWorkClick = () => {
|
||||
// 处理任务取景按钮点击逻辑
|
||||
router.push({ name: 'Work' })
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
// 处理任务取景按钮点击逻辑
|
||||
router.push({ name: 'equipment' })
|
||||
}
|
||||
const highlightShow = ref(true)
|
||||
</script>
|
||||
|
||||
@@ -59,7 +64,10 @@ const highlightShow = ref(true)
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.Midterm" />
|
||||
</div>
|
||||
<!-- 进度百分比 -->
|
||||
<div v-if="highlightShow" class="absolute left-1/2 top-[12%] -translate-x-1/2">
|
||||
<div
|
||||
v-if="highlightShow"
|
||||
class="absolute left-1/2 top-[12%] -translate-x-1/2 pointer-events-none"
|
||||
>
|
||||
<div class="flex">
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.Progress" />
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.Four" />
|
||||
@@ -73,7 +81,11 @@ const highlightShow = ref(true)
|
||||
class="absolute left-[21%] top-[9%] -translate-x-1/2"
|
||||
style="z-index: 998"
|
||||
>
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.MidtermEquipment" class="ml-[60px]" />
|
||||
<ImgButton
|
||||
:image="IMAGE_PATHS.VLOG.MidtermEquipment"
|
||||
@click="handleClick"
|
||||
class="ml-[60px]"
|
||||
/>
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.MidtermFraming" class="ml-[40px]" />
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.MidtermShoot" class="ml-[20px]" />
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ProgressBar } from '@/components'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { GuideSteps } from '@/components/Guide'
|
||||
type WorkTab = 'vlog' | 'case' | 'profession' | 'idea'
|
||||
const steps = [
|
||||
{
|
||||
id: 'step1',
|
||||
@@ -115,7 +114,11 @@ const highlightShow = ref(true)
|
||||
:highlight-padding="12"
|
||||
:highlight-radius="20"
|
||||
@step-change="handleStepChangeGui"
|
||||
/>
|
||||
>
|
||||
<GuideStepsItem>
|
||||
<img src="@/assets/images/midterm/前期准备引导1.png" alt="" />
|
||||
</GuideStepsItem>
|
||||
</GuideSteps>
|
||||
</div>
|
||||
<div
|
||||
v-if="highlightShow"
|
||||
@@ -130,6 +133,7 @@ const highlightShow = ref(true)
|
||||
.guide-ui {
|
||||
top: 80% !important;
|
||||
}
|
||||
|
||||
.dragon-character {
|
||||
top: 80% !important;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ProgressBar } from '@/components'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const currentStep = ref(0)
|
||||
|
||||
@@ -25,6 +26,12 @@ const handleWorkClick = () => {
|
||||
// 处理任务取景按钮点击逻辑
|
||||
router.push({ name: 'Work' })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.key === 'video') {
|
||||
currentStep.value = 1
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -51,7 +58,10 @@ const handleWorkClick = () => {
|
||||
class="absolute left-24 top-24"
|
||||
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.TASK_CAMERA) }"
|
||||
>
|
||||
<ImgButton :image="IMAGE_PATHS.VLOG.TASK_BTN" @click="handleWorkClick" />
|
||||
<ImgButton
|
||||
:image="currentStep === 1 ? IMAGE_PATHS.VLOG.TASK_BTN_OK : IMAGE_PATHS.VLOG.TASK_BTN"
|
||||
@click="handleWorkClick"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 引导任务 -->
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import ImgButton from '@/components/Button/ImgButton.vue'
|
||||
import { GuideSteps } from '@/components/Guide'
|
||||
import { getStaticImage, IMAGE_PATHS } from '@/utils'
|
||||
import second from '/video/Vlog模块_任务取景框_专业剖析_基础景别运用.mp4'
|
||||
import third from '/video/Vlog模块_任务取景框_专业剖析_画面构图.mp4'
|
||||
import fourth from '/video/Vlog模块_任务取景框_专业剖析_脚本策划.mp4'
|
||||
import five from '/video/Vlog模块_任务取景框_专业剖析_动态事件跟拍与抓拍.mp4'
|
||||
import six from '/video/Vlog模块_任务取景框_专业剖析_素材导入、剪辑拼接.mp4'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const videoRef = ref()
|
||||
const status = ref(false)
|
||||
|
||||
//引导步骤
|
||||
const steps = ref([
|
||||
{
|
||||
id: 'step1',
|
||||
content: '试着用这些方法记录身边的小事,你会发现,原来平凡的日子里有这么多值得捕捉的瞬间。'
|
||||
}
|
||||
])
|
||||
|
||||
// 视频列表
|
||||
const videoList = [second, third, fourth, five, six]
|
||||
const currentVideoIndex = ref(0)
|
||||
|
||||
//按顺序播放
|
||||
const currentVideo = computed(() => videoList[currentVideoIndex.value])
|
||||
const handleVideoChange = () => {
|
||||
if (currentVideoIndex.value < videoList.length - 1) {
|
||||
currentVideoIndex.value++
|
||||
} else {
|
||||
status.value = true
|
||||
steps.value = [
|
||||
{
|
||||
id: 'step1',
|
||||
content: '已完成专业剖析,即可查看脚本'
|
||||
}
|
||||
]
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
if (!videoRef.value) return
|
||||
videoRef.value.muted = false
|
||||
videoRef.value.volume = 1
|
||||
videoRef.value.play()
|
||||
})
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
const image = computed(() => route.query.image as string)
|
||||
const key = computed(() => route.query.key as string)
|
||||
|
||||
const handleClick = () => {
|
||||
if (!videoRef.value) return
|
||||
videoRef.value.muted = false
|
||||
videoRef.value.volume = 1
|
||||
}
|
||||
|
||||
const handlePlay = () => {
|
||||
if (!videoRef.value) return
|
||||
videoRef.value.muted = false
|
||||
videoRef.value.volume = 1
|
||||
}
|
||||
|
||||
const handleComplete = () => {
|
||||
if (key.value === 'profession') {
|
||||
router.push({
|
||||
name: 'Work',
|
||||
query: {
|
||||
key: 'work'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
router.push({
|
||||
name: 'midterm',
|
||||
query: {
|
||||
key: 'midterm'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (key.value === 'idea') {
|
||||
status.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full relative bg-white/10" @click="handleClick">
|
||||
<ImgButton class="absolute left-4 top-4 z-10" :image="image"></ImgButton>
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0 z-10"
|
||||
:image="IMAGE_PATHS.COMPONENTS.BACK"
|
||||
@click="handleClose"
|
||||
/>
|
||||
<div v-if="key === 'vlog'" :src="image">
|
||||
<img :src="getStaticImage('work/vlog背景全屏待定')" alt="" />
|
||||
</div>
|
||||
<video
|
||||
ref="videoRef"
|
||||
v-else-if="key === 'case'"
|
||||
src="/video/案例赏析-美食小确幸:桂花酒酿小圆子.mp4"
|
||||
controls
|
||||
@play="handlePlay"
|
||||
></video>
|
||||
<!-- 专业剖析 -->
|
||||
<video
|
||||
ref="videoRef"
|
||||
v-else-if="key === 'profession'"
|
||||
:src="currentVideo"
|
||||
controls
|
||||
@play="handlePlay"
|
||||
@ended="handleVideoChange"
|
||||
></video>
|
||||
<div v-else-if="key === 'idea'" :src="image">
|
||||
<img :src="getStaticImage('work/思路提点全屏')" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引导与高亮 -->
|
||||
<GuideSteps v-if="status" :steps="steps" position-offset-y="85%" @complete="handleComplete" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* 核心:保持视频比例,多余部分被裁剪 */
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
@@ -44,4 +44,8 @@ const items = [
|
||||
]
|
||||
|
||||
const onChange = (val: string) => emit('change', val as WorkTab)
|
||||
|
||||
defineExpose({
|
||||
items
|
||||
})
|
||||
</script>
|
||||
@@ -1,20 +1,61 @@
|
||||
<script lang="tsx" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { getBackgroundImage, IMAGE_PATHS } from '@/utils'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getBackgroundImage, IMAGE_PATHS, getStaticImage } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import WorkSideSwitcher from './components/WorkSideSwitcher.vue'
|
||||
import { GuideSteps } from '@/components/Guide'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
type WorkTab = 'vlog' | 'case' | 'profession' | 'idea'
|
||||
|
||||
const selected = ref<WorkTab>('vlog')
|
||||
const stepIndex = ref(0)
|
||||
const workSideSwitcherRef = ref<InstanceType<typeof WorkSideSwitcher>>()
|
||||
|
||||
const status = ref(false)
|
||||
|
||||
//引导步骤
|
||||
const steps = [
|
||||
{ id: 'step1', content: '先认识 Vlog:点击右侧「1.vlog初识」继续' },
|
||||
{ id: 'step2', content: '这是主要内容区域的边框' }
|
||||
{
|
||||
id: 'step1',
|
||||
content:
|
||||
'现在我们要一起走进 Vlog 的世界。简单说,Vlog 就是用视频记录生活的一种方式 —— 它像一本会动的日记,能把我们身边的小事、瞬间的心情,通过镜头变得生动又具体。'
|
||||
},
|
||||
{ id: 'step2', content: '接下来,我们就通过一个具体的案例,看看 Vlog 是怎么“捕捉美好” 的吧。' }
|
||||
]
|
||||
|
||||
//切换图片
|
||||
const sideSwitcherItems = ref([
|
||||
{
|
||||
key: 'vlog',
|
||||
description: '记录身边的美好',
|
||||
image: 'work/vlog初识框'
|
||||
},
|
||||
{
|
||||
key: 'case',
|
||||
description: '美食小确幸:桂花酒酿小圆子',
|
||||
image: 'work/案例背景待定'
|
||||
},
|
||||
{
|
||||
key: 'profession',
|
||||
description: '美食小确幸:桂花酒酿小圆子',
|
||||
image: 'work/案例背景待定'
|
||||
},
|
||||
{
|
||||
key: 'idea',
|
||||
description: '美食小确幸:桂花酒酿小圆子',
|
||||
image: 'work/思路提点小屏'
|
||||
}
|
||||
])
|
||||
|
||||
//当前选中的item
|
||||
const currentItem = computed(() => {
|
||||
return sideSwitcherItems.value.find(item => item.key === selected.value)
|
||||
})
|
||||
|
||||
const currentHighlight = computed(() =>
|
||||
stepIndex.value === 0 ? '.img-switch-item--vlog' : '.work-border'
|
||||
)
|
||||
@@ -26,6 +67,38 @@ interface SimpleStep {
|
||||
const handleStepChange = (_step: SimpleStep | null, index: number) => {
|
||||
stepIndex.value = index
|
||||
}
|
||||
|
||||
const handleSideSwitcherChange = (value: WorkTab) => {
|
||||
if (selected.value === value) return
|
||||
}
|
||||
|
||||
const handleImageClick = () => {
|
||||
router.push({
|
||||
name: 'Video',
|
||||
query: {
|
||||
image:
|
||||
workSideSwitcherRef.value?.items.find(item => item.key === selected.value)?.activeImage ||
|
||||
'',
|
||||
key: selected.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
//状态是否完成前置
|
||||
const isComplete = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.key === 'work') {
|
||||
selected.value = 'profession'
|
||||
isComplete.value = true
|
||||
} else {
|
||||
status.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -36,6 +109,13 @@ const handleStepChange = (_step: SimpleStep | null, index: number) => {
|
||||
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.WORK.BG) }"
|
||||
></div>
|
||||
|
||||
<!-- 关闭 -->
|
||||
<ImgButton
|
||||
class="absolute right-0 top-0"
|
||||
:image="IMAGE_PATHS.COMPONENTS.CLOSE"
|
||||
@click="handleClose"
|
||||
/>
|
||||
|
||||
<!-- border图片 -->
|
||||
<div
|
||||
class="work-border absolute top-[140px] left-24 inset-0 bg-cover bg-center bg-no-repeat z-10 w-[1360px] h-[800px] p-10"
|
||||
@@ -48,31 +128,42 @@ const handleStepChange = (_step: SimpleStep | null, index: number) => {
|
||||
<span
|
||||
class="mt-24 text-white font-bold text-6xl [text-shadow:0_2px_4px_rgba(0,0,0,0.2),0_0_6px_rgba(0,207,255,0.9),0_0_14px_rgba(0,207,255,0.7)]"
|
||||
>
|
||||
记录身边的美好</span
|
||||
{{ currentItem?.description }}</span
|
||||
>
|
||||
</div>
|
||||
<img class="rounded-[62px]" src="@/assets/images/work/image1.png" alt="" />
|
||||
<img
|
||||
@click="handleImageClick"
|
||||
class="rounded-[62px] cursor-pointer"
|
||||
:src="getStaticImage(currentItem?.image || '')"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 左侧选项切换 -->
|
||||
<div class="absolute right-4 top-32 z-20">
|
||||
<WorkSideSwitcher
|
||||
ref="workSideSwitcherRef"
|
||||
v-model="selected"
|
||||
@change="() => {}"
|
||||
@change="handleSideSwitcherChange"
|
||||
:item-class="'w-[462px] h-[142px] '"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 任务取景按钮 -->
|
||||
<ImgButton class="absolute right-14 bottom-10" :image="IMAGE_PATHS.WORK.TASK_BTN" />
|
||||
<ImgButton
|
||||
class="absolute right-14 bottom-10"
|
||||
:image="isComplete ? IMAGE_PATHS.WORK.TASK_BTN_OK : IMAGE_PATHS.WORK.TASK_BTN"
|
||||
/>
|
||||
|
||||
<!-- 引导与高亮 -->
|
||||
<GuideSteps
|
||||
v-if="status"
|
||||
:steps="steps"
|
||||
:highlight-selector="currentHighlight"
|
||||
:highlight-padding="12"
|
||||
:highlight-radius="20"
|
||||
@step-change="handleStepChange"
|
||||
position-offset-y="85%"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||