399 lines
9.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page-wrapper">
<!-- Rebuild Trigger -->
<!-- 背景层 -->
<image class="bg-fixed" :src="detail.banner || ''" mode="aspectFill" />
<view class="bg-mask"></view>
<view class="content-area">
<!-- 顶部信息 -->
<view class="header-section">
<view class="title-box">
<text class="main-title">扫雷挑战</text>
<text class="sub-title">福利放送 智勇通关</text>
</view>
<view class="rule-btn" @tap="showRules">规则</view>
</view>
<!-- 挑战区域 (模拟塔层) -->
<view class="tower-container">
<view class="tower-level current">
<view class="level-info">
<text class="level-num">当前挑战</text>
<text class="level-name">扫雷福利局</text>
</view>
<view class="level-status">进行中</view>
</view>
<!-- 剩余次数展示 -->
<view class="ticket-info">
<text class="ticket-label">剩余挑战次数</text>
<text class="ticket-count">{{ remainingTimes }}</text>
</view>
</view>
<!-- 操作区 -->
<view class="action-area">
<button class="challenge-btn" :disabled="!canPlay" :class="{ disabled: !canPlay }" @tap="onStartChallenge">
{{ canPlay ? '开始挑战' : '去获取资格' }}
</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getActivityDetail } from '../../../api/appUser'
const activityId = ref('')
const detail = ref({})
const remainingTimes = ref(0) // 模拟剩余次数
const ticketId = ref('') // 模拟入场券ID
const canPlay = computed(() => remainingTimes.value > 0)
async function loadData(id) {
try {
const d = await getActivityDetail(id)
detail.value = d || {}
} catch (e) {
console.error(e)
}
}
// 模拟检查用户是否有资格
async function checkEligibility() {
// TODO: Replace with actual API call to check bonus/ticket status
// e.g. const res = await getMinesweeperEligibility()
// 模拟数据假设用户有1次机会
setTimeout(() => {
remainingTimes.value = 1
ticketId.value = 'mock-ticket-123456'
}, 500)
}
function onStartChallenge() {
if (!canPlay.value) {
uni.showToast({ title: '去玩其他游戏赢取资格吧!', icon: 'none' })
// TODO: Navigate to other games or shop
return
}
const token = uni.getStorageSync('token')
if (!token) {
uni.showToast({ title: '请先登录', icon: 'none' })
return
}
// Navigate to WebView Game
// TODO: Replace with real game URL
const gameUrl = 'http://localhost:5174/'
uni.navigateTo({
url: `/pages-game/game/webview?url=${encodeURIComponent(gameUrl)}&ticket=${ticketId.value}`
})
}
function showRules() {
uni.showModal({
title: '规则',
content: '1. 参与平台其他游戏有机会获得扫雷挑战资格。\n2. 挑战成功可获得丰厚奖励。\n3. 扫雷过程中请保持网络通畅。',
showCancel: false
})
}
onLoad((opts) => {
if (opts.id) {
activityId.value = opts.id
loadData(opts.id)
}
})
onShow(() => {
checkEligibility()
})
</script>
<style lang="scss" scoped>
/* ============================================
爬塔页面 - 沉浸式暗黑风格 (SCSS Integration)
============================================ */
$local-gold: #FFD700; // 特殊金色,比全局更亮
.page-wrapper {
min-height: 100vh;
position: relative;
background: $bg-dark;
color: $text-dark-main;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 背景装饰 - 暗黑版 */
.bg-decoration {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none;
z-index: 0;
&::before {
content: '';
position: absolute;
top: -10%; left: -20%;
width: 600rpx; height: 600rpx;
background: radial-gradient(circle, rgba($brand-primary, 0.1) 0%, transparent 70%);
filter: blur(80rpx);
border-radius: 50%;
opacity: 0.6;
animation: float 10s ease-in-out infinite;
}
&::after {
content: '';
position: absolute;
bottom: 10%; right: -10%;
width: 500rpx; height: 500rpx;
background: radial-gradient(circle, rgba($local-gold, 0.08) 0%, transparent 70%);
filter: blur(60rpx);
border-radius: 50%;
opacity: 0.5;
animation: float 12s ease-in-out infinite reverse;
}
}
@keyframes float {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(20rpx, 30rpx); }
}
.bg-fixed {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
opacity: 0.3;
z-index: 0;
filter: blur(8rpx);
}
.bg-mask {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(180deg, rgba($bg-dark, 0.85), $bg-dark 95%);
z-index: 1;
}
.content-area {
position: relative;
z-index: 2;
flex: 1;
display: flex;
flex-direction: column;
padding: $spacing-lg;
padding-top: calc(env(safe-area-inset-top) + 20rpx);
}
.header-section {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: $spacing-xl;
animation: fadeInDown 0.6s ease-out;
}
.title-box {
display: flex;
flex-direction: column;
}
.main-title {
font-size: 60rpx;
font-weight: 900;
font-style: italic;
display: block;
text-shadow: 0 4rpx 16rpx rgba(0,0,0,0.6);
background: linear-gradient(180deg, #fff, #b3b3b3);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: 2rpx;
}
.sub-title {
font-size: 26rpx;
opacity: 0.8;
margin-top: $spacing-xs;
display: block;
letter-spacing: 4rpx;
color: $brand-primary;
text-transform: uppercase;
}
.rule-btn {
background: rgba(255,255,255,0.1);
border: 1px solid $border-dark;
padding: 12rpx 32rpx;
border-radius: 100rpx;
font-size: 24rpx;
backdrop-filter: blur(10rpx);
transition: all 0.2s;
color: rgba(255,255,255,0.9);
&:active {
background: rgba(255,255,255,0.25);
transform: scale(0.96);
}
}
.tower-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 40rpx;
}
.tower-level {
width: 100%;
background: $bg-dark-card;
backdrop-filter: blur(20rpx);
padding: 48rpx;
border-radius: $radius-xl;
box-shadow: 0 16rpx 40rpx rgba(0,0,0,0.3);
margin-bottom: 40rpx;
border: 1px solid $border-dark;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
overflow: hidden;
animation: zoomIn 0.5s ease-out backwards;
&::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
}
&.current {
background: rgba($local-gold, 0.15);
border-color: rgba($local-gold, 0.5);
box-shadow: 0 0 40rpx rgba($local-gold, 0.15), inset 0 0 20rpx rgba($local-gold, 0.05);
}
}
.level-info { display: flex; flex-direction: column; z-index: 1; }
.level-num {
font-size: 24rpx;
color: $text-dark-sub;
margin-bottom: 8rpx;
text-transform: uppercase;
letter-spacing: 2rpx;
}
.level-name {
font-size: 48rpx;
font-weight: 700;
color: $text-dark-main;
text-shadow: 0 4rpx 8rpx rgba(0,0,0,0.3);
}
.level-status {
font-size: 24rpx;
background: linear-gradient(135deg, $local-gold, $brand-secondary);
color: #3e2723;
padding: 8rpx 20rpx;
border-radius: 12rpx;
font-weight: 800;
box-shadow: 0 4rpx 16rpx rgba($brand-secondary, 0.3);
z-index: 1;
}
.ticket-info {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 40rpx;
animation: fadeInUp 0.5s ease-out backwards;
animation-delay: 0.2s;
}
.ticket-label {
font-size: 28rpx;
color: $text-dark-sub;
margin-bottom: 10rpx;
}
.ticket-count {
font-size: 80rpx;
font-weight: 900;
color: $local-gold;
font-family: 'DIN Alternate', sans-serif;
text-shadow: 0 0 20rpx rgba($local-gold, 0.4);
}
.action-area {
position: fixed;
left: 40rpx;
right: 40rpx;
bottom: calc(40rpx + env(safe-area-inset-bottom));
background: rgba(26, 26, 26, 0.85);
backdrop-filter: blur(30rpx);
padding: 24rpx 40rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx solid rgba(255, 255, 255, 0.1);
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.4);
z-index: 100;
animation: slideUp 0.6s cubic-bezier(0.23, 1, 0.32, 1) backwards;
}
.challenge-btn {
background: $gradient-brand !important;
color: #fff !important;
font-weight: 900;
border-radius: 999rpx;
padding: 0 60rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 32rpx;
box-shadow: 0 12rpx 32rpx rgba(255, 107, 0, 0.3);
border: none;
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
width: 100%;
&::before {
content: '';
position: absolute;
top: -50%;
left: -150%;
width: 200%;
height: 200%;
background: linear-gradient(
120deg,
rgba(255, 255, 255, 0) 30%,
rgba(255, 255, 255, 0.4) 50%,
rgba(255, 255, 255, 0) 70%
);
transform: rotate(25deg);
animation: btnShine 4s infinite cubic-bezier(0.19, 1, 0.22, 1);
pointer-events: none;
}
&:active {
transform: scale(0.94);
}
&.disabled {
background: #333 !important;
color: #666 !important;
box-shadow: none;
&::before { display: none; }
}
}
@keyframes btnShine {
0% { left: -150%; }
100% { left: 150%; }
}
</style>