wangfukang 2 weeks ago
parent
commit
77593b9ab6
  1. 37
      package1/components/planet/hunt-modal.vue
  2. 76
      package1/components/planet/planet-box.vue
  3. 42
      package1/components/planet/planet-daily-loop.vue
  4. 3
      package1/components/planet/planet-header.vue
  5. 88
      package1/components/planet/planet-me.vue
  6. 67
      package1/components/planet/planet-operate.vue
  7. 2
      package1/components/planet/rank-item.vue
  8. 203
      package1/planet/index.vue
  9. 108
      package1/planet/more.vue
  10. 318
      package1/planet/ticketLog.vue

37
package1/components/planet/hunt-modal.vue

@ -58,14 +58,14 @@
},
resultBadge() {
if (!this.result) return 'GO'
if (this.result.result === 'success') return 'GET'
if (this.result.result === 'shield') return 'SAFE'
if (this.result.result === 'success') return '哈哈'
if (this.result.result === 'shield') return '哭哭'
return 'MISS'
},
resultTitle() {
if (!this.result) return ''
if (this.result.result === 'success') return '追捕成功'
if (this.result.result === 'shield') return '目标已防护'
if (this.result.result === 'shield') return '被防御塔击中'
return '追捕扑空'
}
},
@ -216,20 +216,31 @@
height: 160rpx;
line-height: 160rpx;
text-align: center;
border-radius: 50%;
background: linear-gradient(145deg, #E5FFF1, #EAF8FF);
color: #22B889;
font-size: 30rpx;
border-radius: 54rpx 54rpx 64rpx 64rpx;
background: linear-gradient(145deg, #FFF2B8, #FFB84D);
color: #7A3C00;
font-size: 38rpx;
font-weight: 900;
letter-spacing: 2rpx;
animation: pop 0.5s ease;
box-shadow: inset 0 0 0 2rpx rgba(255,255,255,0.92), 0 18rpx 42rpx rgba(53,214,166,0.14);
animation: pirateLaugh 0.72s ease infinite;
box-shadow: inset 0 0 0 2rpx rgba(255,255,255,0.92), 0 18rpx 42rpx rgba(255,184,77,0.18);
position: relative;
}
.hm-result-emoji:before {
content: '';
position: absolute;
left: 30rpx;
right: 30rpx;
top: 48rpx;
height: 18rpx;
border-radius: 999rpx;
background: rgba(122,60,0,0.18);
}
@keyframes pop {
0% { transform: scale(0.3); opacity: 0; }
70% { transform: scale(1.2); }
100% { transform: scale(1); opacity: 1; }
@keyframes pirateLaugh {
0%, 100% { transform: translateY(0) rotate(-3deg); }
50% { transform: translateY(-8rpx) rotate(3deg); }
}
.hm-result-title {

76
package1/components/planet/planet-box.vue

@ -1,5 +1,5 @@
<template>
<view class="pb" @tap="onOpen">
<view class="pb" :class="{compact: compact}" @tap="onOpen">
<view class="pb-tag">{{available ? 'TODAY DROP' : 'TOMORROW DROP'}}</view>
<view class="pb-visual">
<view class="pb-glow" v-if="available"></view>
@ -28,6 +28,10 @@
opening: {
type: Boolean,
default: false
},
compact: {
type: Boolean,
default: false
}
},
methods: {
@ -175,4 +179,74 @@
color: #7E9691;
box-shadow: none;
}
.pb.compact {
flex: 1;
min-width: 0;
min-height: 190rpx;
margin-top: 0;
padding: 22rpx 18rpx;
border-radius: 34rpx;
flex-direction: column;
align-items: flex-start;
}
.pb.compact .pb-tag {
right: 18rpx;
top: 14rpx;
font-size: 15rpx;
}
.pb.compact .pb-visual {
width: 94rpx;
height: 94rpx;
}
.pb.compact .pb-glow {
width: 112rpx;
height: 112rpx;
}
.pb.compact .pb-box {
width: 66rpx;
height: 68rpx;
border-radius: 18rpx;
}
.pb.compact .pb-box-lid {
left: -6rpx;
top: -14rpx;
width: 78rpx;
height: 26rpx;
}
.pb.compact .pb-box-face {
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
font-size: 22rpx;
}
.pb.compact .pb-mid {
margin-left: 0;
margin-top: 10rpx;
}
.pb.compact .pb-title {
font-size: 27rpx;
}
.pb.compact .pb-desc {
font-size: 20rpx;
line-height: 28rpx;
margin-top: 6rpx;
}
.pb.compact .pb-btn {
margin-top: 14rpx;
height: 48rpx;
line-height: 48rpx;
padding: 0 18rpx;
font-size: 21rpx;
}
</style>

42
package1/components/planet/planet-daily-loop.vue

@ -42,31 +42,6 @@
<view class="main-arrow"></view>
</view>
<view class="sub-task-list">
<view
class="sub-task sign"
:class="{done: taskDone('sign')}"
@tap="onCollect">
<view class="sub-head">
<view class="task-icon icon-spark"></view>
<view class="task-badge">{{taskDone('sign') ? '✓ 已完成' : '待签到'}}</view>
</view>
<view class="sub-title">签到收券</view>
<view class="sub-desc">奖励 +1 星球券</view>
</view>
<view
class="sub-task hunt"
:class="{done: taskDone('hunt')}"
@tap="onRevenge">
<view class="sub-head">
<view class="task-icon icon-gift"></view>
<view class="task-badge">{{taskDone('hunt') ? '✓ 已完成' : '去搜查 →'}}</view>
</view>
<view class="sub-title">星球搜查令</view>
<view class="sub-desc">随机搜查白嫖得券</view>
</view>
</view>
<view class="dl-stat">
<view class="stat-card collected">
<text>{{data.todayCollectedTickets || 0}}</text>
@ -112,16 +87,13 @@
doneCount() {
let count = 0
if (this.taskDone('pool')) count += 1
if (this.taskDone('sign')) count += 1
if (this.taskDone('hunt')) count += 1
return count
},
progressPercent() {
return Math.round((this.doneCount / 3) * 100)
return this.taskDone('pool') ? 100 : 0
},
progressTitle() {
const left = 3 - this.doneCount
return left > 0 ? ('离瓜分奖金池还差 ' + left + ' 步') : '今日白嫖进度已完成'
return this.taskDone('pool') ? '今日已参与奖金池' : '投入星球券瓜分奖金池'
},
boostText() {
const current = Number(this.data.myProbability || 0)
@ -136,19 +108,9 @@
},
methods: {
taskDone(type) {
if (type === 'sign') return !!this.data.signedToday && !this.data.boxAvailable
if (type === 'hunt') return !!this.data.searchedToday || !!this.data.huntedToday || (this.data.remainSearchCount === 0)
if (type === 'pool') return (this.data.myPoolTickets || 0) > 0
return false
},
onCollect() {
this.playReward('+1 星球券', 'sign')
this.$emit('collect')
},
onRevenge() {
this.playReward('随机奖励', 'hunt')
this.$emit('revenge')
},
playReward(text, type) {
if (this.rewardTimer) clearTimeout(this.rewardTimer)
this.rewardFloat = {

3
package1/components/planet/planet-header.vue

@ -5,7 +5,6 @@
<view class="ph-title-row">
<text class="ph-title">Hello白嫖居民</text>
<view class="ph-actions">
<text class="ph-action" @tap="goTicketLog">得券明细</text>
<text class="ph-action ph-action-main" @tap="goDraw">开奖记录</text>
</view>
</view>
@ -65,7 +64,7 @@
</view>
</view>
<view class="ph-squirrel">
<view class="ph-squirrel" @tap="goTicketLog">
<image class="ph-squirrel-img" src="/static/images/img/loading.gif" mode="aspectFit"></image>
<view class="ph-squirrel-tag">我的星球券 {{data.myTicketCount || 0}} </view>
</view>

88
package1/components/planet/planet-me.vue

@ -1,5 +1,5 @@
<template>
<view class="pm">
<view class="pm" :class="{compact: compact}">
<view class="pm-stamp">PLANET PASS</view>
<view class="pm-main">
<view class="pm-left">
@ -39,6 +39,10 @@
data: {
type: Object,
default: () => ({})
},
compact: {
type: Boolean,
default: false
}
},
data() {
@ -233,4 +237,86 @@
color: #22B889;
font-family: DIN, Arial, sans-serif;
}
.pm.compact {
flex: 1;
min-width: 0;
min-height: 190rpx;
margin-top: 0;
padding: 22rpx 18rpx;
border-radius: 34rpx;
}
.pm.compact .pm-stamp {
right: 18rpx;
top: 16rpx;
font-size: 16rpx;
letter-spacing: 2rpx;
}
.pm.compact .pm-avatar {
width: 68rpx;
height: 68rpx;
border-width: 4rpx;
}
.pm.compact .pm-info {
margin-left: 12rpx;
}
.pm.compact .pm-name {
font-size: 27rpx;
}
.pm.compact .pm-sub {
margin-top: 6rpx;
}
.pm.compact .pm-sub text {
font-size: 19rpx;
margin-right: 8rpx;
}
.pm.compact .pm-ticket {
min-width: 94rpx;
height: 74rpx;
border-radius: 22rpx;
padding: 0 10rpx;
}
.pm.compact .pm-ticket-num {
font-size: 28rpx;
line-height: 30rpx;
}
.pm.compact .pm-ticket-label {
font-size: 17rpx;
}
.pm.compact .pm-sign {
margin-top: 16rpx;
height: 50rpx;
line-height: 50rpx;
font-size: 21rpx;
}
.pm.compact .pm-buffs {
margin-top: 14rpx;
}
.pm.compact .pm-buffs-label {
font-size: 19rpx;
margin-right: 8rpx;
}
.pm.compact .pm-buff {
width: 40rpx;
height: 40rpx;
border-radius: 14rpx;
margin-right: 8rpx;
}
.pm.compact .pm-buff-emoji {
font-size: 16rpx;
}
</style>

67
package1/components/planet/planet-operate.vue

@ -4,11 +4,11 @@
<view>
<view class="po-kicker">CAMPUS FARM</view>
<view class="po-title">松鼠星际农场</view>
<view class="po-sub">种券仓库防御塔和地标争夺每天都有一点成长</view>
<view class="po-sub">种券和防御塔保留每天一点成长轻量经营</view>
</view>
<view class="po-stamina">
<text class="po-stamina-num">{{state.stamina || 0}}</text>
<text class="po-stamina-label">体力</text>
<view class="po-tower-badge">
<text>Lv{{state.towerLevel || 1}}</text>
<text>防御塔</text>
</view>
</view>
@ -28,42 +28,12 @@
</scroll-view>
</view>
<view class="po-card warehouse">
<view class="po-icon">SAFE</view>
<view class="po-card-title">松鼠仓库 Lv{{state.warehouseLevel || 1}}</view>
<view class="po-progress"><view class="po-progress-in" :style="{width: warehousePercent + '%'}"></view></view>
<view class="po-card-desc">{{state.warehouseTicketCount || 0}} / {{state.warehouseCapacity || 10}} 张安全券</view>
<view class="po-actions">
<text @tap="$emit('store')">存入</text>
<text @tap="$emit('take')">取出</text>
<text @tap="$emit('upgradeWarehouse')">升级</text>
</view>
</view>
<view class="po-card tower">
<view class="po-icon">TOWER</view>
<view class="po-card-title">防御塔 Lv{{state.towerLevel || 1}}</view>
<view class="po-card-desc">拦截 {{state.towerInterceptRate || 0}}% · 伤害 {{state.towerDamage || 0}}</view>
<view class="po-card-desc">拦截 {{state.towerInterceptRate || 0}}% · 反击 {{state.towerCounterRate || 0}}% · 伤害 {{state.towerDamage || 0}}</view>
<view class="po-main-btn" @tap="$emit('upgradeTower')">升级防御</view>
</view>
<view class="po-card search">
<view class="po-icon">RADAR</view>
<view class="po-card-title">星际搜查</view>
<view class="po-card-desc">今日 {{state.dailySearchCount || 0}} · 获得 {{state.dailySearchGain || 0}} </view>
<view class="po-main-btn" @tap="$emit('search')">派出松鼠宇航员</view>
</view>
</view>
<view class="po-land">
<view class="po-land-title">校园地标</view>
<scroll-view scroll-x class="po-land-scroll">
<view class="po-land-item" v-for="(l,i) in landmarks" :key="i" @tap="$emit('bid', l)">
<view class="po-land-icon">{{l.icon || 'LAND'}}</view>
<view class="po-land-name">{{l.name}}</view>
<view class="po-land-benefit">{{benefitText(l)}}</view>
</view>
</scroll-view>
</view>
<view class="po-orders" v-if="treeOrders.length">
@ -93,25 +63,11 @@
},
treeOrders() {
return (this.data && this.data.treeOrders) || []
},
landmarks() {
return (this.data && this.data.landmarks) || []
},
warehousePercent() {
const cap = this.state.warehouseCapacity || 10
return Math.min(100, Math.round(((this.state.warehouseTicketCount || 0) / cap) * 100))
}
},
methods: {
rateText(rate) {
return Math.round((Number(rate || 0)) * 100) + '%'
},
benefitText(item) {
if (item.benefitType === 'ticket') return '每日+' + item.benefitValue + '券'
if (item.benefitType === 'stamina_speed') return '体力+' + item.benefitValue + '%'
if (item.benefitType === 'search_rate') return '搜查+' + item.benefitValue + '%'
if (item.benefitType === 'tower_damage') return '防御+' + item.benefitValue + '%'
return '校园增益'
}
}
}
@ -123,9 +79,9 @@
.po-kicker { color: #59CBB5; font-size: 20rpx; font-weight: 900; letter-spacing: 2rpx; }
.po-title { margin-top: 6rpx; color: #12342F; font-size: 40rpx; font-weight: 900; }
.po-sub { margin-top: 8rpx; color: #6B817D; font-size: 24rpx; }
.po-stamina { width: 124rpx; height: 124rpx; border-radius: 44rpx; background: linear-gradient(145deg, rgba(255,255,255,0.96), rgba(225,250,255,0.82)); display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 18rpx 42rpx rgba(79,183,255,0.16); }
.po-stamina-num { color: #22B889; font-size: 36rpx; font-weight: 900; }
.po-stamina-label { color: #6B817D; font-size: 20rpx; }
.po-tower-badge { width: 124rpx; height: 124rpx; border-radius: 44rpx; background: linear-gradient(145deg, rgba(255,255,255,0.96), rgba(225,250,255,0.82)); display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 18rpx 42rpx rgba(79,183,255,0.16); }
.po-tower-badge text:first-child { color: #22B889; font-size: 36rpx; font-weight: 900; }
.po-tower-badge text:last-child { color: #6B817D; font-size: 20rpx; }
.po-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18rpx; }
.po-card { position: relative; min-height: 250rpx; padding: 24rpx; border-radius: 38rpx; background: linear-gradient(155deg, rgba(255,255,255,0.9), rgba(241,255,249,0.72)); border: 2rpx solid rgba(255,255,255,0.9); box-shadow: 0 20rpx 46rpx rgba(53,214,166,0.12); overflow: hidden; }
.po-card.tree { grid-column: span 2; min-height: 280rpx; }
@ -143,13 +99,8 @@
.po-actions { display: flex; gap: 10rpx; margin-top: 18rpx; }
.po-actions text, .po-main-btn { display: inline-flex; align-items: center; justify-content: center; height: 54rpx; padding: 0 18rpx; border-radius: 999rpx; background: linear-gradient(135deg, #DFFBF1, #E8F6FF); color: #22B889; font-size: 22rpx; font-weight: 800; }
.po-main-btn { margin-top: 18rpx; }
.po-land, .po-orders { margin-top: 20rpx; padding: 24rpx; border-radius: 38rpx; background: rgba(255,255,255,0.62); border: 2rpx solid rgba(255,255,255,0.86); box-shadow: 0 18rpx 42rpx rgba(79,183,255,0.1); }
.po-orders { margin-top: 20rpx; padding: 24rpx; border-radius: 38rpx; background: rgba(255,255,255,0.62); border: 2rpx solid rgba(255,255,255,0.86); box-shadow: 0 18rpx 42rpx rgba(79,183,255,0.1); }
.po-land-title { color: #12342F; font-size: 30rpx; font-weight: 900; }
.po-land-scroll { margin-top: 18rpx; white-space: nowrap; width: 100%; }
.po-land-item { display: inline-flex; flex-direction: column; justify-content: center; width: 190rpx; height: 170rpx; margin-right: 18rpx; padding: 18rpx; border-radius: 32rpx; background: linear-gradient(155deg, rgba(255,255,255,0.92), rgba(247,239,255,0.68)); box-sizing: border-box; }
.po-land-icon { color: #8F7CFF; font-size: 20rpx; font-weight: 900; }
.po-land-name { margin-top: 12rpx; color: #12342F; font-size: 27rpx; font-weight: 900; }
.po-land-benefit { margin-top: 8rpx; color: #6B817D; font-size: 22rpx; }
.po-order { display: flex; align-items: center; justify-content: space-between; margin-top: 18rpx; padding: 18rpx; border-radius: 28rpx; background: rgba(255,255,255,0.78); }
.po-order-title { color: #12342F; font-size: 26rpx; font-weight: 800; }
.po-order-desc { margin-top: 6rpx; color: #6B817D; font-size: 22rpx; }

2
package1/components/planet/rank-item.vue

@ -23,7 +23,7 @@
@tap.stop="onHunt">
<text v-if="item.shielded">防护中</text>
<text v-else-if="item.self">榜上有我</text>
<text v-else>发起追捕</text>
<text v-else>发起掠夺</text>
</view>
<view class="ri-extra" v-if="item.bountyTickets > 0 && !item.self">额外悬赏+{{item.bountyTickets}}</view>
</view>

203
package1/planet/index.vue

@ -43,25 +43,25 @@
<planet-daily-loop
:data="home.dailyLoop"
:my-ticket-count="home.myTicketCount"
@collect="onDailyCollect"
@revenge="onDailyRevenge"
@more="goMore"
@join="onJoinPool">
</planet-daily-loop>
<planet-adventure-entry @start="goAdventure" @arena="goArena"></planet-adventure-entry>
<planet-me
:data="home"
@sign="onSign"
@openbox="openBox">
</planet-me>
<view class="planet-quick-row">
<planet-box
compact
:available="home.boxAvailable"
:opening="boxOpening"
@open="openBox">
</planet-box>
<planet-me
compact
:data="home"
@sign="onSign">
</planet-me>
</view>
<view class="bottom-space"></view>
</block>
@ -86,6 +86,22 @@
</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="guideVisible" class="guide-mask" @tap="guideVisible=false">
<view class="guide-card" @tap.stop>
<view class="guide-kicker">BANJINLI GUIDE</view>
@ -160,6 +176,11 @@
show: false,
data: {}
},
winningModal: {
show: false,
data: {},
receiving: false
},
guideVisible: false,
huntModal: {
show: false,
@ -180,6 +201,10 @@
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)
}
},
onLoad() {
@ -229,6 +254,7 @@
this.loading = false
if (res.code == 200 && res.result) {
this.home = res.result
this.showUnreadWinning()
} else if (res.message) {
this.tui.toast(res.message)
}
@ -266,6 +292,49 @@
this.boxOpening = false
})
},
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('不能追捕自己')
@ -354,10 +423,10 @@
setTimeout(() => {
if (res.code == 200 && res.result) {
this.huntModal.result = {
result: res.result.intercepted ? 'shield' : 'success',
result: res.result.countered ? 'shield' : (res.result.intercepted ? 'fail' : 'success'),
message: res.result.message,
gainTickets: res.result.gainTickets,
totalGain: res.result.gainTickets,
totalGain: res.result.gainTickets || 0,
remainHunt: res.result.remainSearchCount
}
this.huntModal.phase = 'result'
@ -680,6 +749,13 @@
height: 60rpx;
}
.planet-quick-row {
margin-top: 24rpx;
display: flex;
gap: 18rpx;
align-items: stretch;
}
/* 骨架屏 */
.skeleton {
padding-top: 22rpx;
@ -791,6 +867,113 @@
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;
}
.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;
}
.guide-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;

108
package1/planet/more.vue

@ -11,8 +11,8 @@
<view class="hero">
<view>
<view class="hero-kicker">PLANET SUPPLY</view>
<view class="hero-title">把长期玩法都放在这里</view>
<view class="hero-sub">券树仓库防御塔地标任务和能量站想深入经营时再进来</view>
<view class="hero-title">轻量经营补给地图</view>
<view class="hero-sub">保留松鼠星际农场券树培育和防御塔成长玩法更轻更直接</view>
</view>
<view class="hero-ticket">
<text>{{home.myTicketCount || 0}}</text>
@ -26,12 +26,7 @@
:data="home.operate"
@plant="onPlantTree"
@harvest="onHarvestTree"
@store="onStoreWarehouse"
@take="onTakeWarehouse"
@upgradeWarehouse="onUpgradeWarehouse"
@upgradeTower="onUpgradeTower"
@search="onRandomSearch"
@bid="onBidLandmark">
@upgradeTower="onUpgradeTower">
</planet-operate>
<planet-tasks
@ -39,38 +34,20 @@
@claim="onClaimTask">
</planet-tasks>
<buff-shop
:list="home.buffShop"
:my-ticket="home.myTicketCount"
@buy="onBuyBuff">
</buff-shop>
<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>
</template>
<script>
import planetTasks from '@/package1/components/planet/planet-tasks.vue'
import huntModal from '@/package1/components/planet/hunt-modal.vue'
import buffShop from '@/package1/components/planet/buff-shop.vue'
import planetNews from '@/package1/components/planet/planet-news.vue'
import planetOperate from '@/package1/components/planet/planet-operate.vue'
export default {
components: {
planetTasks,
huntModal,
buffShop,
planetNews,
planetOperate
},
@ -86,15 +63,8 @@
home: {
myTicketCount: 0,
tasks: [],
buffShop: [],
newsList: [],
operate: {}
},
huntModal: {
show: false,
phase: 'searching',
result: null,
target: {}
}
}
},
@ -149,20 +119,6 @@
if (res.code == 200) this.loadHome(true)
})
},
onBuyBuff(buff) {
if (buff.active) {
this.tui.toast('该增益正在生效中')
return
}
if (this.home.myTicketCount < buff.costTickets) {
this.tui.toast('星球券不足')
return
}
this.tui.modal('确认购买', `消耗 ${buff.costTickets} 张星球券购买「${buff.name}」?`, true, (ok) => {
if (!ok) return
this.operateRequest('/app/planet/buff/buy', { buffId: buff.id }, '购买成功')
})
},
promptTickets(title, callback) {
uni.showModal({
title,
@ -200,69 +156,11 @@
}
this.operateRequest('/app/planet/tree/harvest', { orderId: order.id }, '收获成功')
},
onStoreWarehouse() {
this.promptTickets('存入松鼠仓库', (tickets) => {
this.operateRequest('/app/planet/warehouse/store', { tickets }, '存入成功')
})
},
onTakeWarehouse() {
this.promptTickets('从仓库取出', (tickets) => {
this.operateRequest('/app/planet/warehouse/take', { tickets }, '取出成功')
})
},
onUpgradeWarehouse() {
this.tui.modal('升级仓库', '确认消耗星球券升级松鼠仓库?', true, (ok) => {
if (ok) this.operateRequest('/app/planet/warehouse/upgrade', {}, '升级成功')
})
},
onUpgradeTower() {
this.tui.modal('升级防御塔', '确认消耗星球券提升拦截能力?', true, (ok) => {
if (ok) this.operateRequest('/app/planet/tower/upgrade', {}, '升级成功')
})
},
onRandomSearch() {
this.startSearch('/app/planet/search/random', {}, { nickname: '随机同校区玩家' })
},
onBidLandmark(item) {
this.promptTickets(`争夺${item.name}`, (tickets) => {
this.operateRequest('/app/planet/landmark/bid', { landmarkId: item.id, tickets }, '投入成功')
})
},
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.intercepted ? 'shield' : 'success',
message: res.result.message,
gainTickets: res.result.gainTickets,
totalGain: res.result.gainTickets,
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
})
},
closeHunt() {
this.huntModal.show = false
this.huntModal.result = null
},
goBack() {
uni.navigateBack({
delta: 1,

318
package1/planet/ticketLog.vue

@ -7,7 +7,7 @@
</view>
<scroll-view scroll-y class="tl-scroll" :style="{paddingTop: (navHeight + 10) + 'px'}"
@scrolltolower="loadMore">
@scrolltolower="onScrolltolower">
<!-- 汇总卡片 -->
<view class="tl-sum">
<view class="tl-sum-glow"></view>
@ -19,6 +19,40 @@
<view class="tl-sum-tip">星球券永久有效囤越多瓜分越多</view>
</view>
<view class="tl-tabs">
<view class="tl-tab" :class="{active: activeTab === 'raiders'}" @tap="switchTab('raiders')">掠夺者列表</view>
<view class="tl-tab" :class="{active: activeTab === 'logs'}" @tap="switchTab('logs')">星球券明细</view>
</view>
<block v-if="activeTab === 'raiders'">
<view class="raider-card">
<view class="raider-head">
<view>
<view class="raider-kicker">ANGRY PIRATES</view>
<view class="raider-title">偷过我的人</view>
</view>
<view class="raider-more" @tap="loadMoreRaiders">{{raiderLoadText}}</view>
</view>
<view v-if="raiders.length" class="raider-list">
<view class="raider-row" v-for="(item, i) in raiders" :key="i">
<view class="raider-face"></view>
<view class="raider-mid">
<view class="raider-name">{{item.fromUserName || '神秘掠夺者'}}</view>
<view class="raider-desc">{{raiderDesc(item)}}</view>
</view>
<view class="raider-btn" @tap="revengeRaider(item)">反偷</view>
</view>
</view>
<view v-else class="raider-empty">{{raiderLoading ? '正在扫描掠夺者...' : '暂时没人偷过你的券'}}</view>
<view v-if="raiders.length" class="tl-foot">
<text v-if="raiderLoading">加载中</text>
<text v-else-if="raiderNoMore"> 没有更多掠夺者了 </text>
<text v-else @tap="loadMoreRaiders">点击加载更多</text>
</view>
</view>
</block>
<block v-else>
<!-- 骨架屏 -->
<view v-if="loading && list.length===0" class="tl-sk">
<view class="tl-sk-item" v-for="n in 6" :key="n"></view>
@ -53,14 +87,28 @@
<text class="tl-empty-text">还没有星球券记录</text>
<text class="tl-empty-tip">完成任务下单邀请好友都能获得星球券</text>
</view>
</block>
<view style="height:60rpx;"></view>
</scroll-view>
<hunt-modal
:show="huntModal.show"
:phase="huntModal.phase"
:result="huntModal.result"
:target="huntModal.target"
@close="closeHunt">
</hunt-modal>
</view>
</template>
<script>
import huntModal from '@/package1/components/planet/hunt-modal.vue'
export default {
components: {
huntModal
},
data() {
return {
statusBarHeight: 20,
@ -68,12 +116,33 @@
userId: '',
regionId: '',
balance: 0,
activeTab: 'raiders',
list: [],
pageNumber: 1,
pageSize: 15,
total: 0,
loading: false,
noMore: false
noMore: false,
raiders: [],
raiderPageNumber: 1,
raiderPageSize: 5,
raiderTotal: 0,
raiderLoading: false,
raiderNoMore: false,
huntModal: {
show: false,
phase: 'searching',
result: null,
target: {}
},
huntTimers: []
}
},
computed: {
raiderLoadText() {
if (this.raiderLoading) return '加载中'
if (this.raiderNoMore) return '已到底'
return this.raiders.length ? '更多' : '刷新'
}
},
onLoad() {
@ -86,6 +155,10 @@
if (area) this.regionId = JSON.parse(area).id || ''
} catch (e) {}
this.loadPage(true)
this.loadRaiders(true)
},
onUnload() {
this.clearHuntTimers()
},
methods: {
initNavHeight() {
@ -139,6 +212,106 @@
this.pageNumber++
this.loadPage(false)
},
switchTab(tab) {
this.activeTab = tab
if (tab === 'raiders' && !this.raiders.length) this.loadRaiders(true)
if (tab === 'logs' && !this.list.length) this.loadPage(true)
},
onScrolltolower() {
if (this.activeTab === 'raiders') {
this.loadMoreRaiders()
return
}
this.loadMore()
},
loadRaiders(reset) {
if (this.raiderLoading || !this.userId) return
if (reset) {
this.raiderPageNumber = 1
this.raiderNoMore = false
}
this.raiderLoading = true
this.tui.request('/app/planet/search/raiders', 'POST', {
userId: this.userId,
regionId: this.regionId,
pageNumber: this.raiderPageNumber,
pageSize: this.raiderPageSize
}, false, false, true).then((res) => {
this.raiderLoading = false
if (res.code == 200 && res.result) {
const records = res.result.records || []
this.raiderTotal = res.result.total || 0
this.raiders = reset ? records : this.raiders.concat(records)
if (this.raiders.length >= this.raiderTotal || records.length < this.raiderPageSize) {
this.raiderNoMore = true
}
}
}).catch(() => {
this.raiderLoading = false
})
},
loadMoreRaiders() {
if (this.raiderLoading) return
if (!this.raiders.length) {
this.loadRaiders(true)
return
}
if (this.raiderNoMore) return
this.raiderPageNumber++
this.loadRaiders(false)
},
raiderDesc(item) {
if (item.countered === 1) return '已被防御塔反击'
if (item.intercepted === 1) return '被防御塔拦截过'
return '偷走 ' + (item.gainTickets || 0) + ' 张券 · ' + (item.createTime || '')
},
revengeRaider(item) {
if (!item || !item.fromUserId) return
this.clearHuntTimers()
this.huntModal.show = true
this.huntModal.phase = 'searching'
this.huntModal.result = null
this.huntModal.target = {
userId: item.fromUserId,
nickname: item.fromUserName || '神秘掠夺者',
avatar: item.fromAvatar
}
this.huntTimers.push(setTimeout(() => { this.huntModal.phase = 'locking' }, 900))
this.huntTimers.push(setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800))
this.tui.request('/app/planet/search/target', 'POST', {
userId: this.userId,
regionId: this.regionId,
toUserId: item.fromUserId
}, false, false, true).then((res) => {
this.huntTimers.push(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.loadPage(true)
this.loadRaiders(true)
} else {
this.huntModal.show = false
this.tui.toast(res.message)
}
}, 2200))
}).catch(() => {
this.huntModal.show = false
})
},
clearHuntTimers() {
this.huntTimers.forEach((timer) => clearTimeout(timer))
this.huntTimers = []
},
closeHunt() {
this.huntModal.show = false
this.huntModal.result = null
},
typeIcon(type) {
const map = {
order: '食', group: '团', invite: '邀', sign: '签',
@ -149,7 +322,7 @@
typeName(type) {
const map = {
order: '外卖订单', group: '团购订单', invite: '邀请好友', sign: '每日签到',
box: '幸运宝箱', hunt: '星际追捕', buff: '购买增益', draw: '开奖瓜分'
box: '幸运宝箱', hunt: '星际掠夺', buff: '购买增益', draw: '开奖瓜分'
}
return map[type] || '星球券变动'
},
@ -281,6 +454,145 @@
color: #7E9691;
}
.tl-tabs {
position: relative;
margin-top: 24rpx;
padding: 8rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.64);
border: 2rpx solid rgba(255,255,255,0.88);
display: flex;
gap: 8rpx;
box-shadow: 0 14rpx 34rpx rgba(53,214,166,0.1);
}
.tl-tab {
flex: 1;
height: 66rpx;
line-height: 66rpx;
border-radius: 999rpx;
text-align: center;
color: #6B817D;
font-size: 25rpx;
font-weight: 900;
}
.tl-tab.active {
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.2);
}
.raider-card {
position: relative;
margin-top: 22rpx;
padding: 28rpx;
border-radius: 38rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.88), rgba(255,239,232,0.66));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 20rpx 48rpx rgba(255,122,89,0.12);
}
.raider-head {
display: flex;
align-items: center;
justify-content: space-between;
}
.raider-kicker {
color: #FF7A59;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.raider-title {
margin-top: 6rpx;
color: #12342F;
font-size: 32rpx;
font-weight: 900;
}
.raider-more {
height: 48rpx;
line-height: 48rpx;
padding: 0 18rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.74);
color: #FF7A59;
font-size: 21rpx;
font-weight: 900;
}
.raider-list { margin-top: 18rpx; }
.raider-row {
display: flex;
align-items: center;
margin-top: 14rpx;
padding: 16rpx;
border-radius: 28rpx;
background: rgba(255,255,255,0.7);
}
.raider-face {
width: 58rpx;
height: 58rpx;
line-height: 58rpx;
text-align: center;
border-radius: 20rpx;
background: linear-gradient(145deg, #FFB84D, #FF7A59);
color: #FFFFFF;
font-size: 24rpx;
font-weight: 900;
box-shadow: 0 12rpx 24rpx rgba(255,122,89,0.2);
}
.raider-mid {
flex: 1;
min-width: 0;
margin-left: 16rpx;
}
.raider-name {
color: #12342F;
font-size: 26rpx;
font-weight: 900;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.raider-desc {
margin-top: 6rpx;
color: #7E9691;
font-size: 21rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.raider-btn {
height: 52rpx;
line-height: 52rpx;
padding: 0 20rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #FFB84D, #FF7A59);
color: #FFFFFF;
font-size: 22rpx;
font-weight: 900;
}
.raider-empty {
margin-top: 18rpx;
padding: 26rpx;
border-radius: 26rpx;
background: rgba(255,255,255,0.58);
color: #7E9691;
font-size: 23rpx;
text-align: center;
}
/* 骨架 */
.tl-sk { margin-top: 28rpx; }
.tl-sk-item {

Loading…
Cancel
Save