You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2457 lines
60 KiB

<template>
<view class="planet" :style="{'--statusbar': statusBarHeight + 'px'}">
<view class="planet-bg">
<view class="bg-aura bg-aura-a"></view>
<view class="bg-aura bg-aura-b"></view>
<view class="bg-aura bg-aura-c"></view>
<view class="bg-space-dust bg-space-dust-a"></view>
<view class="bg-space-dust bg-space-dust-b"></view>
<view class="bg-shooting bg-shooting-a"></view>
<view class="bg-shooting bg-shooting-b"></view>
<view class="bg-orbit bg-orbit-a"></view>
<view class="bg-orbit bg-orbit-b"></view>
<view class="bg-star" v-for="(s, i) in stars" :key="i" :style="s"></view>
<view class="bg-planet bg-planet-a"></view>
<view class="bg-planet bg-planet-b"></view>
</view>
<view class="nav" :style="{height: navHeight + 'px', paddingTop: statusBarHeight + 'px'}">
<view class="nav-back" @tap="goBack">
<text class="nav-back-icon">‹</text>
</view>
<view class="nav-title">白嫖星球</view>
</view>
<scroll-view
scroll-y
enhanced
scroll-with-animation
:scroll-into-view="guideFocus.anchor"
:show-scrollbar="false"
class="planet-scroll"
:style="{paddingTop: (navHeight + 10) + 'px'}">
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton">
<view class="sk-block sk-header"></view>
<view class="sk-block sk-me"></view>
<view class="sk-block sk-row"></view>
<view class="sk-block sk-list"></view>
</view>
<block v-else>
<planet-header
:data="home"
@draw="goDrawResult"
@guide="showGuideModal"
@poolrule="showPoolRuleModal"
@join="onJoinPool"
@rank="goRank">
</planet-header>
<view
id="guide-main-loop"
class="guide-anchor-wrap">
<planet-daily-loop
ref="dailyLoop"
class="primary-loop"
:data="home.dailyLoop"
:my-ticket-count="home.myTicketCount"
:rank-no="home.myRankNo"
:guide-key="guideFocus.key"
@join="onJoinPool"
@rank="goRank"
@ticketlog="goTicketLog">
</planet-daily-loop>
</view>
<view
id="guide-tasks"
class="flow-card"
:class="{'guide-focus-target': guideFocus.key === 'tasks'}">
<view class="flow-title">玩法很简单</view>
<view class="flow-sub">先赚券,再参与玩法,最后等开奖</view>
<view class="flow-steps">
<view
class="flow-step"
v-for="(item, i) in flowButtons"
:key="item.key"
@tap="onFlowTap(item)">
<view class="flow-dot">{{i + 1}}</view>
<view class="flow-copy">
<view class="flow-name">{{item.name}}</view>
<view class="flow-desc">{{item.desc}}</view>
</view>
<view class="flow-go">{{item.action}}</view>
<view v-if="i < flowButtons.length - 1" class="flow-arrow">→</view>
</view>
</view>
<!-- <view class="dl-sub">种树、掠夺、拆盲盒、签到、学院排位可获得星球券</view> -->
</view>
<view
id="guide-adventure"
class="guide-anchor-wrap"
:class="{'guide-focus-target': guideFocus.key === 'adventure'}">
<planet-adventure-entry @start="goAdventure" @arena="goArena"></planet-adventure-entry>
</view>
<view
id="guide-game"
class="planet-quick-row secondary-play">
<view
id="guide-box"
class="quick-box guide-anchor-wrap"
:class="{'guide-focus-target': guideFocus.key === 'box'}">
<planet-box
compact
:available="home.boxAvailable"
:opening="boxOpening"
@open="openBox">
</planet-box>
</view>
<view class="planet-side-col">
<view class="planet-map-card" @tap="goMore">
<view class="planet-map-orb"></view>
<view class="planet-map-head">
<text class="planet-map-title">星球补给地图</text>
<text class="planet-map-btn">进入</text>
</view>
<view class="planet-map-plays">
<text>下单得券</text>
<text>种树得券</text>
<text>升防御塔</text>
</view>
</view>
<view
id="guide-sign"
class="quick-me guide-anchor-wrap"
:class="{'guide-focus-target': guideFocus.key === 'sign'}">
<planet-me
compact
:data="home"
@sign="onSign">
</planet-me>
</view>
</view>
</view>
<view class="bottom-space"></view>
</block>
</scroll-view>
<!-- 追捕浮层 -->
<hunt-modal
:show="huntModal.show"
:phase="huntModal.phase"
:result="huntModal.result"
:target="huntModal.target"
@close="closeHunt">
</hunt-modal>
<view v-if="guideFocus.show && guideFocus.rect" class="guide-focus-layer">
<view class="guide-mask-piece guide-mask-top" :style="{height: guideFocus.rect.top + 'px'}" @tap="clearGuideFocus"></view>
<view class="guide-mask-piece guide-mask-left" :style="{top: guideFocus.rect.top + 'px', height: guideFocus.rect.height + 'px', width: guideFocus.rect.left + 'px'}" @tap="clearGuideFocus"></view>
<view class="guide-mask-piece guide-mask-right" :style="{top: guideFocus.rect.top + 'px', height: guideFocus.rect.height + 'px', left: guideFocus.rect.right + 'px'}" @tap="clearGuideFocus"></view>
<view class="guide-mask-piece guide-mask-bottom" :style="{top: guideFocus.rect.bottom + 'px'}" @tap="clearGuideFocus"></view>
<view
class="guide-focus-tip"
:class="guideFocus.tipPlacement === 'top' ? 'tip-top' : 'tip-bottom'"
:style="guideFocus.tipStyle"
@tap.stop>
<view class="guide-focus-step">{{guideFocus.stepText}}</view>
<view class="guide-focus-title">{{guideFocus.title}}</view>
<view class="guide-focus-desc">{{guideFocus.desc}}</view>
<view class="guide-focus-actions">
<!-- <view class="guide-focus-skip" @tap="finishGuideTask">跳过</view> -->
<view class="guide-focus-btn" @tap="nextGuideStep">{{guideFocus.isLast ? '完成引导' : '下一步'}}</view>
</view>
</view>
</view>
<view v-if="boxResult.show" class="box-result-mask" @tap="closeBoxResult">
<view class="box-result" @tap.stop>
<view class="box-result-burst"></view>
<view class="box-result-icon">BLIND BOX</view>
<view class="box-result-title">{{boxResult.data.rewardName}}</view>
<view class="box-result-desc">{{boxResult.data.message}}</view>
<view class="box-result-btn" @tap="closeBoxResult">收下了</view>
</view>
</view>
<view v-if="winningModal.show" class="win-mask" @tap.stop>
<view class="win-card" @tap.stop>
<view class="win-burst"></view>
<view class="win-medal">WIN</view>
<view class="win-title">恭喜中奖啦</view>
<view class="win-sub">{{winningModal.data.levelName || '白嫖星球奖励'}}</view>
<view class="win-amount">
<text>¥</text>{{winningAmount}}
</view>
<view class="win-tip">奖金已准备好,开心收下后会进入账户余额</view>
<view class="win-btn" :class="{disabled: winningModal.receiving}" @tap.stop="receiveWinning">
{{winningModal.receiving ? '领取中...' : '开心收下'}}
</view>
</view>
</view>
<view v-if="taskDrawerVisible" class="task-drawer-mask" @tap="taskDrawerVisible=false">
<view class="task-drawer" @tap.stop>
<view class="drawer-handle"></view>
<view class="section-head">
<view>
<view class="section-kicker">DAILY TASK</view>
<view class="section-title">今日收券任务</view>
</view>
<view class="section-time" @tap="taskDrawerVisible=false">收起</view>
</view>
<view class="task-list">
<view class="task-item" v-for="task in dailyTasks" :key="task.key">
<view class="task-icon">{{task.icon}}</view>
<view class="task-copy">
<view class="task-name">{{task.name}}</view>
<view class="task-reward">{{task.reward}}</view>
</view>
<view class="task-action" :class="{done: task.done}" @tap="runDailyTask(task)">
{{task.done ? '已完成' : task.action}}
</view>
</view>
</view>
</view>
</view>
<view v-if="poolRuleVisible" class="pool-rule-mask" @tap="poolRuleVisible=false">
<view class="pool-detail-card pool-detail-modal" @tap.stop>
<view class="pool-detail-head">
<view>
<view class="pool-detail-kicker">CURRENT POOL</view>
<view class="pool-detail-title">本期奖池规则</view>
</view>
<view class="pool-detail-close" @tap="poolRuleVisible=false">×</view>
</view>
<view class="pool-detail-time modal-time">{{drawTimeText}}</view>
<view class="pool-detail-grid">
<view class="pool-detail-item">
<text>¥{{Number(home.poolAmount || 0).toFixed(2)}}</text>
<text>奖池金额</text>
</view>
<view class="pool-detail-item">
<text>{{home.joinCount || 0}}</text>
<text>参与人数</text>
</view>
<view class="pool-detail-item">
<text>{{(home.dailyLoop && home.dailyLoop.poolTicketTotal) || 0}}</text>
<text>总投入券</text>
</view>
<view class="pool-detail-item">
<text>{{(home.dailyLoop && home.dailyLoop.myWeightTickets) || 0}}</text>
<text>我投入券</text>
</view>
</view>
<view class="reward-list" v-if="home.rewardList && home.rewardList.length">
<view class="reward-row" v-for="(r, i) in home.rewardList" :key="i">
<text>{{r.levelName}}</text>
<text>{{Number(r.amount || 0).toFixed(2)}} × {{r.quota || 0}}</text>
</view>
</view>
<view class="pool-rule">投入越多概率越高但权重会递减每人每期最多中一次概率仅为实时估算</view>
</view>
</view>
<view v-if="guideVisible" class="guide-mask" @tap="guideVisible=false">
<view class="guide-card" @tap.stop>
<view class="guide-kicker">BANJINGLI GUIDE</view>
<view class="guide-title">白嫖星球玩法说明书</view>
<view class="guide-list">
<view class="guide-item">
<text>1</text>
<view>每天签到、开补给箱、玩学院排位赛都能获得星球券和现金奖励。</view>
</view>
<view class="guide-item">
<text>2</text>
<view>把星球券投入奖池,券越多,开奖时瓜分现金奖励的概率越高。</view>
</view>
<view class="guide-item">
<text>3</text>
<view>财富排名里可以发起星际追查,可以互相偷取星球券。</view>
</view>
<view class="guide-item">
<text>4</text>
<view>补给地图:券树培育、防御塔升级、下单给券。适合每天顺手经营。</view>
</view>
</view>
<view class="guide-btn" @tap="guideVisible=false">知道怎么玩了</view>
</view>
</view>
</view>
</template>
<script>
import planetHeader from '@/package1/components/planet/planet-header.vue'
import planetMe from '@/package1/components/planet/planet-me.vue'
import planetBox from '@/package1/components/planet/planet-box.vue'
import huntModal from '@/package1/components/planet/hunt-modal.vue'
import planetDailyLoop from '@/package1/components/planet/planet-daily-loop.vue'
import planetAdventureEntry from '@/package1/components/planet/planet-adventure-entry.vue'
export default {
components: {
planetHeader,
planetMe,
planetBox,
huntModal,
planetDailyLoop,
planetAdventureEntry
},
data() {
return {
statusBarHeight: 20,
navHeight: 64,
loading: true,
userId: '',
regionId: '',
regionName: '',
nickname: '',
avatar: '',
college: '',
loadedOnce: false,
home: {
poolAmount: 0,
joinCount: 0,
myTicketCount: 0,
myRankNo: 0,
remainHunt: 3,
tasks: [],
rankList: [],
buffShop: [],
newsList: [],
myBuffs: [],
operate: {},
dailyLoop: {}
},
boxOpening: false,
boxResult: {
show: false,
data: {}
},
winningModal: {
show: false,
data: {},
receiving: false
},
poolRuleVisible: false,
guideVisible: false,
taskDrawerVisible: false,
guidePausedStep: null,
guideFocus: {
show: false,
key: '',
anchor: '',
target: '',
title: '',
desc: '',
rect: null,
tipStyle: '',
tipPlacement: 'bottom',
stepIndex: 0,
stepText: '',
isLast: false
},
huntModal: {
show: false,
phase: 'searching',
result: null,
target: {}
}
}
},
computed: {
stars() {
const arr = []
for (let n = 1; n <= 20; n++) {
const top = (n * 29) % 100
const left = (n * 47) % 100
const delay = (n % 12) / 2
const size = (n % 4) + 1
arr.push(`top:${top}%;left:${left}%;width:${size}px;height:${size}px;animation-delay:${delay}s;`)
}
return arr
},
winningAmount() {
const amount = Number((this.winningModal.data && this.winningModal.data.amount) || 0)
return amount.toFixed(2)
},
drawTimeText() {
if (!this.home || !this.home.drawTime) return '每日 22:00 开奖'
const text = String(this.home.drawTime)
return text.length > 16 ? text.substring(5, 16) + ' 开奖' : text
},
flowButtons() {
return [
{
key: 'tasks',
name: '获得星球券',
desc: '打开今日任务',
action: '收券'
},
{
key: 'pool',
name: '投入星球券',
desc: '22:00开奖得现金',
action: '投券'
}
]
},
gloryPrizes() {
return [
{ rank: 1, amount: 5 },
{ rank: 2, amount: 4 },
{ rank: 3, amount: 3 },
{ rank: 4, amount: 2 },
{ rank: 5, amount: 1 }
]
},
loop() {
return (this.home && this.home.dailyLoop) || {}
},
collegeRankText() {
const rank = this.pickNumber([
this.home.myCollegeRankNo,
this.home.collegeRankNo,
this.home.adventureHome && this.home.adventureHome.myCollegeRankNo,
this.home.dailyAdventure && this.home.dailyAdventure.myCollegeRankNo
])
return rank > 0 ? ('第' + rank + '名') : '未上榜'
},
personalRankText() {
const rank = this.pickNumber([
this.home.myAdventureRankNo,
this.home.myPersonalRankNo,
this.home.personalRankNo,
this.home.adventureHome && this.home.adventureHome.myRankNo,
this.home.dailyAdventure && this.home.dailyAdventure.myRankNo
])
return rank > 0 ? ('第' + rank + '名') : '未上榜'
},
todayScore() {
return this.pickNumber([
this.home.myTodayScore,
this.home.myScore,
this.home.score,
this.home.adventureHome && this.home.adventureHome.myScore,
this.home.dailyAdventure && this.home.dailyAdventure.myScore
])
},
nextGapText() {
const gap = this.pickNumber([
this.home.nextRankGap,
this.home.distanceNextScore,
this.home.adventureHome && this.home.adventureHome.nextRankGap,
this.home.dailyAdventure && this.home.dailyAdventure.nextRankGap
])
return gap > 0 ? (gap + '分') : '继续冲榜'
},
dailyTasks() {
const signed = !!(this.home.signedToday || this.loop.signedToday)
const boxAvailable = !!(this.home.boxAvailable || this.loop.boxAvailable)
const remainHunt = this.home.remainHunt !== undefined ? this.home.remainHunt : this.loop.remainHunt
const hunted = remainHunt !== undefined && Number(remainHunt) < 3
return [
{
key: 'box',
icon: '?',
name: '幸运盲盒舱抽券',
reward: '随机中奖获得额外星球券',
done: !boxAvailable,
action: boxAvailable ? '领取' : '已完成'
},
{
key: 'sign',
icon: '签',
name: '签到收券',
reward: '每日签到得1张星球券',
done: signed,
action: signed ? '已完成' : '领取'
},
{
key: 'hunt',
icon: '偷',
name: '发起掠夺偷券',
reward: '打土豪,随机偷取星球券',
done: hunted,
action: hunted ? '已完成' : '去完成'
}
]
},
guideTasks() {
return [
{ key: 'sign', index: 1, name: '签到领券', desc: '每日签到拿星球券', action: '去签到' },
{ key: 'box', index: 2, name: '幸运盲盒舱领券', desc: '抽盲盒有机会获得额外星球券', action: '去领取' },
{ key: 'hunt', index: 3, name: '发起掠夺', desc: '去财富排名页随机偷取星球券', action: '去掠夺' },
{ key: 'pool', index: 4, name: '投入券', desc: '投入星球券参与22:00开奖', action: '去投券' },
{ key: 'adventure', index: 5, name: '参与学院排位赛', desc: '打开学院排位赛页面了解奖励', action: '去排位' }
]
}
},
onLoad() {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
this.initNavHeight()
this.userId = uni.getStorageSync('id') || ''
this.nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || ''
this.avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || ''
try {
const area = uni.getStorageSync('area')
if (area) {
const areaInfo = JSON.parse(area)
this.regionId = areaInfo.id || ''
this.regionName = areaInfo.title || ''
}
} catch (e) {}
if (!this.checkPoolJoinRegion()) return
this.loadHome()
},
onShow() {
if (this.loadedOnce) {
this.loadHome(true)
}
},
methods: {
initNavHeight() {
try {
const menu = uni.getMenuButtonBoundingClientRect && uni.getMenuButtonBoundingClientRect()
if (menu && menu.top && menu.height) {
this.navHeight = menu.top + menu.height + 8
return
}
} catch (e) {}
this.navHeight = this.statusBarHeight + 44
},
showPoolRuleModal() {
this.poolRuleVisible = true
},
poolJoinRegionKey() {
return 'planetPoolJoinRegion_' + this.userId
},
checkPoolJoinRegion() {
if (!this.userId || !this.regionId) return true
const record = uni.getStorageSync(this.poolJoinRegionKey())
if (!record) return true
let joinRegion = record
if (typeof record === 'string') {
try {
joinRegion = JSON.parse(record)
} catch (e) {
return true
}
}
const joinRegionId = joinRegion && joinRegion.regionId
if (!joinRegionId) return true
if (String(joinRegionId) === String(this.regionId)) {
if (!joinRegion.regionName && this.regionName) this.savePoolJoinRegion()
return true
}
const joinRegionName = joinRegion.regionName || '其他'
uni.showModal({
title: '提示',
content: `您已在“${joinRegionName}”区域参与抽奖`,
showCancel: false,
success() {
uni.reLaunch({ url: '/pages/index/index' })
}
})
return false
},
savePoolJoinRegion() {
if (!this.userId || !this.regionId) return
uni.setStorageSync(this.poolJoinRegionKey(), {
userId: this.userId,
regionId: this.regionId,
regionName: this.regionName
})
},
loadHome(silent) {
if (!this.userId) {
this.tui.toast('请先登录')
return
}
if (!this.regionId) {
this.loading = false
this.tui.toast('未获取到校区信息,白嫖星球仅限本区域内进行')
return
}
if (!silent) this.loading = true
this.tui.request('/app/planet/home', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college
}, false, false, true).then((res) => {
this.loading = false
if (res.code == 200 && res.result) {
this.home = res.result
this.loadedOnce = true
this.showUnreadWinning()
this.showGuideTaskIfNeeded()
} else if (res.message) {
this.tui.toast(res.message)
}
}).catch(() => {
this.loading = false
})
},
onSign() {
const pausedStep = this.pauseGuideForAction('sign')
this.tui.request('/app/planet/sign', 'POST', {
userId: this.userId,
regionId: this.regionId
}).then((res) => {
this.tui.toast(res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
this.resumeGuideAfterAction(pausedStep, res.code == 200)
})
},
openBox() {
if (!this.home.boxAvailable || this.boxOpening) return
this.pauseGuideForAction('box')
this.boxOpening = true
this.tui.request('/app/planet/box/open', 'POST', {
userId: this.userId,
regionId: this.regionId
}).then((res) => {
setTimeout(() => {
this.boxOpening = false
if (res.code == 200 && res.result) {
this.boxResult.data = res.result
this.boxResult.show = true
this.loadHome(true)
} else {
this.tui.toast(res.message)
this.resumeGuideAfterAction(this.guidePausedStep, false)
}
}, 800)
}).catch(() => {
this.boxOpening = false
this.resumeGuideAfterAction(this.guidePausedStep, false)
})
},
closeBoxResult() {
this.boxResult.show = false
this.resumeGuideAfterAction(this.guidePausedStep, true)
},
showUnreadWinning() {
const win = this.home && this.home.unreadWinning
const winnerId = this.getWinnerId(win)
if (!win || !winnerId) return
if (this.winningModal.show && this.getWinnerId(this.winningModal.data) === winnerId) return
this.winningModal = {
show: true,
data: win,
receiving: false
}
},
getWinnerId(win) {
if (!win) return ''
return win.id || win.winnerId || win._id || ''
},
receiveWinning() {
const win = this.winningModal.data || {}
const winnerId = this.getWinnerId(win)
if (this.winningModal.receiving) return
if (!winnerId) {
this.tui.toast('中奖记录ID缺失')
return
}
if (win.isReceived === 1) {
this.winningModal.show = false
return
}
this.winningModal.receiving = true
this.tui.request('/app/planet/draw/receive', 'POST', {
userId: this.userId,
regionId: this.regionId,
winnerId
}).then((res) => {
this.winningModal.receiving = false
this.tui.toast(res.message, 1500, res.code == 200)
if (res.code == 200) {
this.winningModal.show = false
this.loadHome(true)
}
}).catch(() => {
this.winningModal.receiving = false
})
},
onHunt(item) {
if (item.self) {
this.tui.toast('不能追捕自己')
return
}
if (item.shielded) {
this.tui.toast('目标已开启防护罩')
return
}
if (this.home.remainHunt <= 0) {
this.tui.toast('今日追捕次数已用完')
return
}
this.huntModal.show = true
this.huntModal.phase = 'searching'
this.huntModal.result = null
this.huntModal.target = item
// 动画阶段推进
setTimeout(() => { this.huntModal.phase = 'locking' }, 900)
setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800)
this.tui.request('/app/planet/hunt', 'POST', {
userId: this.userId,
regionId: this.regionId,
toUserId: item.userId
}, false, false, true).then((res) => {
setTimeout(() => {
if (res.code == 200 && res.result) {
this.huntModal.result = res.result
this.huntModal.phase = 'result'
this.loadHome(true)
} else {
this.huntModal.show = false
this.tui.toast(res.message)
}
}, 2600)
}).catch(() => {
this.huntModal.show = false
})
},
closeHunt() {
this.huntModal.show = false
this.huntModal.result = null
},
operateRequest(url, data, successText) {
this.tui.request(url, 'POST', Object.assign({
userId: this.userId,
regionId: this.regionId
}, data || {})).then((res) => {
this.tui.toast(res.code == 200 ? successText : res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
})
},
pickNumber(values) {
for (let i = 0; i < values.length; i++) {
const value = Number(values[i] || 0)
if (value > 0) return value
}
return 0
},
guideTaskKey() {
return 'planetGuideTaskShown_' + (this.userId || 'guest')
},
showGuideTaskIfNeeded() {
const record = uni.getStorageSync(this.guideTaskKey())
if (record && record.completed) return
setTimeout(() => {
this.startGuideSteps()
}, 500)
},
hideGuideTask() {
this.clearGuideFocus()
},
finishGuideTask() {
this.hideGuideTask()
uni.setStorageSync(this.guideTaskKey(), {
shown: true,
completed: true,
stepIndex: this.guideTasks.length,
closedAt: Date.now()
})
},
runDailyTask(task) {
if (!task || task.done) return
if (task.key === 'box') {
this.openBox()
return
}
if (task.key === 'sign') {
this.onSign()
return
}
if (task.key === 'hunt') {
this.taskDrawerVisible = false
this.goRank()
}
},
startGuideSteps() {
if (!this.guideTasks.length) return
const record = uni.getStorageSync(this.guideTaskKey())
let stepIndex = 0
if (record && record.completed) return
if (record && record.stepIndex !== undefined) {
stepIndex = Math.min(Math.max(Number(record.stepIndex) || 0, 0), this.guideTasks.length - 1)
}
this.focusGuideTarget(this.guideTasks[stepIndex], stepIndex)
},
nextGuideStep() {
const nextIndex = Number(this.guideFocus.stepIndex || 0) + 1
if (nextIndex >= this.guideTasks.length) {
this.finishGuideTask()
return
}
this.focusGuideTarget(this.guideTasks[nextIndex], nextIndex)
},
pauseGuideForAction(key) {
if (!this.guideFocus.show || this.guideFocus.key !== key) return null
const pausedStep = Number(this.guideFocus.stepIndex || 0)
this.guidePausedStep = pausedStep
this.clearGuideFocus()
return pausedStep
},
resumeGuideAfterAction(pausedStep, success) {
if (pausedStep === null || pausedStep === undefined) return
const nextIndex = success ? pausedStep + 1 : pausedStep
this.guidePausedStep = null
if (nextIndex >= this.guideTasks.length) {
this.finishGuideTask()
return
}
setTimeout(() => {
this.focusGuideTarget(this.guideTasks[nextIndex], nextIndex)
}, 360)
},
focusGuideTarget(task, stepIndex) {
const map = {
sign: {
anchor: 'guide-sign',
scrollAnchor: 'guide-game',
title: '点击这里签到领券',
desc: '点“今日签到盖章”,每天可领星球券。'
},
box: {
anchor: 'guide-box',
scrollAnchor: 'guide-game',
title: '点击这里开幸运盲盒舱',
desc: '拆盲盒有机会获得额外星球券。'
},
hunt: {
anchor: 'guide-hunt',
scrollAnchor: 'guide-main-loop',
title: '点击这里发起掠夺',
desc: '点模块里的“财富排名”,进入榜单后可随机偷取星球券。'
},
pool: {
anchor: 'guide-pool',
scrollAnchor: 'guide-main-loop',
title: '点击这里投入星球券',
desc: '投券后参与今日22:00现金池开奖。'
},
adventure: {
anchor: 'guide-adventure',
scrollAnchor: 'guide-adventure',
title: '这里是游戏版块',
desc: '学院排位赛、组队PK、盲盒和补给地图都在这里。'
}
}
const target = map[task.key]
if (!target) return
this.hideGuideTask()
const currentIndex = Number(stepIndex || 0)
this.guideFocus = {
show: false,
key: task.key,
anchor: target.scrollAnchor || target.anchor,
target: target.anchor,
title: target.title,
desc: target.desc,
rect: null,
tipStyle: '',
tipPlacement: 'bottom',
stepIndex: currentIndex,
stepText: '新手引导 ' + (currentIndex + 1) + '/' + this.guideTasks.length,
isLast: currentIndex >= this.guideTasks.length - 1
}
uni.setStorageSync(this.guideTaskKey(), {
shown: true,
completed: false,
stepIndex: currentIndex,
updatedAt: Date.now()
})
setTimeout(() => {
this.measureGuideTarget(0)
}, 520)
},
measureGuideTarget(retryCount) {
if ((this.guideFocus.key === 'hunt' || this.guideFocus.key === 'pool') && this.$refs.dailyLoop && this.$refs.dailyLoop.getGuideRect) {
this.$refs.dailyLoop.getGuideRect(this.guideFocus.key).then(rect => {
const sys = uni.getSystemInfoSync()
if (rect && rect.bottom > 0 && rect.top < sys.windowHeight) {
this.applyGuideRect(rect, sys)
return
}
this.measureMainLoopFallback()
})
return
}
const targetId = this.guideFocus.target || this.guideFocus.anchor
if (!targetId) return
const sys = uni.getSystemInfoSync()
uni.createSelectorQuery()
.in(this)
.select('#' + targetId)
.boundingClientRect(rect => {
if (!rect) {
if ((this.guideFocus.key === 'hunt' || this.guideFocus.key === 'pool') && retryCount >= 4) {
this.measureMainLoopFallback()
return
}
if (retryCount < 8) {
setTimeout(() => this.measureGuideTarget(retryCount + 1), 180)
}
return
}
const fullyInvisible = rect.bottom <= 0 || rect.top >= sys.windowHeight
if (fullyInvisible && retryCount < 8) {
setTimeout(() => this.measureGuideTarget(retryCount + 1), 180)
return
}
this.applyGuideRect(rect, sys)
})
.exec()
},
measureMainLoopFallback() {
const sys = uni.getSystemInfoSync()
uni.createSelectorQuery()
.in(this)
.select('#guide-main-loop')
.boundingClientRect(rect => {
if (!rect) return
let targetRect = rect
if (this.guideFocus.key === 'hunt') {
const cardWidth = rect.width / 3
targetRect = {
top: rect.bottom - 88,
bottom: rect.bottom - 8,
left: rect.left + 8,
right: rect.left + cardWidth - 4,
width: cardWidth - 12,
height: 80
}
}
if (this.guideFocus.key === 'pool') {
targetRect = {
top: rect.top + 132,
bottom: rect.top + 314,
left: rect.left + 28,
right: rect.right - 28,
width: rect.width - 56,
height: 182
}
}
this.applyGuideRect(targetRect, sys)
})
.exec()
},
applyGuideRect(targetRect, sys) {
const pad = this.guideFocus.key === 'pool' ? 4 : 8
const tipTop = targetRect.top > sys.windowHeight * 0.42
const tipStyle = tipTop
? 'left:28rpx;right:28rpx;bottom:' + Math.max(24, (sys.windowHeight - targetRect.top + 14)) + 'px;'
: 'left:28rpx;right:28rpx;top:' + Math.max(24, targetRect.bottom + 14) + 'px;'
this.guideFocus.rect = {
top: Math.max(0, targetRect.top - pad),
left: Math.max(0, targetRect.left - pad),
right: targetRect.right + pad,
bottom: targetRect.bottom + pad,
height: targetRect.height + pad * 2
}
this.guideFocus.tipStyle = tipStyle
this.guideFocus.tipPlacement = tipTop ? 'top' : 'bottom'
this.guideFocus.show = true
},
clearGuideFocus() {
this.guideFocus = {
show: false,
key: '',
anchor: '',
target: '',
title: '',
desc: '',
rect: null,
tipStyle: '',
tipPlacement: 'bottom',
stepIndex: 0,
stepText: '',
isLast: false
}
},
onFlowTap(item) {
if (!item) return
if (item.key === 'tasks') {
this.taskDrawerVisible = true
return
}
if (item.key === 'pool') {
if (this.$refs.dailyLoop && this.$refs.dailyLoop.onJoin) {
this.$refs.dailyLoop.onJoin()
} else {
this.onJoinPool()
}
}
},
onRandomSearch() {
this.startSearch('/app/planet/search/random', {}, { nickname: '随机同校区玩家' })
},
onDailyCollect() {
if (!this.home.dailyLoop || !this.home.dailyLoop.signedToday) {
this.onSign()
return
}
if (this.home.dailyLoop.boxAvailable) {
this.openBox()
return
}
this.tui.toast('今天的顺手券已经收完')
},
onDailyRevenge() {
const loop = this.home.dailyLoop || {}
if (loop.hasRevengeTarget && loop.revengeTarget && loop.revengeTarget.fromUserId) {
this.startSearch('/app/planet/search/target', { toUserId: loop.revengeTarget.fromUserId }, loop.revengeTarget)
} else {
this.onRandomSearch()
}
},
startSearch(url, data, target) {
this.huntModal.show = true
this.huntModal.phase = 'searching'
this.huntModal.result = null
this.huntModal.target = target || { nickname: '同校区玩家' }
setTimeout(() => { this.huntModal.phase = 'locking' }, 900)
setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800)
this.tui.request(url, 'POST', Object.assign({
userId: this.userId,
regionId: this.regionId
}, data || {}), false, false, true).then((res) => {
setTimeout(() => {
if (res.code == 200 && res.result) {
this.huntModal.result = {
result: res.result.countered ? 'shield' : (res.result.intercepted ? 'fail' : 'success'),
message: res.result.message,
gainTickets: res.result.gainTickets,
totalGain: res.result.gainTickets || 0,
remainHunt: res.result.remainSearchCount
}
this.huntModal.phase = 'result'
this.loadHome(true)
} else {
this.huntModal.show = false
this.tui.toast(res.message)
}
}, 2200)
}).catch(() => {
this.huntModal.show = false
})
},
onJoinPool() {
if (!this.checkPoolJoinRegion()) return
const loop = this.home.dailyLoop || {}
if ((this.home.myTicketCount || 0) <= 0) {
this.taskDrawerVisible = true
this.tui.toast('星球券不足,先完成任务收券')
return
}
this.home.myTicketCount = Math.max(0, (this.home.myTicketCount || 0) - 1)
loop.myPoolTickets = (loop.myPoolTickets || 0) + 1
this.$set(this.home, 'dailyLoop', loop)
this.tui.request('/app/planet/pool/join', 'POST', {
userId: this.userId,
regionId: this.regionId,
tickets: 1
}).then((res) => {
if (res.code == 200) {
this.savePoolJoinRegion()
this.tui.toast('您已参与今日奖,记得22:00来查看中奖情况哦')
this.loadHome(true)
} else {
this.home.myTicketCount = (this.home.myTicketCount || 0) + 1
loop.myPoolTickets = Math.max(0, (loop.myPoolTickets || 0) - 1)
this.$set(this.home, 'dailyLoop', loop)
this.tui.toast(res.message)
}
}).catch(() => {
this.home.myTicketCount = (this.home.myTicketCount || 0) + 1
loop.myPoolTickets = Math.max(0, (loop.myPoolTickets || 0) - 1)
this.$set(this.home, 'dailyLoop', loop)
})
},
goDrawResult() {
uni.navigateTo({
url: '/package1/planet/drawResult'
})
},
goTicketLog() {
uni.navigateTo({
url: '/package1/planet/ticketLog'
})
},
goAdventure() {
uni.navigateTo({
url: '/package1/planet/adventure'
})
},
goArena() {
uni.navigateTo({
url: '/package1/planet/pkHall'
})
},
goRank() {
uni.navigateTo({
url: '/package1/planet/rank'
})
},
goMore() {
uni.navigateTo({
url: '/package1/planet/more'
})
},
showGuideModal() {
this.guideVisible = true
},
goBack() {
uni.reLaunch({ url: '/pages/index/index' })
}
}
}
</script>
<style lang="scss" scoped>
.planet {
min-height: 100vh;
background:
radial-gradient(circle at 50% 260rpx, rgba(255,255,255,0.72), rgba(255,255,255,0) 360rpx),
linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 42%, #F7EEFF 76%, #FFF8DE 100%);
position: relative;
overflow: hidden;
color: #12342F;
}
.planet-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1500rpx;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.bg-aura {
position: absolute;
border-radius: 50%;
opacity: 0.72;
}
.bg-aura-a {
width: 520rpx;
height: 520rpx;
right: -160rpx;
top: 74rpx;
background: radial-gradient(circle, rgba(79,183,255,0.34), rgba(79,183,255,0));
}
.bg-aura-b {
width: 460rpx;
height: 460rpx;
left: -180rpx;
top: 360rpx;
background: radial-gradient(circle, rgba(53,214,166,0.35), rgba(53,214,166,0));
animation-delay: -2.5s;
}
.bg-aura-c {
width: 420rpx;
height: 420rpx;
right: -110rpx;
top: 860rpx;
background: radial-gradient(circle, rgba(143,124,255,0.22), rgba(143,124,255,0));
animation-delay: -4s;
}
.bg-space-dust {
position: absolute;
border-radius: 50%;
background:
radial-gradient(circle, rgba(255,255,255,0.9) 0 2rpx, transparent 3rpx),
radial-gradient(circle, rgba(53,214,166,0.5) 0 2rpx, transparent 3rpx),
radial-gradient(circle, rgba(143,124,255,0.38) 0 2rpx, transparent 3rpx);
background-size: 86rpx 92rpx, 124rpx 116rpx, 156rpx 148rpx;
opacity: 0.38;
}
.bg-space-dust-a {
left: -120rpx;
top: 120rpx;
width: 980rpx;
height: 860rpx;
}
.bg-space-dust-b {
left: -180rpx;
top: 900rpx;
width: 1000rpx;
height: 760rpx;
animation-delay: -6s;
opacity: 0.24;
}
@keyframes dustDrift {
0% { transform: translate3d(0, 0, 0); }
100% { transform: translate3d(-86rpx, 92rpx, 0); }
}
.bg-shooting {
position: absolute;
width: 180rpx;
height: 3rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.9), rgba(79,183,255,0.25));
transform: rotate(-30deg);
opacity: 0.52;
}
.bg-shooting-a {
right: -160rpx;
top: 240rpx;
}
.bg-shooting-b {
right: -160rpx;
top: 780rpx;
width: 130rpx;
animation-delay: -2.8s;
opacity: 0.72;
}
@keyframes shooting {
0% { transform: translate3d(180rpx, -80rpx, 0) rotate(-30deg); opacity: 0; }
10% { opacity: 1; }
60% { opacity: 1; }
100% { transform: translate3d(-760rpx, 300rpx, 0) rotate(-30deg); opacity: 0; }
}
@keyframes auraFloat {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(18rpx, -34rpx, 0) scale(1.06); }
}
.bg-orbit {
position: absolute;
border: 2rpx solid rgba(255,255,255,0.72);
border-radius: 50%;
transform: rotate(-18deg);
}
.bg-orbit-a {
width: 920rpx;
height: 300rpx;
left: -60rpx;
top: 250rpx;
opacity: 0.34;
}
.bg-orbit-b {
width: 720rpx;
height: 210rpx;
right: -190rpx;
top: 680rpx;
opacity: 0.26;
}
.bg-star {
position: absolute;
background: rgba(255,255,255,0.92);
border-radius: 50%;
box-shadow: 0 0 12rpx rgba(79,183,255,0.45);
opacity: 0.55;
}
@keyframes twinkle {
0%, 100% { opacity: 0.28; transform: scale(0.8); }
50% { opacity: 0.85; transform: scale(1.28); }
}
.bg-planet {
position: absolute;
border-radius: 50%;
opacity: 0.8;
box-shadow: inset -18rpx -22rpx 42rpx rgba(18,52,47,0.08), 0 22rpx 60rpx rgba(53,214,166,0.18);
}
.bg-planet-a {
width: 120rpx;
height: 120rpx;
right: 34rpx;
top: 106rpx;
background: radial-gradient(circle at 32% 28%, #FFFFFF 0%, #BAF7CF 32%, #7DE2FF 100%);
}
.bg-planet-b {
width: 92rpx;
height: 92rpx;
left: 24rpx;
top: 760rpx;
background: radial-gradient(circle at 30% 30%, #FFFFFF 0%, #FFE7A8 36%, #FFB7D1 100%);
}
@keyframes float {
0%, 100% { transform: translateY(0) translateX(0); }
50% { transform: translateY(-24rpx) translateX(12rpx); }
}
.nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 20;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
background:
radial-gradient(circle at 50% 260rpx, rgba(255,255,255,0.72), rgba(255,255,255,0) 360rpx),
linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 100%);
overflow: visible;
}
.nav:after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -18rpx;
height: 20rpx;
background: linear-gradient(180deg, #EAF8FF 0%, rgba(234,248,255,0));
pointer-events: none;
}
.nav-back {
position: absolute;
left: 24rpx;
bottom: 14rpx;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255,255,255,.72);
box-shadow: 0 8rpx 20rpx rgba(18,52,47,.08);
}
.nav-back-icon {
color: #12342F;
font-size: 64rpx;
font-weight: 500;
padding-bottom: 10rpx;
padding-right: 4rpx;
line-height: 1;
transform: translateY(-2rpx);
}
.nav-title {
color: #12342F;
font-size: 32rpx;
font-weight: 900;
letter-spacing: 1rpx;
padding-bottom: 14rpx;
}
.planet-scroll {
position: relative;
z-index: 5;
height: 100vh;
box-sizing: border-box;
padding-left: 22rpx;
padding-right: 22rpx;
}
.guide-anchor-wrap {
position: relative;
}
.guide-focus-target {
position: relative;
z-index: 92;
border-radius: 34rpx;
box-shadow: 0 0 0 6rpx rgba(255,255,255,0.95), 0 0 0 14rpx rgba(53,214,166,0.34), 0 24rpx 70rpx rgba(18,52,47,0.26);
}
.guide-focus-layer {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 90;
pointer-events: none;
}
.guide-mask-piece {
position: fixed;
z-index: 90;
background: rgba(0,0,0,0.56);
pointer-events: auto;
}
.guide-mask-top {
top: 0;
left: 0;
right: 0;
}
.guide-mask-left {
left: 0;
}
.guide-mask-right {
right: 0;
}
.guide-mask-bottom {
left: 0;
right: 0;
bottom: 0;
}
.guide-focus-tip {
position: fixed;
z-index: 93;
pointer-events: auto;
padding: 22rpx 24rpx 22rpx;
border-radius: 28rpx;
background: rgba(255,255,255,0.96);
box-shadow: 0 24rpx 70rpx rgba(0,0,0,0.24);
}
.guide-focus-tip:after {
content: '';
position: absolute;
left: 70rpx;
width: 0;
height: 0;
border-left: 16rpx solid transparent;
border-right: 16rpx solid transparent;
}
.guide-focus-tip.tip-bottom:after {
top: -15rpx;
border-bottom: 16rpx solid rgba(255,255,255,0.96);
}
.guide-focus-tip.tip-top:after {
bottom: -15rpx;
border-top: 16rpx solid rgba(255,255,255,0.96);
}
.guide-focus-title {
margin-top: 8rpx;
color: #12342F;
font-size: 30rpx;
font-weight: 900;
}
.guide-focus-step {
color: #35BDA0;
font-size: 20rpx;
font-weight: 900;
letter-spacing: 1rpx;
}
.guide-focus-desc {
margin-top: 10rpx;
color: #42635E;
font-size: 23rpx;
font-weight: 800;
line-height: 1.45;
}
.guide-focus-actions {
margin-top: 18rpx;
display: flex;
align-items: center;
gap: 14rpx;
}
.guide-focus-skip {
flex: 1;
height: 64rpx;
line-height: 64rpx;
text-align: center;
border-radius: 999rpx;
background: rgba(18,52,47,0.06);
color: #7E9691;
font-size: 24rpx;
font-weight: 900;
}
.guide-focus-btn {
flex: 2;
height: 64rpx;
line-height: 64rpx;
text-align: center;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 24rpx;
font-weight: 900;
}
.bottom-space {
height: 60rpx;
}
.pool-detail-card {
padding: 28rpx;
border-radius: 36rpx;
background: rgba(255,255,255,0.86);
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.12);
}
.pool-rule-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 75;
background: rgba(18,52,47,0.34);
display: flex;
align-items: center;
justify-content: center;
padding: 0 32rpx;
}
.pool-detail-modal {
width: 100%;
max-height: 78vh;
overflow-y: auto;
background: #fff;
}
.pool-detail-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.pool-detail-kicker {
color: #59CBB5;
font-size: 20rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.pool-detail-title {
margin-top: 6rpx;
color: #12342F;
font-size: 34rpx;
font-weight: 900;
}
.pool-detail-time {
padding: 8rpx 16rpx;
border-radius: 999rpx;
background: rgba(255,184,77,0.16);
color: #D17812;
font-size: 22rpx;
font-weight: 900;
}
.modal-time {
display: inline-flex;
margin-top: 18rpx;
}
.pool-detail-close {
width: 56rpx;
height: 56rpx;
line-height: 52rpx;
border-radius: 50%;
text-align: center;
background: rgba(18,52,47,0.08);
color: #42635E;
font-size: 42rpx;
font-weight: 300;
}
.pool-detail-grid {
margin-top: 22rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14rpx;
}
.pool-detail-item {
padding: 18rpx;
border-radius: 24rpx;
background: rgba(245,255,251,0.78);
display: flex;
flex-direction: column;
}
.pool-detail-item text:first-child {
color: #12342F;
font-size: 32rpx;
font-weight: 900;
}
.pool-detail-item text:last-child {
margin-top: 6rpx;
color: #7E9691;
font-size: 22rpx;
}
.reward-list {
margin-top: 20rpx;
}
.reward-row {
display: flex;
justify-content: space-between;
padding: 14rpx 4rpx;
border-bottom: 1rpx solid rgba(18,52,47,0.06);
color: #42635E;
font-size: 24rpx;
font-weight: 800;
}
.reward-row text:last-child {
color: #22B889;
}
.pool-rule {
margin-top: 18rpx;
padding: 16rpx 18rpx;
border-radius: 22rpx;
background: rgba(79,183,255,0.1);
color: #42635E;
font-size: 23rpx;
line-height: 1.5;
}
.section-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 18rpx;
}
.section-kicker {
color: #35D6A6;
font-size: 19rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.section-kicker.hot { color: #FF7A00; }
.section-kicker.weak { color: #8EA4A0; }
.section-title {
margin-top: 6rpx;
color: #12342F;
font-size: 38rpx;
font-weight: 900;
}
.weak-title {
color: #42635E;
font-size: 34rpx;
}
.section-time {
flex-shrink: 0;
padding: 9rpx 15rpx;
border-radius: 999rpx;
background: rgba(53,214,166,0.12);
color: #12A97D;
font-size: 21rpx;
font-weight: 900;
}
.section-time.hot {
background: rgba(255,122,0,0.12);
color: #FF7A00;
}
.section-time.weak {
background: rgba(18,52,47,0.06);
color: #7E9691;
}
.flow-card,
.glory-card,
.task-center,
.play-card {
margin-top: 24rpx;
padding: 26rpx 22rpx;
border-radius: 34rpx;
background: rgba(255,255,255,0.82);
border: 2rpx solid rgba(255,255,255,0.94);
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
}
.flow-title {
color: #12342F;
font-size: 30rpx;
font-weight: 900;
}
.flow-sub {
margin-top: 8rpx;
color: #7E9691;
font-size: 22rpx;
font-weight: 800;
}
.flow-steps {
margin-top: 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14rpx;
}
.flow-step {
position: relative;
min-width: 0;
min-height: 128rpx;
padding: 16rpx 16rpx 16rpx 76rpx;
box-sizing: border-box;
border-radius: 28rpx;
background:
radial-gradient(circle at 94% 12%, rgba(79,183,255,0.14), transparent 30%),
linear-gradient(145deg, rgba(255,255,255,0.88), rgba(235,255,246,0.66));
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.1);
display: flex;
flex-direction: column;
justify-content: center;
overflow: visible;
}
.flow-dot {
position: absolute;
left: 16rpx;
top: 18rpx;
width: 52rpx;
height: 52rpx;
line-height: 52rpx;
text-align: center;
border-radius: 50%;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 24rpx;
font-weight: 900;
}
.flow-copy {
min-width: 0;
}
.flow-name {
color: #12342F;
font-size: 25rpx;
font-weight: 900;
line-height: 1.25;
}
.flow-desc {
margin-top: 6rpx;
color: #7E9691;
font-size: 19rpx;
font-weight: 800;
line-height: 1.25;
}
.flow-go {
margin-top: 10rpx;
align-self: flex-end;
padding: 6rpx 14rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 19rpx;
font-weight: 900;
box-shadow: 0 8rpx 18rpx rgba(53,214,166,0.16);
}
.flow-arrow {
position: absolute;
right: -16rpx;
top: 42rpx;
z-index: 2;
width: 32rpx;
height: 32rpx;
line-height: 30rpx;
border-radius: 50%;
text-align: center;
background: rgba(255,255,255,0.9);
color: #35BDA0;
font-size: 26rpx;
font-weight: 900;
box-shadow: 0 8rpx 18rpx rgba(53,214,166,0.12);
}
.glory-card {
background:
radial-gradient(circle at 92% 12%, rgba(255,184,77,0.2), transparent 34%),
linear-gradient(155deg, rgba(255,255,255,0.92), rgba(255,245,226,0.78));
box-shadow: 0 20rpx 48rpx rgba(255,122,0,0.12);
}
.glory-prizes {
margin-top: 20rpx;
display: flex;
gap: 8rpx;
}
.glory-prize {
flex: 1;
min-height: 82rpx;
border-radius: 22rpx;
background: rgba(255,255,255,0.72);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.glory-prize text:first-child {
color: #8A5A1F;
font-size: 20rpx;
font-weight: 900;
}
.glory-prize text:last-child {
margin-top: 4rpx;
color: #FF6B00;
font-size: 28rpx;
font-weight: 900;
font-family: DIN, Arial, sans-serif;
}
.glory-stats {
margin-top: 16rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12rpx;
}
.glory-stat {
padding: 16rpx 10rpx;
border-radius: 24rpx;
background: rgba(255,255,255,0.78);
display: flex;
flex-direction: column;
align-items: center;
}
.glory-stat text:first-child {
color: #12342F;
font-size: 28rpx;
font-weight: 900;
}
.glory-stat text:last-child {
margin-top: 6rpx;
color: #7E9691;
font-size: 20rpx;
font-weight: 800;
}
.glory-btn {
margin-top: 18rpx;
height: 82rpx;
line-height: 82rpx;
border-radius: 999rpx;
text-align: center;
background: linear-gradient(135deg, #FF7A00, #FFB84D);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 900;
box-shadow: 0 16rpx 34rpx rgba(255,122,0,0.22);
}
.task-list {
margin-top: 20rpx;
display: flex;
flex-direction: column;
gap: 14rpx;
}
.task-item {
display: flex;
align-items: center;
gap: 16rpx;
padding: 18rpx;
border-radius: 26rpx;
background: rgba(255,255,255,0.72);
border: 2rpx solid rgba(255,255,255,0.92);
}
.task-icon {
width: 70rpx;
height: 70rpx;
line-height: 70rpx;
text-align: center;
border-radius: 24rpx;
background: linear-gradient(145deg, #FFF3D8, #E8FFF4);
color: #12342F;
font-size: 26rpx;
font-weight: 900;
flex-shrink: 0;
}
.task-copy {
flex: 1;
min-width: 0;
}
.task-name {
color: #12342F;
font-size: 27rpx;
font-weight: 900;
}
.task-reward {
margin-top: 6rpx;
color: #7E9691;
font-size: 22rpx;
font-weight: 800;
}
.task-action {
flex-shrink: 0;
width: 122rpx;
height: 62rpx;
line-height: 62rpx;
text-align: center;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 23rpx;
font-weight: 900;
}
.task-action.done {
background: rgba(18,52,47,0.08);
color: #8EA4A0;
}
.play-card {
background: rgba(255,255,255,0.62);
box-shadow: 0 14rpx 30rpx rgba(18,52,47,0.06);
}
.play-grid {
margin-top: 18rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12rpx;
}
.play-entry {
min-height: 104rpx;
padding: 18rpx;
border-radius: 24rpx;
background: rgba(255,255,255,0.68);
border: 2rpx solid rgba(255,255,255,0.82);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.play-entry text:first-child {
color: #42635E;
font-size: 24rpx;
font-weight: 900;
}
.play-entry text:last-child {
margin-top: 12rpx;
color: #35B58C;
font-size: 20rpx;
font-weight: 900;
}
.play-entry.disabled {
opacity: .58;
}
.primary-loop {
position: relative;
z-index: 8;
}
.task-center {
padding: 22rpx 20rpx;
background: rgba(255,255,255,0.66);
box-shadow: 0 14rpx 30rpx rgba(53,214,166,0.08);
}
.task-center .section-title {
font-size: 34rpx;
}
.task-item {
min-height: 112rpx;
}
.secondary-play {
opacity: .96;
}
.planet-quick-row {
margin-top: 24rpx;
display: flex;
gap: 16rpx;
align-items: stretch;
}
.quick-box {
width: calc((100% - 16rpx) / 2);
flex: 0 0 calc((100% - 16rpx) / 2);
min-height: 368rpx;
}
.planet-side-col {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 14rpx;
}
.planet-map-card {
min-height: 164rpx;
padding: 20rpx 18rpx;
box-sizing: border-box;
border-radius: 32rpx;
background:
radial-gradient(circle at 18% 30%, rgba(53,214,166,0.16), transparent 24%),
radial-gradient(circle at 86% 18%, rgba(79,183,255,0.18), transparent 28%),
linear-gradient(145deg, #FFFFFF, #E5FFF1);
color: #22B889;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-shadow: 0 14rpx 28rpx rgba(53,214,166,0.16);
border: 2rpx solid rgba(255,255,255,0.96);
position: relative;
overflow: hidden;
}
.planet-map-orb {
position: absolute;
left: 14rpx;
top: 20rpx;
width: 30rpx;
height: 30rpx;
border-radius: 50%;
background: linear-gradient(145deg, #B9FFE3, #4FB7FF);
box-shadow: 0 0 22rpx rgba(53,214,166,0.28);
opacity: .72;
}
.planet-map-head {
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rpx;
}
.planet-map-title {
flex: 1;
color: #13A77C;
font-size: 24rpx;
font-weight: 900;
line-height: 1.18;
text-align: right;
white-space: nowrap;
}
.planet-map-btn {
position: relative;
padding: 5rpx 15rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 18rpx;
font-weight: 900;
box-shadow: 0 10rpx 20rpx rgba(53,214,166,0.2);
white-space: nowrap;
}
.planet-map-plays {
position: relative;
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 8rpx;
}
.planet-map-plays text {
padding: 7rpx 10rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.66);
color: #42635E;
font-size: 18rpx;
font-weight: 900;
line-height: 1;
box-shadow: inset 0 1rpx 0 rgba(255,255,255,0.86);
}
.quick-me {
flex: 1;
min-height: 0;
}
/* 骨架屏 */
.skeleton {
padding-top: 22rpx;
}
.sk-block {
border-radius: 36rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.45) 25%, rgba(255,255,255,0.9) 37%, rgba(255,255,255,0.45) 63%);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
margin-bottom: 24rpx;
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
}
.sk-header { height: 520rpx; border-radius: 48rpx; }
.sk-me { height: 150rpx; }
.sk-row { height: 220rpx; }
.sk-list { height: 330rpx; }
@keyframes shimmer {
0% { background-position: 100% 50%; }
100% { background-position: 0 50%; }
}
/* 宝箱结果 */
.box-result-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(18,52,47,0.28);
backdrop-filter: blur(8px);
z-index: 60;
display: flex;
align-items: center;
justify-content: center;
}
.box-result {
position: relative;
width: 540rpx;
padding: 62rpx 40rpx 42rpx;
border-radius: 48rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.92), rgba(244,255,249,0.78));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 34rpx 80rpx rgba(53,214,166,0.24);
text-align: center;
overflow: hidden;
}
.box-result-burst {
position: absolute;
top: -60rpx;
left: 50%;
transform: translateX(-50%);
width: 400rpx;
height: 400rpx;
background: radial-gradient(circle, rgba(255,184,77,0.36), transparent 62%);
animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: translateX(-50%) scale(0.9); opacity: 0.6; }
50% { transform: translateX(-50%) scale(1.1); opacity: 1; }
}
.box-result-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 190rpx;
height: 190rpx;
border-radius: 46rpx;
background: linear-gradient(145deg, #FFFFFF, #DDF8FF 48%, #E8DFFF);
color: #22B889;
font-size: 24rpx;
font-weight: 900;
letter-spacing: 1rpx;
position: relative;
animation: bounce 1s ease infinite;
box-shadow: inset -16rpx -20rpx 34rpx rgba(143,124,255,0.16), 0 22rpx 48rpx rgba(79,183,255,0.18);
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12rpx); }
}
.box-result-title {
margin-top: 20rpx;
color: #12342F;
font-size: 36rpx;
font-weight: 800;
}
.box-result-desc {
margin-top: 12rpx;
color: #42635E;
font-size: 26rpx;
}
.box-result-btn {
margin-top: 36rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 700;
box-shadow: 0 18rpx 36rpx rgba(53,214,166,0.25);
}
.win-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 86;
background: rgba(18,52,47,0.34);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
padding: 0 42rpx;
}
.win-card {
position: relative;
width: 100%;
padding: 62rpx 40rpx 42rpx;
border-radius: 52rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.96), rgba(255,247,225,0.9) 54%, rgba(235,255,246,0.88));
border: 2rpx solid rgba(255,255,255,0.94);
box-shadow: 0 38rpx 90rpx rgba(255,184,77,0.24);
text-align: center;
overflow: hidden;
}
.win-burst {
position: absolute;
left: 50%;
top: -120rpx;
width: 520rpx;
height: 520rpx;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(255,206,89,0.46), transparent 66%);
animation: pulse 1.5s ease-in-out infinite;
}
.win-medal {
position: relative;
margin: 0 auto;
width: 178rpx;
height: 178rpx;
line-height: 178rpx;
border-radius: 50%;
background: linear-gradient(145deg, #FFF7B8, #FFB84D);
color: #9A5A00;
font-size: 34rpx;
font-weight: 900;
letter-spacing: 3rpx;
box-shadow: inset -12rpx -16rpx 28rpx rgba(154,90,0,0.12), 0 22rpx 48rpx rgba(255,184,77,0.28);
animation: bounce 1s ease infinite;
}
.win-title {
position: relative;
margin-top: 24rpx;
color: #12342F;
font-size: 42rpx;
font-weight: 900;
}
.win-sub {
position: relative;
margin-top: 10rpx;
color: #8A6A26;
font-size: 26rpx;
font-weight: 800;
}
.win-amount {
position: relative;
margin-top: 18rpx;
color: #FF7A59;
font-size: 64rpx;
font-weight: 900;
font-family: DIN, Arial, sans-serif;
}
.win-amount text {
font-size: 32rpx;
margin-right: 4rpx;
}
.dl-sub { margin-top: 8rpx; color: #6B817D; font-size: 20rpx; }
.win-tip {
position: relative;
margin-top: 10rpx;
color: #42635E;
font-size: 25rpx;
line-height: 1.5;
}
.win-btn {
position: relative;
margin-top: 34rpx;
height: 82rpx;
line-height: 82rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #FFB84D, #FF7A59);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 900;
box-shadow: 0 18rpx 38rpx rgba(255,122,89,0.26);
}
.win-btn.disabled {
opacity: 0.68;
}
.task-drawer-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 78;
background: rgba(18,52,47,0.26);
display: flex;
align-items: flex-end;
}
.task-drawer {
width: 100%;
max-height: 78vh;
overflow-y: auto;
padding: 18rpx 24rpx 42rpx;
box-sizing: border-box;
border-radius: 42rpx 42rpx 0 0;
background:
radial-gradient(circle at 92% 0%, rgba(53,214,166,0.16), transparent 30%),
linear-gradient(155deg, rgba(255,255,255,0.98), rgba(244,255,250,0.94));
box-shadow: 0 -24rpx 70rpx rgba(18,52,47,0.18);
border: 2rpx solid rgba(255,255,255,0.96);
}
.drawer-handle {
margin: 0 auto 18rpx;
width: 88rpx;
height: 10rpx;
border-radius: 999rpx;
background: rgba(18,52,47,0.12);
}
.guide-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(18,52,47,0.32);
z-index: 70;
display: flex;
align-items: center;
justify-content: center;
padding: 0 36rpx;
}
.guide-card {
position: relative;
width: 100%;
padding: 44rpx 34rpx 34rpx;
border-radius: 46rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.96), rgba(240,255,248,0.88));
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 34rpx 80rpx rgba(53,214,166,0.24);
overflow: hidden;
}
.guide-card:before {
content: '';
position: absolute;
right: -100rpx;
top: -120rpx;
width: 320rpx;
height: 320rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(79,183,255,0.24), transparent 68%);
}
.guide-kicker {
position: relative;
color: #59CBB5;
font-size: 20rpx;
font-weight: 900;
letter-spacing: 3rpx;
}
.guide-title {
position: relative;
margin-top: 10rpx;
color: #12342F;
font-size: 40rpx;
font-weight: 900;
}
.guide-list {
position: relative;
margin-top: 26rpx;
}
.guide-item {
margin-top: 16rpx;
padding: 20rpx;
border-radius: 28rpx;
background: rgba(255,255,255,0.72);
display: flex;
gap: 16rpx;
color: #42635E;
font-size: 25rpx;
line-height: 1.45;
font-weight: 700;
}
.guide-item text {
width: 42rpx;
height: 42rpx;
line-height: 42rpx;
border-radius: 50%;
text-align: center;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 22rpx;
font-weight: 900;
flex-shrink: 0;
}
.guide-btn {
position: relative;
margin-top: 30rpx;
height: 82rpx;
line-height: 82rpx;
border-radius: 999rpx;
text-align: center;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 28rpx;
font-weight: 900;
box-shadow: 0 18rpx 36rpx rgba(53,214,166,0.24);
}
</style>