Files
super_video/src/views/vlog/midterm-shooting/equipment/index.vue
T

567 lines
13 KiB
Vue

<script lang="tsx" setup>
import { getBackgroundImage, getPublicPath, getStaticImage, IMAGE_PATHS } from '@/utils'
import { GuideSteps } from '@/components/Guide'
import { onBeforeUnmount, ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { ImgButton } from '@/components/Button'
const router = useRouter()
const back = () => {
// 跳转设备准备
router.back()
}
const steps = ref([
{
id: 'step1',
content:
'设备准备妥当后,我们就该走进真实场景,开启实地取景的环节啦,一起去捕捉那些适合拍摄的画面吧!',
audioSrc: getPublicPath('audio/py_2_013.MP3')
}
])
const showGuide = ref(false)
const guideTriggered = ref(false)
const guideTimer = ref<ReturnType<typeof setTimeout> | null>(null)
const viewedMenuKeys = ref<Set<string>>(new Set())
const hiddenbar = ref(false)
const currentIndex = ref(0) // 当前显示的图片索引
const selectedMenuIndex = ref(0) // 当前选中的菜单项索引
type EquipmentCategoryId = 'phone' | 'phone-stabilizer' | 'wireless-lavalier-mic'
interface EquipmentProduct {
img: string
}
interface EquipmentCategory {
id: EquipmentCategoryId
name: string
disabled: boolean
titleImg: string
contentImg: string
products: EquipmentProduct[]
}
const categories = ref<EquipmentCategory[]>([
{
id: 'phone',
name: '手机',
disabled: false,
titleImg: getStaticImage('equipment/标题-苹果.png'),
contentImg: getStaticImage('equipment/内容-苹果.png'),
products: [
{ img: getStaticImage('equipment/设备-手机1.png') },
{ img: getStaticImage('equipment/设备-手机2.png') }
]
},
{
id: 'phone-stabilizer',
name: '手机稳定器',
disabled: false,
titleImg: getStaticImage('equipment/手机稳定器标题.png'),
contentImg: getStaticImage('equipment/手机稳定器图文.png'),
products: [{ img: getStaticImage('equipment/手机稳定器.png') }]
},
{
id: 'wireless-lavalier-mic',
name: '无线领夹麦克风',
disabled: false,
titleImg: getStaticImage('equipment/无线领夹麦克风标题.png'),
contentImg: getStaticImage('equipment/无线领夹麦克风图文.png'),
products: [{ img: getStaticImage('equipment/无线领夹麦克风.png') }]
}
])
categories.value = categories.value.map(c => ({
...c,
disabled: (c.products?.length ?? 0) === 0
}))
// 默认选中的菜单也算作已查看
if (categories.value[0] && !categories.value[0].disabled) {
viewedMenuKeys.value.add(categories.value[0].id)
}
const maybeTriggerGuide = () => {
if (guideTriggered.value) return
const enabledIds = categories.value.filter(c => !c.disabled).map(c => c.id)
if (enabledIds.length === 0) return
const allViewed = enabledIds.every(id => viewedMenuKeys.value.has(id))
if (!allViewed) return
guideTriggered.value = true
guideTimer.value = setTimeout(() => {
showGuide.value = true
guideTimer.value = null
}, 3000)
}
onBeforeUnmount(() => {
if (guideTimer.value) {
clearTimeout(guideTimer.value)
guideTimer.value = null
}
})
const currentCategory = computed(
() => categories.value[selectedMenuIndex.value] ?? categories.value[0]
)
const currentProducts = computed(() => currentCategory.value?.products ?? [])
const currentTitleImg = computed(() => currentCategory.value?.titleImg ?? '')
const currentContentImg = computed(() => currentCategory.value?.contentImg ?? '')
const hiddenbarclick = () => {
hiddenbar.value = !hiddenbar.value
}
// 选择菜单项
const selectMenuItem = (index: number, item: { id: string; name: string; disabled: boolean }) => {
if (item.disabled) {
return
}
selectedMenuIndex.value = index
currentIndex.value = 0
viewedMenuKeys.value.add(item.id)
maybeTriggerGuide()
}
// 下一张图片
const nextPhone = () => {
if (currentIndex.value < currentProducts.value.length - 1) {
currentIndex.value++
}
}
// 上一张图片
const prevPhone = () => {
if (currentIndex.value > 0) {
currentIndex.value--
}
}
</script>
<template>
<div class="equipment-index overflow-hidden">
<!-- 背景图片 -->
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat equipment-bg"
:style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.EQUIPMENT.BG) }"
></div>
<div class="equipment-bar">
<img src="@/assets/images/equipment/侧边导航栏.png" alt="" :hidden="hiddenbar" />
<!-- 内容列表 -->
<div class="equipment-item" :hidden="hiddenbar">
<div class="menu-list">
<div
v-for="(item, index) in categories"
:key="item.id"
class="menu-item"
:class="{
'menu-item--active': selectedMenuIndex === index,
'menu-item--disabled': item.disabled
}"
@click="selectMenuItem(index, item)"
>
<div
class="menu-item__indicator"
v-if="selectedMenuIndex === index && !item.disabled"
></div>
<span class="menu-item__text">{{ item.name }}</span>
<div class="menu-item__status" v-if="item.disabled">
<!-- <span class="status-text">未解锁</span> -->
</div>
</div>
</div>
</div>
<!-- 设备准备按钮 -->
<div class="equipment-bt">
<img src="@/assets/images/equipment/按钮选中-设备准备.png" alt="" />
</div>
<div
:class="['cursor-pointer', hiddenbar ? 'equipment-unfold1' : 'equipment-unfold2']"
@click="hiddenbarclick()"
>
<img
:src="
hiddenbar
? getStaticImage('equipment/按钮-展开.png')
: getStaticImage('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="currentProducts[currentIndex]?.img" alt="" />
</div>
<div
class="equipment-rightbutton"
@click="nextPhone"
:style="{ visibility: currentIndex < currentProducts.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="currentTitleImg" alt="" />
</div>
<div class="plate-content">
<div class="text">
<img :src="currentContentImg" alt="" />
</div>
</div>
</div>
</div>
<ImgButton class="absolute right-0 top-0" :image="IMAGE_PATHS.COMPONENTS.BACK" @click="back" />
<!-- 调试按钮组 -->
<!-- <div class="debug-controls">
<button @click="enableAllMenuItems" class="debug-btn">启用全部</button>
<button @click="disableAllMenuItems" class="debug-btn">禁用全部</button>
<button @click="toggleMenuItem('bluetooth-remote')" class="debug-btn">切换蓝牙遥控器</button>
</div> -->
<!-- 引导步骤 -->
<GuideSteps
:visible="showGuide"
:steps="steps"
:highlight-padding="12"
:highlight-radius="20"
position-offset-y="85%"
/>
</div>
</template>
<style lang="less" scoped>
.equipment-index {
position: relative;
width: 100%;
height: 100%;
}
.equipment-layout {
position: relative;
width: 100%;
height: 100%;
}
.equipment-bar {
position: absolute;
top: 0;
left: 0;
z-index: 10;
.equipment-bt {
position: absolute;
top: 0;
left: -135px;
width: 577px;
img {
width: 100%;
height: 100%;
}
}
.equipment-unfold1 {
position: absolute;
top: 490px;
left: -41px;
width: 150px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.equipment-unfold2 {
position: absolute;
top: 490px;
left: 268px;
width: 150px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.equipment-item {
position: absolute;
top: 0;
left: 0;
padding-top: 220px;
padding-left: 20px;
.menu-list {
display: flex;
flex-direction: column;
gap: 8px;
//background: linear-gradient(135deg, #1a2a4a 0%, #0f1a2e 100%);
border-radius: 12px;
padding: 16px 12px;
// box-shadow:
// 0 0 20px rgba(64, 150, 255, 0.3),
// inset 0 0 20px rgba(64, 150, 255, 0.1);
// backdrop-filter: blur(10px);
}
.menu-item {
position: relative;
display: flex;
align-items: center;
padding: 24px 32px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
min-height: 44px;
&:hover {
// background: rgba(64, 150, 255, 0.1);
transform: translateX(4px);
}
&--active {
// background: rgba(64, 150, 255, 0.2);
// box-shadow: 0 0 15px rgba(64, 150, 255, 0.4);
.menu-item__text {
color: #ffffff;
text-shadow:
0 0 10px rgba(64, 150, 255, 0.8),
0 0 20px rgba(64, 150, 255, 0.6),
0 0 30px rgba(64, 150, 255, 0.4);
font-weight: 600;
}
}
&__indicator {
position: absolute;
left: 0px;
top: 45%;
transform: translateY(-50%);
width: 6px;
height: 38px;
background: #ffffff;
border-radius: 2px;
box-shadow: 0 0 16px rgba(255, 255, 255, 0.8);
}
&__text {
color: #64a5ff;
font-size: 30px;
font-weight: 800;
text-shadow: 0 0 8px rgba(64, 150, 255, 0.6);
transition: all 0.3s ease;
user-select: none;
}
&__status {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
.status-text {
font-size: 12px;
color: #999;
background: rgba(0, 0, 0, 0.6);
padding: 2px 8px;
border-radius: 4px;
border: 1px solid #333;
}
}
// 禁用状态样式
&--disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: auto; // 保持可点击以显示禁用提示
&:hover {
transform: none;
// background: rgba(255, 0, 0, 0.1);
}
.menu-item__text {
color: #666;
text-shadow: none;
}
.menu-item__indicator {
display: none;
}
}
}
}
.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;
.plate {
position: absolute;
top: 30px;
left: 1200px;
width: 600px;
/* 调整为你需要的宽度 */
height: 1000px;
/* 调整为你需要的高度 */
img {
width: 100%;
height: 100%;
}
}
.plate-content {
position: absolute;
top: 144px;
right: 100px;
width: 600px;
height: 797px;
overflow: hidden;
/* 隐藏超出部分 */
object-fit: contain;
// display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
/* 允许垂直滚动 */
scrollbar-width: none;
/* Firefox隐藏滚动条 */
-ms-overflow-style: none;
/* IE和Edge隐藏滚动条 */
perspective: 1000px;
&::-webkit-scrollbar {
display: none;
/* Chrome, Safari隐藏滚动条 */
}
}
.title {
// margin-top: 47px;
width: 400px;
z-index: 2;
position: absolute;
top: 36px;
left: 1304px;
}
.text {
position: absolute;
top: -9px;
left: 8px;
z-index: 2;
transform: rotate3d(0, 1, 0, -20deg);
img {
width: 100%;
height: auto;
}
}
}
// 调试控制按钮样式
.debug-controls {
position: absolute;
top: 20px;
left: 20px;
display: flex;
flex-direction: column;
gap: 8px;
z-index: 1000;
.debug-btn {
background: rgba(0, 0, 0, 0.8);
color: white;
border: 1px solid #333;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.3s ease;
&:hover {
background: rgba(0, 0, 0, 0.9);
}
}
}
</style>