305 lines
6.7 KiB
Vue
305 lines
6.7 KiB
Vue
<template>
|
||
<view class="laser-result-page">
|
||
<image class="background-image" src="/static/background/exhibitionSuccess.png" mode="aspectFill" />
|
||
|
||
<!-- 礼盒打开瞬间的全局扫光 -->
|
||
<view v-if="showOpeningSweep" class="opening-sweep" />
|
||
|
||
<!-- 5 张候选卡的金字塔 -->
|
||
<view class="cards-container" :class="{ 'cards-visible': isGiftOpened }">
|
||
<LaserVariantPyramid
|
||
:paths="generatedImages"
|
||
:selectedIndex="selectedIndex"
|
||
:opened="isGiftOpened"
|
||
@select="handleSelect"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 礼盒 -->
|
||
<view class="gift-box" :class="{ 'gift-opened': isGiftOpened }">
|
||
<image v-if="!isGiftOpened" class="gift-image" src="/static/nft/lihe.png" mode="aspectFit" />
|
||
<image
|
||
v-else
|
||
class="gift-image gift-image-opened"
|
||
src="/static/nft/lihe_kaiqi.png"
|
||
mode="aspectFit"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 底部操作 -->
|
||
<view class="bottom-action">
|
||
<view class="action-button action-button--secondary" @tap="handleBack">
|
||
<text class="button-text">重新选择</text>
|
||
</view>
|
||
<view class="action-button" @tap="selectAsset">
|
||
<text class="button-text">开始铸造</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 铸造确认弹窗 -->
|
||
<ConfirmModal
|
||
v-if="showConfirmModal"
|
||
:visible="showConfirmModal"
|
||
:cost-crystal="confirmCostInfo.costCrystal"
|
||
:current-balance="confirmCostInfo.currentBalance"
|
||
:mint-count="confirmCostInfo.mintCount"
|
||
:next-tier-cost="confirmCostInfo.nextTierCost"
|
||
@confirm="handleConfirmMint"
|
||
@cancel="handleCancelMint"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, onMounted, ref, watch } from 'vue'
|
||
import ConfirmModal from '@/components/ConfirmModal.vue'
|
||
import LaserVariantPyramid from '@/components/laser/LaserVariantPyramid.vue'
|
||
import { useLaserMint } from '@/composables/useLaserMint.js'
|
||
import { CASTLOVE_FORM_KEY, GENERATED_IMAGES_KEY, GENERATION_RESULT_META_KEY } from '@/utils/castloveGenerationFlow.js'
|
||
|
||
const generatedImages = ref([])
|
||
const selectedIndex = ref(-1)
|
||
const instanceNo = ref('')
|
||
const craftFormData = ref(null)
|
||
const isGiftOpened = ref(false)
|
||
const showOpeningSweep = ref(false)
|
||
|
||
/**
|
||
* 从 generatedImages 中提取 URL(兼容旧格式纯字符串和新格式 { url, oss_key })
|
||
*/
|
||
const resolveImageUrl = (item) => {
|
||
if (!item) return ''
|
||
return typeof item === 'object' && item.url ? item.url : String(item)
|
||
}
|
||
|
||
/**
|
||
* 从 generatedImages 中提取 oss_key(仅新格式有值)
|
||
*/
|
||
const resolveOssKey = (item) => {
|
||
if (!item) return ''
|
||
return typeof item === 'object' ? (item.oss_key || '') : ''
|
||
}
|
||
|
||
const selectedImagePath = computed(() => resolveImageUrl(generatedImages.value?.[selectedIndex.value]))
|
||
|
||
const getSelectedImagePath = () => resolveImageUrl(generatedImages.value?.[selectedIndex.value])
|
||
|
||
const getSelectedOssKey = () => resolveOssKey(generatedImages.value?.[selectedIndex.value])
|
||
|
||
const {
|
||
showConfirmModal,
|
||
confirmCostInfo,
|
||
selectAsset,
|
||
handleConfirmMint,
|
||
handleCancelMint,
|
||
} = useLaserMint({
|
||
getSelectedImagePath,
|
||
getSelectedOssKey,
|
||
getSelectedPresetIndex: () => selectedIndex.value,
|
||
formDataRef: craftFormData,
|
||
getInstanceNo: () => instanceNo.value,
|
||
})
|
||
|
||
const handleSelect = (idx) => {
|
||
if (selectedIndex.value === idx) return
|
||
selectedIndex.value = idx
|
||
}
|
||
|
||
const handleBack = () => {
|
||
uni.navigateBack({ delta: 1 })
|
||
}
|
||
|
||
// 礼盒打开后:白光扫光 → 选中默认卡(克制,无彩虹过曝)
|
||
const playOpenSequence = () => {
|
||
showOpeningSweep.value = true
|
||
setTimeout(() => {
|
||
showOpeningSweep.value = false
|
||
}, 700)
|
||
setTimeout(() => {
|
||
if (selectedIndex.value < 0) {
|
||
selectedIndex.value = 2 // 默认选中金字塔顶部那张
|
||
}
|
||
}, 900)
|
||
}
|
||
|
||
onMounted(() => {
|
||
try {
|
||
const formStr = uni.getStorageSync(CASTLOVE_FORM_KEY)
|
||
if (formStr) craftFormData.value = JSON.parse(formStr)
|
||
const imagesStr = uni.getStorageSync(GENERATED_IMAGES_KEY)
|
||
if (imagesStr) {
|
||
generatedImages.value = JSON.parse(imagesStr)
|
||
}
|
||
const metaStr = uni.getStorageSync(GENERATION_RESULT_META_KEY)
|
||
if (metaStr) {
|
||
const meta = JSON.parse(metaStr)
|
||
instanceNo.value = meta.instanceNo || ''
|
||
}
|
||
} catch (e) {
|
||
console.error('[laser-result] init failed', e)
|
||
uni.showToast({ title: '数据错误', icon: 'none' })
|
||
setTimeout(() => uni.navigateBack(), 1200)
|
||
}
|
||
|
||
// 800ms 礼盒打开 + 进场动效序列
|
||
setTimeout(() => {
|
||
isGiftOpened.value = true
|
||
playOpenSequence()
|
||
}, 800)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.laser-result-page {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
background: #000;
|
||
}
|
||
|
||
.background-image {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* 礼盒打开瞬间的全局扫光:克制白光从右上到左下斜切(不抢戏) */
|
||
.opening-sweep {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 50;
|
||
pointer-events: none;
|
||
background: linear-gradient(
|
||
115deg,
|
||
transparent 38%,
|
||
rgba(255, 255, 255, 0.45) 50%,
|
||
rgba(220, 230, 240, 0.25) 60%,
|
||
transparent 75%
|
||
);
|
||
background-size: 250% 250%;
|
||
background-position: 120% -120%;
|
||
animation: opening-sweep-anim 0.7s ease-out forwards;
|
||
}
|
||
|
||
@keyframes opening-sweep-anim {
|
||
0% {
|
||
background-position: 120% -120%;
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
background-position: -120% 220%;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.cards-container {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 20;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.5s ease-in-out;
|
||
}
|
||
|
||
.cards-container.cards-visible {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.gift-box {
|
||
position: absolute;
|
||
bottom: 20%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 480rpx;
|
||
height: 480rpx;
|
||
z-index: 3;
|
||
animation: giftFloat 3s ease-in-out infinite;
|
||
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.gift-box.gift-opened {
|
||
width: 17rem;
|
||
bottom: 15%;
|
||
animation: none;
|
||
}
|
||
|
||
@keyframes giftFloat {
|
||
0%,
|
||
100% {
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
50% {
|
||
transform: translateX(-50%) translateY(-20rpx);
|
||
}
|
||
}
|
||
|
||
.gift-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
transition: opacity 0.5s ease-in-out;
|
||
}
|
||
|
||
.gift-image-opened {
|
||
width: 17rem;
|
||
animation: giftOpenPop 0.5s ease-out;
|
||
}
|
||
|
||
@keyframes giftOpenPop {
|
||
0% {
|
||
transform: scale(0.8);
|
||
opacity: 0;
|
||
}
|
||
50% {
|
||
transform: scale(1.1);
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.bottom-action {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 70rpx;
|
||
z-index: 30;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 24rpx;
|
||
padding: 0 40rpx;
|
||
}
|
||
|
||
.action-button {
|
||
min-width: 260rpx;
|
||
height: 92rpx;
|
||
border-radius: 50rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-image: url('/static/icon/confirmbj.png');
|
||
background-size: cover;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.35);
|
||
}
|
||
|
||
.action-button--secondary {
|
||
background-image: none;
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border: 2rpx solid rgba(255, 255, 255, 0.45);
|
||
}
|
||
|
||
.button-text {
|
||
font-size: 34rpx;
|
||
}
|
||
</style>
|