wangfukang 2 months ago
parent
commit
af124ab3d6
  1. 33
      package1/components/planet/planet-adventure-entry.vue
  2. 13
      package1/components/planet/planet-daily-loop.vue
  3. 89
      package1/components/planet/planet-header.vue
  4. 86
      package1/components/planet/planet-operate.vue
  5. 93
      package1/components/planet/planet-tasks.vue
  6. 135
      package1/planet/adventure.vue
  7. 48
      package1/planet/drawResult.vue
  8. 186
      package1/planet/index.vue
  9. 256
      package1/planet/more.vue
  10. 123
      package1/planet/pkHall.vue
  11. 30
      package1/planet/rank.vue
  12. 2
      package1/planet/ticketLog.vue

33
package1/components/planet/planet-adventure-entry.vue

@ -1,12 +1,12 @@
<template>
<view>
<view class="pa-row">
<view class="pa" @tap="$emit('start')">
<view class="pa-badge">开始助推</view>
<view class="pa-badge">开始排位</view>
<view class="pa-left">
<view class="pa-kicker">COLLEGE ROCKET</view>
<view class="pa-title">学院助推</view>
<view class="pa-sub">玩一局有机会获得更多星球券为学院争地标</view>
<!-- <view class="pa-cta">开始助推</view> -->
<view class="pa-title">学院排位</view>
<view class="pa-sub">个人前五得现金奖励</view>
<!-- <view class="pa-cta">开始排位</view> -->
</view>
<view class="pa-ship">
<view class="pa-squirrel">🚀</view>
@ -17,8 +17,8 @@
<view class="pa-badge">开始PK</view>
<view class="pa-left">
<view class="pa-kicker">COLLEGE ROCKET</view>
<view class="pa-title">星球擂台</view>
<view class="pa-sub">看看谁才是PK王</view>
<view class="pa-title">组队PK</view>
<view class="pa-sub">不服看看谁才是PK王</view>
<!-- <view class="pa-cta">开始PK</view> -->
</view>
<view class="pa-ship">
@ -35,26 +35,27 @@
</script>
<style lang="scss" scoped>
.pa { margin-top: 22rpx; min-height: 210rpx; padding: 30rpx; border-radius: 42rpx; background: radial-gradient(circle at 92% 20%, rgba(255,184,77,0.36), transparent 32%), linear-gradient(135deg, rgba(255,255,255,0.96), rgba(232,246,255,0.78)); border: 4rpx solid rgba(255,184,77,0.58); box-shadow: 0 24rpx 56rpx rgba(255,156,66,0.18), inset 0 0 0 2rpx rgba(255,255,255,0.72); display: flex; justify-content: space-between; align-items: center; overflow: hidden; position: relative; }
.pa-row { margin-top: 22rpx; display: flex; gap: 16rpx; position: relative; z-index: 8; }
.pa { flex: 1; min-width: 0; min-height: 160rpx; padding: 24rpx 18rpx; border-radius: 34rpx; background: radial-gradient(circle at 92% 20%, rgba(255,184,77,0.36), transparent 32%), linear-gradient(135deg, rgba(255,255,255,0.96), rgba(232,246,255,0.78)); border: 3rpx solid rgba(255,184,77,0.58); box-shadow: 0 18rpx 42rpx rgba(255,156,66,0.16), inset 0 0 0 2rpx rgba(255,255,255,0.72); display: flex; justify-content: space-between; align-items: flex-end; overflow: hidden; position: relative; }
.pa:before { content: ''; position: absolute; right: -60rpx; top: -80rpx; width: 300rpx; height: 300rpx; border-radius: 50%; background: radial-gradient(circle, rgba(255,184,77,0.38), transparent 66%); }
.pa:after { content: ''; position: absolute; left: 24rpx; right: 24rpx; bottom: 12rpx; height: 10rpx; border-radius: 999rpx; background: linear-gradient(90deg, rgba(255,156,66,0.24), rgba(79,183,255,0.18)); }
.pa-pk { background: radial-gradient(circle at 90% 18%, rgba(124,58,237,0.25), transparent 34%), radial-gradient(circle at 15% 100%, rgba(56,189,248,0.20), transparent 42%), linear-gradient(135deg, rgba(255,255,255,0.98), rgba(245,239,255,0.86) 48%, rgba(232,247,255,0.76)); border-color: rgba(124,58,237,0.38); box-shadow: 0 24rpx 56rpx rgba(124,58,237,0.14), inset 0 0 0 2rpx rgba(255,255,255,0.76); }
.pa-pk:before { background: radial-gradient(circle, rgba(124,58,237,0.30), transparent 66%); }
.pa-pk:after { background: linear-gradient(90deg, rgba(124,58,237,0.22), rgba(56,189,248,0.20)); }
.pa-left { position: relative; z-index: 1; padding-right: 18rpx; }
.pa-badge { position: absolute; right: 28rpx; top: 22rpx; z-index: 2; height: 44rpx; line-height: 44rpx; padding: 0 18rpx; border-radius: 999rpx; background: linear-gradient(135deg, #FF7A59, #FFB84D); color: #FFFFFF; font-size: 20rpx; font-weight: 900; box-shadow: 0 12rpx 24rpx rgba(255,122,89,0.2); }
.pa-left { position: relative; z-index: 1; padding-right: 8rpx; }
.pa-badge { position: absolute; right: 16rpx; top: 16rpx; z-index: 2; height: 38rpx; line-height: 38rpx; padding: 0 14rpx; border-radius: 999rpx; background: linear-gradient(135deg, #FF7A59, #FFB84D); color: #FFFFFF; font-size: 18rpx; font-weight: 900; box-shadow: 0 12rpx 24rpx rgba(255,122,89,0.2); }
.pa-pk .pa-badge { background: linear-gradient(135deg, #7C3AED, #38BDF8); box-shadow: 0 12rpx 26rpx rgba(124,58,237,0.20); }
.pa-arena { position: absolute; right: 28rpx; top: 76rpx; z-index: 2; height: 48rpx; line-height: 48rpx; padding: 0 20rpx; border-radius: 999rpx; background: linear-gradient(135deg, #7C3AED, #38BDF8); color: #FFFFFF; font-size: 21rpx; font-weight: 900; box-shadow: 0 12rpx 28rpx rgba(124,58,237,0.22); }
.pa-kicker { color: #FF9C42; font-size: 20rpx; font-weight: 900; letter-spacing: 2rpx; }
.pa-kicker { color: #FF9C42; font-size: 17rpx; font-weight: 900; letter-spacing: 1rpx; }
.pa-pk .pa-kicker { color: #7C3AED; }
.pa-title { margin-top: 8rpx; color: #12342F; font-size: 44rpx; font-weight: 900; }
.pa-sub { margin-top: 10rpx; color: #6B817D; font-size: 24rpx; line-height: 1.38; max-width: 430rpx; }
.pa-title { margin-top: 8rpx; color: #12342F; font-size: 31rpx; font-weight: 900; white-space: nowrap; }
.pa-sub { margin-top: 8rpx; color: #6B817D; font-size: 20rpx; line-height: 1.35; max-width: 200rpx; }
.pa-cta { margin-top: 18rpx; display: inline-flex; align-items: center; justify-content: center; height: 54rpx; padding: 0 26rpx; border-radius: 999rpx; background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; font-size: 23rpx; font-weight: 900; box-shadow: 0 12rpx 24rpx rgba(53,214,166,0.18); }
.pa-ship { position: relative; z-index: 1; width: 156rpx; height: 118rpx; margin-top: 40rpx; border-radius: 58rpx 58rpx 42rpx 42rpx; background: linear-gradient(145deg, #FFFFFF, #DDF8FF 50%, #FFE5AE); box-shadow: inset -12rpx -16rpx 28rpx rgba(79,183,255,0.14), 0 18rpx 42rpx rgba(255,156,66,0.18); flex-shrink: 0; }
.pa-ship { position: absolute; right: 16rpx; bottom: 18rpx; z-index: 1; width: 82rpx; height: 62rpx; border-radius: 32rpx 32rpx 24rpx 24rpx; background: linear-gradient(145deg, #FFFFFF, #DDF8FF 50%, #FFE5AE); box-shadow: inset -12rpx -16rpx 28rpx rgba(79,183,255,0.14), 0 18rpx 42rpx rgba(255,156,66,0.18); flex-shrink: 0; opacity: .9; }
.pa-pk .pa-ship { background: linear-gradient(145deg, #FFFFFF, #EEE7FF 48%, #DDF8FF); box-shadow: inset -12rpx -16rpx 28rpx rgba(124,58,237,0.12), 0 18rpx 42rpx rgba(56,189,248,0.18); }
.pa-squirrel { position: absolute; left: 38rpx; top: 28rpx; width: 70rpx; height: 48rpx; line-height: 48rpx; border-radius: 999rpx; background: #8B5A2B; color: #fff; font-size: 20rpx; font-weight: 900; text-align: center; }
.pa-squirrel { position: absolute; left: 20rpx; top: 14rpx; width: 40rpx; height: 32rpx; line-height: 32rpx; border-radius: 999rpx; background: #8B5A2B; color: #fff; font-size: 18rpx; font-weight: 900; text-align: center; }
.pa-pk .pa-squirrel { background: #5B21B6; }
.pa-flame { position: absolute; right: -18rpx; top: 38rpx; width: 40rpx; height: 26rpx; border-radius: 50%; background: linear-gradient(90deg, #FFB84D, rgba(255,184,77,0)); }
.pa-flame { position: absolute; right: -12rpx; top: 22rpx; width: 28rpx; height: 18rpx; border-radius: 50%; background: linear-gradient(90deg, #FFB84D, rgba(255,184,77,0)); }
.pa-pk .pa-flame { background: linear-gradient(90deg, #38BDF8, rgba(56,189,248,0)); }
@keyframes fly { 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(-10rpx); } }
@keyframes flame { 0%,100%{ transform: scaleX(.8); opacity: .7; } 50%{ transform: scaleX(1.2); opacity: 1; } }

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

@ -1,7 +1,7 @@
<template>
<view class="dl">
<view class="dl-top">
<view>
<view class="dl-copy">
<view class="dl-kicker">2 MIN DAILY</view>
<view class="dl-title">{{progressTitle}}</view>
<view class="dl-sub">完成任务可获得星球券与开奖权重</view>
@ -53,7 +53,7 @@
</view>
<view class="stat-card probability">
<text>{{data.myProbability || 0}}%</text>
<text>中奖概率</text>
<text>当前中奖概率</text>
</view>
</view>
@ -141,14 +141,15 @@
</script>
<style lang="scss" scoped>
.dl { margin-top: -20rpx; padding: 28rpx; border-radius: 44rpx; background: linear-gradient(155deg, rgba(255,255,255,0.92), rgba(239,255,249,0.78)); border: 2rpx solid rgba(255,255,255,0.92); box-shadow: 0 26rpx 60rpx rgba(53,214,166,0.14); overflow: visible; position: relative; }
.dl { margin-top: 18rpx; padding: 28rpx; border-radius: 44rpx; background: linear-gradient(155deg, rgba(255,255,255,0.92), rgba(239,255,249,0.78)); border: 2rpx solid rgba(255,255,255,0.92); box-shadow: 0 26rpx 60rpx rgba(53,214,166,0.14); overflow: visible; position: relative; }
.dl:before { content: ''; position: absolute; right: -90rpx; top: -120rpx; width: 320rpx; height: 320rpx; border-radius: 50%; background: radial-gradient(circle, rgba(79,183,255,0.26), transparent 68%); }
.dl-top { position: relative; z-index: 1; display: flex; align-items: flex-start; justify-content: space-between; }
.dl-top { position: relative; z-index: 1; display: flex; align-items: flex-start; justify-content: space-between; gap: 16rpx; }
.dl-copy { min-width: 0; flex: 1; }
.dl-kicker { color: #59CBB5; font-size: 20rpx; font-weight: 900; letter-spacing: 2rpx; }
.dl-title { margin-top: 6rpx; color: #12342F; font-size: 42rpx; font-weight: 900; }
.dl-sub { margin-top: 8rpx; color: #6B817D; font-size: 24rpx; }
.dl-map { width: 146rpx; min-height: 82rpx; padding: 12rpx 10rpx; border-radius: 28rpx; background: linear-gradient(145deg, #FFFFFF, #E5FFF1); color: #22B889; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 14rpx 28rpx rgba(53,214,166,0.16); border: 2rpx solid rgba(255,255,255,0.96); flex-shrink: 0; }
.dl-map text:first-child { font-size: 21rpx; font-weight: 900; line-height: 1.18; text-align: center; }
.dl-map { width: auto; min-width: 164rpx; min-height: 82rpx; padding: 12rpx 18rpx; border-radius: 28rpx; background: linear-gradient(145deg, #FFFFFF, #E5FFF1); color: #22B889; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 14rpx 28rpx rgba(53,214,166,0.16); border: 2rpx solid rgba(255,255,255,0.96); flex-shrink: 0; }
.dl-map text:first-child { font-size: 21rpx; font-weight: 900; line-height: 1.18; text-align: center; white-space: nowrap; }
.dl-map text:last-child { margin-top: 8rpx; padding: 4rpx 14rpx; border-radius: 999rpx; background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; font-size: 18rpx; font-weight: 900; }
.progress-overview { position: relative; z-index: 1; margin-top: 20rpx; padding: 18rpx 20rpx; border-radius: 30rpx; background: rgba(255,255,255,0.54); border: 1px solid rgba(255,255,255,.7); box-shadow: inset 0 1rpx 0 rgba(255,255,255,0.86); }
.progress-row { display: flex; justify-content: space-between; color: #42635E; font-size: 22rpx; font-weight: 900; }

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

@ -69,9 +69,9 @@
<view class="ph-squirrel-tag">我的星球券 {{data.myTicketCount || 0}} </view>
</view>
<view class="ph-satellite ph-satellite-left">
<text class="ph-satellite-num">{{data.joinCount || 0}}</text>
<text class="ph-satellite-label">竟奖人数</text>
<view class="ph-satellite ph-satellite-left" @tap="goPoolRule">
<text class="ph-satellite-num">规则</text>
<text class="ph-satellite-label">奖池规则</text>
</view>
<view class="ph-satellite ph-satellite-right" @tap="goRank">
<text class="ph-satellite-num">{{rankText}}</text>
@ -150,6 +150,9 @@
goGuide() {
this.$emit('guide')
},
goPoolRule() {
this.$emit('poolrule')
},
goRank() {
this.$emit('rank')
},
@ -216,7 +219,7 @@
.ph {
position: relative;
margin-top: 6rpx;
height: 820rpx;
height: 660rpx;
overflow: visible;
}
@ -320,10 +323,10 @@
.ph-station {
position: absolute;
right: 26rpx;
top: 150rpx;
width: 170rpx;
height: 78rpx;
right: 30rpx;
top: 132rpx;
width: 132rpx;
height: 60rpx;
z-index: 4;
}
@ -334,27 +337,39 @@
.ph-station-core {
position: absolute;
left: 58rpx;
top: 20rpx;
width: 56rpx;
height: 38rpx;
border-radius: 18rpx;
left: 44rpx;
top: 16rpx;
width: 44rpx;
height: 30rpx;
border-radius: 16rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.96), rgba(224,247,255,0.88));
border: 2rpx solid rgba(79,183,255,0.28);
box-shadow: 0 14rpx 28rpx rgba(79,183,255,0.18);
}
.ph-station-core:after {
content: '';
position: absolute;
left: 16rpx;
top: 8rpx;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: rgba(143,124,255,0.7);
box-shadow: 0 0 12rpx rgba(79,183,255,0.45);
}
.ph-station-wing {
position: absolute;
top: 30rpx;
width: 58rpx;
height: 18rpx;
top: 24rpx;
width: 42rpx;
height: 14rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, rgba(143,124,255,0.76), rgba(79,183,255,0.72));
}
.ph-station-wing-left { left: 0; }
.ph-station-wing-right { right: 0; }
.ph-station-wing-left { left: 4rpx; }
.ph-station-wing-right { right: 4rpx; }
.ph-asteroid {
position: absolute;
@ -387,9 +402,9 @@
.ph-floating-box {
position: absolute;
left: 34rpx;
top: 625rpx;
width: 128rpx;
height: 86rpx;
top: 500rpx;
width: 116rpx;
height: 76rpx;
border-radius: 22rpx;
background: linear-gradient(145deg, #FF8B6A, #FFCF7A);
box-shadow: inset -8rpx -10rpx 18rpx rgba(146,75,26,0.18), 0 18rpx 36rpx rgba(255,122,89,0.22);
@ -451,11 +466,11 @@
.ph-planet-wrap {
position: absolute;
top: 142rpx;
top: 112rpx;
left: 50%;
transform: translateX(-50%);
width: 610rpx;
height: 610rpx;
width: 500rpx;
height: 500rpx;
z-index: 3;
}
@ -463,8 +478,8 @@
position: absolute;
left: 50%;
top: 50%;
width: 680rpx;
height: 680rpx;
width: 560rpx;
height: 560rpx;
border-radius: 50%;
transform: translate(-50%, -50%);
background: radial-gradient(circle, rgba(53,214,166,0.26), rgba(79,183,255,0.12) 42%, rgba(255,255,255,0) 68%);
@ -475,8 +490,8 @@
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 570rpx;
height: 570rpx;
width: 468rpx;
height: 468rpx;
border-radius: 50%;
background:
radial-gradient(circle at 28% 22%, rgba(255,255,255,0.96) 0%, rgba(255,255,255,0.72) 15%, transparent 28%),
@ -566,8 +581,8 @@
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 390rpx;
min-height: 270rpx;
width: 340rpx;
min-height: 232rpx;
border-radius: 56rpx;
background: rgba(255,255,255,0.34);
border: 1rpx solid rgba(255,255,255,0.72);
@ -581,7 +596,7 @@
.ph-core-label {
color: #42635E;
font-size: 24rpx;
font-size: 22rpx;
font-weight: 800;
letter-spacing: 2rpx;
margin-top: 14rpx;
@ -661,7 +676,7 @@
.ph-pool-symbol {
position: relative;
z-index: 1;
font-size: 40rpx;
font-size: 34rpx;
font-weight: 900;
color: #FFB000;
vertical-align: 8rpx;
@ -670,7 +685,7 @@
.ph-pool-num {
position: relative;
z-index: 1;
font-size: 102rpx;
font-size: 78rpx;
font-weight: 900;
font-family: DIN, sans-serif;
letter-spacing: -2rpx;
@ -684,7 +699,7 @@
position: relative;
text-align: center;
color: #42635E;
font-size: 24rpx;
font-size: 22rpx;
}
.ph-ticket-pill {
@ -764,13 +779,13 @@
.ph-satellite-left {
left: 0;
top: 300rpx;
top: 252rpx;
transform: rotate(-6deg);
}
.ph-satellite-right {
right: 0;
top: 258rpx;
top: 220rpx;
transform: rotate(7deg);
animation-delay: -1.5s;
}
@ -800,7 +815,7 @@
.ph-squirrel {
position: absolute;
right: 18rpx;
top: 585rpx;
top: 470rpx;
width: 142rpx;
height: 164rpx;
z-index: 7;

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

@ -14,36 +14,58 @@
<view class="po-grid">
<view class="po-card tree">
<view class="po-focus-tag">推荐先种</view>
<view class="po-tree">
<view class="po-tree-crown"></view>
<view class="po-tree-body"></view>
</view>
<view class="po-card-title">券树培育</view>
<view class="po-card-desc">选择周期投入星球券成熟后返还本金和收益</view>
<view class="po-card-desc highlight">投入星球券开始培育成熟后返还本金和收益</view>
<view class="po-tool-tip">
<text>🌱</text>
<text>周期越长越适合闲置券成熟后记得回来收获</text>
</view>
<scroll-view scroll-x class="po-cycles">
<view class="po-cycle" v-for="(c,i) in treeConfigs" :key="i" @tap="$emit('plant', c)">
<text>{{c.cycleHours}}h</text>
<text>收益 {{rateText(c.rate)}}</text>
<text>{{investRange(c)}}</text>
</view>
</scroll-view>
<view class="po-orders inline" v-if="treeOrders.length">
<view class="po-land-title">成长中的券树</view>
<view class="po-order" v-for="(o,i) in treeOrders" :key="i">
<view>
<view class="po-order-title">{{o.cycleHours}}h · 投入{{o.investTickets}}</view>
<view class="po-order-desc">预计收获 {{o.expectedTotal}} </view>
</view>
<view class="po-order-btn" @tap="$emit('harvest', o)">{{o.status === 2 ? '已收' : '收获'}}</view>
</view>
</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.towerCounterRate || 0}}% · 伤害 {{state.towerDamage || 0}}</view>
<view class="po-main-btn" @tap="$emit('upgradeTower')">升级防御</view>
</view>
</view>
<view class="po-orders" v-if="treeOrders.length">
<view class="po-land-title">成长中的券树</view>
<view class="po-order" v-for="(o,i) in treeOrders" :key="i">
<view>
<view class="po-order-title">{{o.cycleHours}}h · 投入{{o.investTickets}}</view>
<view class="po-order-desc">预计收获 {{o.expectedTotal}} </view>
<view class="tower-copy">
<view class="po-icon">TOWER</view>
<view class="po-card-title">防御塔 Lv{{state.towerLevel || 1}}</view>
<view class="po-card-desc highlight">拦截 {{state.towerInterceptRate || 0}}% · 反击 {{state.towerCounterRate || 0}}% · 伤害 {{state.towerDamage || 0}}</view>
<view class="tower-next" v-if="nextTower">
<text>下一级 Lv{{nextTower.level}}</text>
<text>拦截 {{nextTower.interceptRate || 0}}% · 反击 {{nextTower.counterRate || 0}}% · 伤害 {{nextTower.damage || 0}}</text>
<text>升级消耗 {{nextTower.upgradeCost || 0}} 张星球券</text>
</view>
<view class="tower-next max" v-else>
<text>已达到当前最高等级</text>
</view>
<view class="po-tool-tip tower-tip">
<text>🛡</text>
<text>被偷券时更容易挡住还能反击消耗对方体力</text>
</view>
</view>
<view class="tower-action">
<view class="tower-visual">🛡</view>
<view class="po-main-btn" @tap="$emit('upgradeTower')">升级防御</view>
</view>
<view class="po-order-btn" @tap="$emit('harvest', o)">{{o.status === 2 ? '已收' : '收获'}}</view>
</view>
</view>
</view>
@ -63,19 +85,27 @@
},
treeOrders() {
return (this.data && this.data.treeOrders) || []
},
nextTower() {
return (this.data && this.data.nextTowerConfig) || null
}
},
methods: {
rateText(rate) {
return Math.round((Number(rate || 0)) * 100) + '%'
},
investRange(config) {
const min = config.minInvest || 1
const max = config.maxInvest || 0
return max > 0 ? `${min}-${max}` : `最低${min}`
}
}
}
</script>
<style lang="scss" scoped>
.po { margin-top: 24rpx; }
.po-head { display: flex; justify-content: space-between; align-items: flex-start; padding: 0 10rpx 14rpx; }
.po { margin-top: 20rpx; }
.po-head { display: none; }
.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; }
@ -85,21 +115,37 @@
.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; }
.po-card.tower { grid-column: span 2; min-height: 220rpx; display: flex; align-items: center; justify-content: space-between; gap: 20rpx; background: linear-gradient(135deg, rgba(255,255,255,0.92), rgba(232,246,255,0.78)); }
.po-focus-tag { position: absolute; right: 22rpx; top: 22rpx; z-index: 2; padding: 6rpx 16rpx; border-radius: 999rpx; background: linear-gradient(135deg, #FFB84D, #35D6A6); color: #FFFFFF; font-size: 20rpx; font-weight: 900; box-shadow: 0 10rpx 20rpx rgba(53,214,166,0.16); }
.po-card-title { color: #12342F; font-size: 30rpx; font-weight: 900; }
.po-card-desc { margin-top: 10rpx; color: #6B817D; font-size: 23rpx; line-height: 1.5; }
.po-card-desc.highlight { color: #315B50; font-weight: 900; }
.po-tool-tip { position: relative; z-index: 1; display: flex; gap: 12rpx; margin-top: 16rpx; padding: 16rpx 18rpx; border-radius: 24rpx; background: rgba(255,184,77,0.13); border: 2rpx solid rgba(255,184,77,0.22); color: #7A5420; font-size: 22rpx; line-height: 1.45; font-weight: 800; max-width: 430rpx; }
.po-tool-tip text:first-child { flex-shrink: 0; }
.po-tool-tip.tower-tip { background: rgba(79,183,255,0.13); border-color: rgba(79,183,255,0.22); color: #315A78; }
.po-tree { position: absolute; right: 54rpx; top: 34rpx; width: 140rpx; height: 150rpx; animation: treeBreath 2.6s ease-in-out infinite; }
.po-tree-crown { width: 130rpx; height: 110rpx; border-radius: 70rpx 70rpx 50rpx 50rpx; background: radial-gradient(circle at 35% 28%, #FFFFFF, #A8F7C1 42%, #35D6A6); box-shadow: 0 18rpx 38rpx rgba(53,214,166,0.24); }
.po-tree-body { margin: -12rpx auto 0; width: 28rpx; height: 66rpx; border-radius: 20rpx; background: linear-gradient(180deg, #F5C68A, #D8894F); }
@keyframes treeBreath { 0%,100%{ transform: translateY(0) scale(1); } 50%{ transform: translateY(-10rpx) scale(1.04); } }
.po-cycles { margin-top: 34rpx; white-space: nowrap; width: 430rpx; }
.po-cycle { display: inline-flex; flex-direction: column; justify-content: center; width: 132rpx; height: 86rpx; margin-right: 14rpx; padding-left: 20rpx; border-radius: 28rpx; background: rgba(255,255,255,0.82); color: #22B889; font-size: 22rpx; font-weight: 800; box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.1); }
.po-cycles { margin-top: 22rpx; white-space: nowrap; width: 420rpx; max-width: 420rpx; box-sizing: border-box; }
.po-cycle { display: inline-flex; flex-direction: column; justify-content: center; width: 122rpx; height: 98rpx; margin-right: 10rpx; padding-left: 16rpx; border-radius: 26rpx; background: linear-gradient(145deg, #FFFFFF, #E9FFF4); color: #22B889; font-size: 21rpx; font-weight: 900; box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.14); border: 2rpx solid rgba(53,214,166,0.18); }
.po-cycle text:nth-child(2) { margin-top: 4rpx; }
.po-cycle text:nth-child(3) { margin-top: 4rpx; color: #7E9691; font-size: 18rpx; font-weight: 800; }
.po-icon { display: inline-flex; padding: 10rpx 16rpx; margin-bottom: 18rpx; border-radius: 999rpx; background: rgba(255,255,255,0.8); color: #22B889; font-size: 20rpx; font-weight: 900; }
.po-progress { margin-top: 18rpx; height: 14rpx; border-radius: 999rpx; background: rgba(53,214,166,0.12); overflow: hidden; }
.po-progress-in { height: 100%; border-radius: 999rpx; background: linear-gradient(90deg, #35D6A6, #4FB7FF); }
.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-main-btn { margin-top: 12rpx; background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; box-shadow: 0 12rpx 24rpx rgba(53,214,166,0.18); }
.tower-copy { flex: 1; min-width: 0; }
.tower-action { width: 150rpx; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; }
.tower-visual { width: 104rpx; height: 104rpx; line-height: 104rpx; text-align: center; border-radius: 36rpx; background: rgba(255,255,255,0.72); font-size: 48rpx; box-shadow: 0 14rpx 30rpx rgba(79,183,255,0.14); }
.tower-next { margin-top: 14rpx; padding: 16rpx 18rpx; border-radius: 24rpx; background: rgba(255,255,255,0.68); color: #315A78; display: flex; flex-direction: column; gap: 5rpx; font-size: 21rpx; font-weight: 800; }
.tower-next text:first-child { color: #12342F; font-size: 23rpx; font-weight: 900; }
.tower-next text:last-child { color: #22B889; }
.tower-next.max text:last-child { color: #315A78; }
.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.inline { margin-top: 22rpx; padding: 20rpx; border-radius: 30rpx; background: rgba(255,255,255,0.58); box-shadow: none; }
.po-land-title { color: #12342F; font-size: 30rpx; font-weight: 900; }
.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; }

93
package1/components/planet/planet-tasks.vue

@ -19,7 +19,7 @@
:class="{auto: t.actionType==='auto', done: t.actionType==='claim' && !t.canClaim}"
@tap="onClick(t)">
<text>+{{t.rewardTickets}}</text>
<text>{{btnText(t)}}</text>
<text>{{btnText(t)}} <text v-if="t.actionType==='auto'" class="btn-arrow"></text></text>
</view>
</view>
</view>
@ -83,14 +83,11 @@
<style lang="scss" scoped>
.pt {
position: relative;
margin-top: 34rpx;
margin-top: 20rpx;
}
.pt-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 22rpx;
display: none;
}
.pt-kicker {
@ -118,44 +115,37 @@
.pt-list {
position: relative;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
flex-direction: column;
gap: 16rpx;
}
.pt-list:before {
content: '';
position: absolute;
left: 42rpx;
right: 42rpx;
top: 102rpx;
height: 180rpx;
border-top: 3rpx dashed rgba(53,214,166,0.28);
border-radius: 50%;
transform: rotate(-7deg);
background: transparent;
display: none;
}
.pt-item {
position: relative;
width: 318rpx;
min-height: 224rpx;
width: 100%;
min-height: 148rpx;
box-sizing: border-box;
padding: 26rpx 22rpx 24rpx;
margin-bottom: 24rpx;
border-radius: 36rpx;
padding: 22rpx;
border-radius: 30rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.82), rgba(244,255,249,0.64));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
overflow: hidden;
display: flex;
align-items: center;
gap: 18rpx;
}
.pt-item.offset {
margin-top: 54rpx;
transform: rotate(1.4deg);
margin-top: 0;
transform: none;
}
.pt-item:not(.offset) {
transform: rotate(-1deg);
transform: none;
}
.pt-item:before {
@ -176,8 +166,8 @@
.pt-route-dot {
position: absolute;
left: 28rpx;
top: -8rpx;
left: 18rpx;
top: 18rpx;
width: 20rpx;
height: 20rpx;
border-radius: 50%;
@ -188,9 +178,9 @@
.pt-item-icon {
position: relative;
width: 72rpx;
height: 72rpx;
border-radius: 24rpx;
width: 76rpx;
height: 76rpx;
border-radius: 26rpx;
background: linear-gradient(145deg, #E5FFF1, #EAF8FF);
display: flex;
align-items: center;
@ -199,11 +189,14 @@
font-size: 30rpx;
font-weight: 900;
box-shadow: inset 0 0 0 1rpx rgba(255,255,255,0.86);
flex-shrink: 0;
}
.pt-item-mid {
position: relative;
margin-top: 18rpx;
margin-top: 0;
flex: 1;
min-width: 0;
}
.pt-item-name {
@ -217,29 +210,34 @@
font-size: 22rpx;
margin-top: 8rpx;
line-height: 32rpx;
min-height: 64rpx;
min-height: 0;
}
.pt-item-btn {
position: relative;
margin-top: 18rpx;
height: 58rpx;
padding: 0 18rpx;
border-radius: 999rpx;
margin-top: 0;
width: 142rpx;
height: 82rpx;
padding: 0;
border-radius: 28rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 23rpx;
justify-content: center;
flex-direction: column;
font-size: 22rpx;
font-weight: 800;
box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.22);
flex-shrink: 0;
border: 2rpx solid rgba(255,255,255,0.82);
transition: transform .16s ease, opacity .16s ease;
}
.pt-item-btn.auto {
background: rgba(18,52,47,0.08);
color: #22B889;
box-shadow: none;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.24);
}
.pt-item-btn.done {
@ -247,4 +245,15 @@
color: #9AA9A5;
box-shadow: none;
}
.pt-item-btn:active {
transform: scale(.96);
opacity: .88;
}
.btn-arrow {
margin-left: 4rpx;
font-size: 26rpx;
font-weight: 900;
}
</style>

135
package1/planet/adventure.vue

@ -2,7 +2,7 @@
<view class="boost">
<view class="nav" :style="{height: navHeight + 'px', paddingTop: statusBarHeight + 'px'}">
<view class="nav-back" @tap="goBack"><text></text></view>
<view class="nav-title">{{isDailyMode ? '学院助推赛' : 'PK局'}}</view>
<view class="nav-title">{{isDailyMode ? '学院排位赛' : 'PK局'}}</view>
</view>
<scroll-view scroll-y class="page" :style="{paddingTop: (navHeight + 10) + 'px'}">
@ -19,7 +19,12 @@
<view class="rocket-progress" :style="{width: rocketPercent + '%'}"></view>
</view>
</view>
<view class="hero-sub">{{heroSub}}</view>
<view class="hero-reward-tip">
<view class="hero-reward-icon">🏆</view>
<view class="hero-reward-copy">
<text>{{heroSub}}</text>
</view>
</view>
</view>
<view class="glory-card" v-if="isDailyMode">
@ -29,7 +34,7 @@
</view>
<view class="glory-list">
<view class="glory-item" v-for="(item,i) in gloryList" :key="item._key" :class="'rank-' + i">
<view class="glory-medal">{{['🥇','🥈','🥉'][i] || (i + 1)}}</view>
<view class="glory-medal">{{gloryMedals[i] || (i + 1)}}</view>
<view class="glory-user">
<text>{{item.college || '神秘学院'}}</text>
<text>{{item.nickname || '匿名同学'}}</text>
@ -69,7 +74,7 @@
<text>先点这里再开始消除卡牌</text>
</view>
<view class="start-strip done" v-if="cleared && isDailyMode">
<text>今日已助推</text>
<text>今日已排位</text>
<text>明天再来为学院继续推进</text>
</view>
@ -175,6 +180,7 @@
avatar: '',
college: '',
colleges: [],
gloryMedals: ['🥇','🥈','🥉','4','5'],
collegeModal: false,
home: {},
session: null,
@ -223,7 +229,7 @@
heroSub() {
if (this.isPkMode) return '同一房间使用完全一致的卡牌布局。每人只有一次机会,退出或失败都会记录成绩。'
if (this.isChallengeMode) return '复刻好友通关布局,挑战同一组卡牌,结束后会显示领先或落后秒数。'
return '每天1关,通关贡献学院推进值。21:30结算,前三学院成员分别获得3/2/1张星球券。'
return '21:30结算学院榜券奖励,前三名学院参与者每人得3/2/1张星球券;个人最快通关前五名可获得现金奖励5/4/3/2/1元。'
},
gameTitle() {
if (this.isPkMode) return '擂台关卡'
@ -231,11 +237,11 @@
return '今日关卡'
},
startButtonText() {
if (this.playing) return '助推中'
if (this.cleared) return this.isDailyMode ? '今日已助推' : '成绩已提交'
if (this.playing) return '排位中'
if (this.cleared) return this.isDailyMode ? '今日已排位' : '成绩已提交'
if (this.isPkMode) return '开始擂台'
if (this.isChallengeMode) return '开始挑战'
return '开始助推'
return '开始排位'
},
collegeRanks() {
const list = this.home.rankList || []
@ -305,7 +311,7 @@
if (!this.playing) return '开始后进入倒计时'
if (this.timeLeft <= 30) return '最后冲刺,槽位别满'
if (this.timeLeft <= 60) return '时间紧张,加快决策'
return '300秒内完成助推'
return '300秒内完成排位'
},
riskText() {
if (this.slots.length >= this.slotLimit - 1) return '危险:差1格就满'
@ -370,7 +376,7 @@
}
const collegeParam = this.college ? ('?college=' + encodeURIComponent(this.college)) : ''
return {
title: this.college ? `来为${this.college}助力,冲学院助推榜!` : '学院助推赛,来帮学院冲榜',
title: this.college ? `来为${this.college}助力,冲学院排位榜!` : '学院排位赛,来帮学院冲榜',
path: '/package1/planet/adventure' + collegeParam
}
},
@ -441,7 +447,7 @@
regionId: this.regionId
}, false, false, true).then(res => {
if (res.code == 200) {
this.gloryList = (res.result || []).map((item, index) => {
this.gloryList = (res.result || []).slice(0, 5).map((item, index) => {
item._key = item.id || ('glory_' + index)
return item
})
@ -450,24 +456,15 @@
},
prepareSharedMode() {
if (this.isPkMode && this.roomId && !this.fixedSeed) {
this.tui.request('/app/planet/adventure/pk/join', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college,
roomId: this.roomId
}).then(res => {
if (res.code != 200 || !res.result) {
this.tui.toast(res.message)
return
}
this.fixedSeed = res.result.seed
this.pkInfo = res.result.room
if (!this.regionId && this.pkInfo) this.regionId = this.pkInfo.regionId || ''
if (!this.college && res.result.player) this.college = res.result.player.college || ''
this.roundSeed = this.fixedSeed
this.buildLevel()
this.checkPkRoomAvailable(() => {
this.joinPkRoom(res => {
this.fixedSeed = res.result.seed
this.pkInfo = res.result.room
if (!this.regionId && this.pkInfo) this.regionId = this.pkInfo.regionId || ''
if (!this.college && res.result.player) this.college = res.result.player.college || ''
this.roundSeed = this.fixedSeed
this.buildLevel()
})
})
}
if (this.isChallengeMode && this.challengeId && !this.fixedSeed) {
@ -489,6 +486,48 @@
})
}
},
checkPkRoomAvailable(next) {
this.tui.request('/app/planet/adventure/pk/roomInfo', 'POST', {
userId: this.userId,
regionId: this.regionId,
roomId: this.roomId
}, false, false, true).then(res => {
if (res.code != 200 || !res.result || !res.result.room) {
this.tui.toast(res.message)
return
}
const room = res.result.room
const joined = !!res.result.player
this.pkInfo = room
if (!this.regionId && room) this.regionId = room.regionId || ''
if (!this.college && res.result.player) this.college = res.result.player.college || ''
if (room.status === 2 || room.status === 3) {
this.tui.toast('房间已结束')
return
}
if (!joined && (room.playerCount || 0) >= (room.maxPlayers || 2)) {
this.tui.toast('房间人数已满,无法进入')
return
}
if (typeof next === 'function') next()
})
},
joinPkRoom(done) {
this.tui.request('/app/planet/adventure/pk/join', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college,
roomId: this.roomId
}).then(res => {
if (res.code != 200 || !res.result) {
this.tui.toast(res.message)
return
}
if (typeof done === 'function') done(res)
})
},
schedulePrebuildLevel() {
if (!this.isDailyMode && !this.fixedSeed) return
this.clearPrebuildTimer()
@ -539,24 +578,15 @@
},
startFixedMode() {
if (!this.fixedSeed && this.isPkMode) {
this.tui.request('/app/planet/adventure/pk/join', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college,
roomId: this.roomId
}).then(res => {
if (res.code != 200 || !res.result) {
this.tui.toast(res.message)
return
}
this.fixedSeed = res.result.seed
this.pkInfo = res.result.room
this.roomId = res.result.roomId || this.roomId
if (!this.regionId && this.pkInfo) this.regionId = this.pkInfo.regionId || ''
if (!this.college && res.result.player) this.college = res.result.player.college || ''
this.startFixedMode()
this.checkPkRoomAvailable(() => {
this.joinPkRoom(res => {
this.fixedSeed = res.result.seed
this.pkInfo = res.result.room
this.roomId = res.result.roomId || this.roomId
if (!this.regionId && this.pkInfo) this.regionId = this.pkInfo.regionId || ''
if (!this.college && res.result.player) this.college = res.result.player.college || ''
this.startFixedMode()
})
})
return
}
@ -1125,11 +1155,11 @@
const card = this.cards.find(item => item.id === id)
if (!card) return
if (this.cleared) {
this.tui.toast('今日已助推,明天再来')
this.tui.toast('今日已排位,明天再来')
return
}
if (!this.playing) {
this.tui.toast('先点击开始助推')
this.tui.toast('先点击开始排位')
return
}
this.refreshCardState()
@ -1231,7 +1261,7 @@
this.cleared = true
this.modal = {
show: true,
title: '助推成功',
title: '排位成功',
sub: `${this.collegeName} 获得 ${progress} 点推进值。分享给好友继续为学院助力,每个人都会随机生成自己的关卡。`,
invite: true,
duration
@ -1419,6 +1449,11 @@
.hero-kicker { color: #FF9C42; font-size: 22rpx; font-weight: 900; letter-spacing: 2rpx; }
.hero-title { margin-top: 10rpx; font-size: 42rpx; font-weight: 900; }
.hero-sub, .tips { margin-top: 16rpx; color: #6B817D; font-size: 24rpx; line-height: 1.55; }
.hero-reward-tip { position: relative; margin-top: 20rpx; padding: 20rpx 22rpx 20rpx 86rpx; border-radius: 24rpx; background: linear-gradient(135deg, rgba(255,247,221,0.96), rgba(255,255,255,0.9) 48%, rgba(229,255,246,0.92)); border: 3rpx solid rgba(255,184,77,0.58); box-shadow: 0 16rpx 34rpx rgba(255,184,77,0.18), inset 0 0 0 2rpx rgba(255,255,255,0.72); overflow: hidden; }
.hero-reward-tip:before { content: ''; position: absolute; right: -48rpx; top: -62rpx; width: 180rpx; height: 180rpx; border-radius: 50%; background: radial-gradient(circle, rgba(255,184,77,0.26), transparent 66%); }
.hero-reward-tip:after { content: '现金奖励'; position: absolute; right: 18rpx; top: 14rpx; padding: 4rpx 14rpx; border-radius: 999rpx; background: linear-gradient(135deg, #FFB84D, #35D6A6); color: #FFFFFF; font-size: 19rpx; font-weight: 900; }
.hero-reward-icon { position: absolute; left: 20rpx; top: 50%; transform: translateY(-50%); width: 48rpx; height: 48rpx; line-height: 48rpx; border-radius: 50%; text-align: center; background: rgba(255,255,255,0.82); box-shadow: 0 8rpx 18rpx rgba(255,184,77,0.18); font-size: 28rpx; }
.hero-reward-copy { position: relative; z-index: 1; padding-right: 92rpx; color: #6A4A1D; font-size: 24rpx; line-height: 1.55; font-weight: 800; }
.tips { display: flex; flex-direction: column; gap: 6rpx; }
.countdown-card { margin-top: 16rpx; padding: 18rpx 20rpx; border-radius: 28rpx; background: linear-gradient(135deg, rgba(53,214,166,0.14), rgba(79,183,255,0.16)); border: 2rpx solid rgba(53,214,166,0.22); }
.countdown-card.warning { background: linear-gradient(135deg, rgba(255,184,77,0.18), rgba(255,244,215,0.9)); border-color: rgba(255,184,77,0.42); }

48
package1/planet/drawResult.vue

@ -26,12 +26,18 @@
</view>
<view v-else class="dr-empty">本商圈暂未开奖敬请期待 🌌</view>
<view class="dr-winners" v-if="winners && winners.length">
<view class="dr-sec-title">中奖名单</view>
<view class="dr-winner" v-for="(w, i) in winners" :key="i">
<text class="dr-w-level" :class="'lv'+w.rewardLevel">{{w.levelName}}</text>
<text class="dr-w-name">{{w.userName || '神秘居民'}}</text>
<text class="dr-w-amount">{{Number(w.amount||0).toFixed(2)}}</text>
<view class="dr-winners" v-if="history && history.length">
<view class="dr-sec-title">最近10期中奖记录</view>
<view class="dr-history" v-for="(h, hi) in history" :key="hi">
<view class="dr-history-head">
<text> {{h.record && h.record.periodNo}} </text>
<text>{{drawDate(h.record && h.record.drawTime)}}</text>
</view>
<view class="dr-winner" v-for="(w, i) in h.winners" :key="i">
<text class="dr-w-level" :class="'lv'+w.rewardLevel">{{w.levelName}}</text>
<text class="dr-w-name">{{maskName(w.userName)}}</text>
<text class="dr-w-amount">{{Number(w.amount||0).toFixed(2)}}</text>
</view>
</view>
</view>
</block>
@ -67,6 +73,7 @@
regionId: '',
record: null,
winners: [],
history: [],
myWinning: []
}
},
@ -105,6 +112,7 @@
if (res.code == 200 && res.result) {
this.record = res.result.record
this.winners = res.result.winners || []
this.history = res.result.history || []
}
})
},
@ -127,6 +135,17 @@
if (res.code == 200) this.loadMyWinning()
})
},
maskName(name) {
if (!name) return '神秘居民'
const text = String(name)
if (text.length <= 1) return text + '*'
if (text.length === 2) return text.charAt(0) + '*'
return text.charAt(0) + '*' + text.charAt(text.length - 1)
},
drawDate(time) {
if (!time) return ''
return String(time).substring(0, 10)
},
goBack() {
const pages = getCurrentPages ? getCurrentPages() : []
if (pages.length > 1) {
@ -295,6 +314,23 @@
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.08);
}
.dr-history {
margin-bottom: 22rpx;
padding: 20rpx;
border-radius: 30rpx;
background: rgba(255,255,255,0.58);
border: 1rpx solid rgba(255,255,255,0.86);
}
.dr-history-head {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
color: #42635E;
font-size: 24rpx;
font-weight: 900;
}
.dr-w-level {
font-size: 22rpx;
padding: 2rpx 14rpx;

186
package1/planet/index.vue

@ -37,6 +37,7 @@
@ticketlog="goTicketLog"
@draw="goDrawResult"
@guide="showGuideModal"
@poolrule="showPoolRuleModal"
@rank="goRank">
</planet-header>
@ -102,6 +103,44 @@
</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">BANJINLI GUIDE</view>
@ -109,7 +148,7 @@
<view class="guide-list">
<view class="guide-item">
<text>1</text>
<view>每天签到开补给箱玩学院助推赛都能获得星球券</view>
<view>每天签到开补给箱玩学院排位赛都能获得星球券</view>
</view>
<view class="guide-item">
<text>2</text>
@ -121,7 +160,7 @@
</view>
<view class="guide-item">
<text>4</text>
<view>补给地图里有券树仓库防御塔地标和任务适合长期经营</view>
<view>补给地图保留农场券树防御塔和校园下单给券任务适合每天顺手经营</view>
</view>
</view>
<view class="guide-btn" @tap="guideVisible=false">知道怎么玩了</view>
@ -157,6 +196,7 @@
nickname: '',
avatar: '',
college: '',
loadedOnce: false,
home: {
poolAmount: 0,
joinCount: 0,
@ -181,6 +221,7 @@
data: {},
receiving: false
},
poolRuleVisible: false,
guideVisible: false,
huntModal: {
show: false,
@ -205,6 +246,11 @@
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
}
},
onLoad() {
@ -222,6 +268,11 @@
} catch (e) {}
this.loadHome()
},
onShow() {
if (this.loadedOnce) {
this.loadHome(true)
}
},
methods: {
initNavHeight() {
try {
@ -233,6 +284,9 @@
} catch (e) {}
this.navHeight = this.statusBarHeight + 44
},
showPoolRuleModal() {
this.poolRuleVisible = true
},
loadHome(silent) {
if (!this.userId) {
this.tui.toast('请先登录')
@ -254,6 +308,7 @@
this.loading = false
if (res.code == 200 && res.result) {
this.home = res.result
this.loadedOnce = true
this.showUnreadWinning()
} else if (res.message) {
this.tui.toast(res.message)
@ -749,6 +804,133 @@
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: linear-gradient(155deg, rgba(255,255,255,0.97), rgba(240,255,248,0.9));
}
.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;
}
.planet-quick-row {
margin-top: 24rpx;
display: flex;

256
package1/planet/more.vue

@ -5,7 +5,7 @@
<view class="nav-title">星球补给地图</view>
</view>
<scroll-view scroll-y class="page" :style="{paddingTop: (statusBarHeight + 58) + 'px'}">
<scroll-view scroll-y class="page" :scroll-into-view="scrollTarget" :scroll-top="scrollTop" scroll-with-animation :style="{paddingTop: (statusBarHeight + 58) + 'px'}">
<view v-if="loading" class="loading-card">正在加载星球补给...</view>
<block v-else>
<view class="hero">
@ -22,21 +22,105 @@
<planet-news :list="home.newsList"></planet-news>
<planet-operate
:data="home.operate"
@plant="onPlantTree"
@harvest="onHarvestTree"
@upgradeTower="onUpgradeTower">
</planet-operate>
<view class="guide-card">
<view class="guide-title-row">
<view>
<view class="guide-kicker">TODAY GUIDE</view>
<view class="guide-title">今天先做这三件事</view>
</view>
<view class="guide-tag">补给路线</view>
</view>
<view class="guide-list">
<view class="guide-item task">
<text class="guide-icon">🎁</text>
<view>
<text>下单得券</text>
<text>外卖团购等校园订单完成后系统自动发放星球券</text>
</view>
<text class="guide-action" @tap.stop="scrollToSupply('task')">去完成</text>
</view>
<view class="guide-item primary">
<text class="guide-icon">🌱</text>
<view>
<text>种券树</text>
<text>投入闲置星球券成熟后收回本金和收益</text>
</view>
<text class="guide-action" @tap.stop="scrollToSupply('operate')">去完成</text>
</view>
<view class="guide-item tower">
<text class="guide-icon">🛡</text>
<view>
<text>升防御塔</text>
<text>提高被偷券时的拦截反击和伤害能力</text>
</view>
<text class="guide-action" @tap.stop="scrollToSupply('operate')">去完成</text>
</view>
</view>
</view>
<planet-tasks
:tasks="home.tasks"
@claim="onClaimTask">
</planet-tasks>
<view id="supply-task-section" class="supply-anchor"></view>
<view class="section-card task-section">
<view class="section-head">
<view>
<view class="section-kicker">CAMPUS SUPPLY</view>
<view class="section-title">校园补给任务</view>
</view>
<!-- <view class="section-pill warn">可领券</view> -->
</view>
<view class="task-tip">
<text>💡</text>
<text>重点看右侧按钮显示领取的任务可以直接拿券显示去完成的任务完成校园订单后系统自动发放星球券</text>
</view>
<planet-tasks
:tasks="home.tasks"
@claim="onClaimTask"
@go="goTask">
</planet-tasks>
</view>
<view id="supply-operate-section" class="supply-anchor"></view>
<view class="section-card operate-section">
<view class="section-head">
<view>
<view class="section-kicker">FARM & DEFENSE</view>
<view class="section-title">经营补给区</view>
</view>
<view class="section-pill">种券 / 防守</view>
</view>
<planet-operate
:data="home.operate"
@plant="onPlantTree"
@harvest="onHarvestTree"
@upgradeTower="onUpgradeTower">
</planet-operate>
</view>
<view class="bottom-space"></view>
</block>
</scroll-view>
<view v-if="treeInput.show" class="tree-input-mask" @tap="closeTreeInput">
<view class="tree-input-card" @tap.stop>
<view class="tree-input-kicker">PLANT TREE</view>
<view class="tree-input-title">投入 {{treeInput.config.cycleHours || 0}}h 券树</view>
<view class="tree-input-sub">
<text>收益率 {{rateText(treeInput.config.rate)}}</text>
<text>{{treeInputRange}}</text>
</view>
<input class="tree-input" type="number" v-model="treeInput.tickets" placeholder="输入星球券数量" />
<view class="tree-quick-row">
<view @tap="fillTreeMin">最低投入</view>
<view v-if="treeInputMax > 0" @tap="fillTreeMax">最高投入</view>
</view>
<view class="tree-input-tip">
<text>🌱</text>
<text>成熟后返还本金和收益超过范围会无法种植</text>
</view>
<view class="tree-input-actions">
<view @tap="closeTreeInput">取消</view>
<view @tap="confirmPlantTree">确认种植</view>
</view>
</view>
</view>
</view>
</template>
@ -65,9 +149,27 @@
tasks: [],
newsList: [],
operate: {}
},
scrollTarget: '',
scrollTop: 0,
treeInput: {
show: false,
config: {},
tickets: ''
}
}
},
computed: {
treeInputMin() {
return (this.treeInput.config && this.treeInput.config.minInvest) || 1
},
treeInputMax() {
return (this.treeInput.config && this.treeInput.config.maxInvest) || 0
},
treeInputRange() {
return this.treeInputMax > 0 ? `可投入 ${this.treeInputMin}-${this.treeInputMax}` : `最低投入 ${this.treeInputMin}`
}
},
onLoad() {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
@ -92,7 +194,7 @@
return
}
if (!silent) this.loading = true
this.tui.request('/app/planet/home', 'POST', {
this.tui.request('/app/planet/supply', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
@ -119,22 +221,49 @@
if (res.code == 200) this.loadHome(true)
})
},
promptTickets(title, callback) {
uni.showModal({
title,
editable: true,
placeholderText: '输入星球券数量',
success: (res) => {
if (!res.confirm) return
const tickets = parseInt(res.content || '0', 10)
if (!tickets || tickets <= 0) {
this.tui.toast('请输入正确数量')
return
}
callback(tickets)
scrollToSupply(type) {
const id = type === 'task' ? 'supply-task-section' : 'supply-operate-section'
this.scrollTarget = ''
this.$nextTick(() => {
this.scrollTarget = id
setTimeout(() => {
this.scrollToSupplyByTop(id)
}, 80)
})
},
scrollToSupplyByTop(id) {
const query = uni.createSelectorQuery().in(this)
query.select('.page').boundingClientRect()
query.select('#' + id).boundingClientRect()
query.select('.page').scrollOffset()
query.exec((res) => {
const pageRect = res && res[0]
const targetRect = res && res[1]
const scroll = res && res[2]
if (!pageRect || !targetRect || !scroll) return
const top = Math.max(0, scroll.scrollTop + targetRect.top - pageRect.top - 8)
if (Math.abs(top - this.scrollTop) < 2) {
this.scrollTop = Math.max(0, top - 1)
this.$nextTick(() => {
this.scrollTop = top
})
return
}
this.scrollTop = top
})
},
goTask(task) {
const map = {
waimai: '/package2/group/groupBuyList',
group: '/package2/group/studentStoreList'
}
const url = map[task.code]
if (!url) {
if (task.code === 'invite') this.tui.toast('邀请页面正在准备中')
return
}
uni.navigateTo({ url })
},
operateRequest(url, data, successText) {
this.tui.request(url, 'POST', Object.assign({
userId: this.userId,
@ -145,9 +274,43 @@
})
},
onPlantTree(config) {
this.promptTickets(`投入 ${config.cycleHours}h 券树`, (tickets) => {
this.operateRequest('/app/planet/tree/plant', { configId: config.id, tickets }, '种植成功')
})
this.treeInput = {
show: true,
config: config || {},
tickets: ''
}
},
closeTreeInput() {
this.treeInput.show = false
},
fillTreeMin() {
this.treeInput.tickets = String(this.treeInputMin)
},
fillTreeMax() {
if (this.treeInputMax > 0) this.treeInput.tickets = String(this.treeInputMax)
},
rateText(rate) {
return Math.round(Number(rate || 0) * 100) + '%'
},
confirmPlantTree() {
const tickets = parseInt(this.treeInput.tickets || '0', 10)
if (!tickets || tickets <= 0) {
this.tui.toast('请输入正确数量')
return
}
if (tickets < this.treeInputMin) {
this.tui.toast(`最低投入${this.treeInputMin}`)
return
}
if (this.treeInputMax > 0 && tickets > this.treeInputMax) {
this.tui.toast(`最高投入${this.treeInputMax}`)
return
}
this.closeTreeInput()
this.operateRequest('/app/planet/tree/plant', {
configId: this.treeInput.config.id,
tickets
}, '种植成功')
},
onHarvestTree(order) {
if (order.status === 2) {
@ -192,5 +355,42 @@
.hero-ticket { width: 140rpx; height: 120rpx; border-radius: 34rpx; background: linear-gradient(145deg, #35D6A6, #4FB7FF); color: #fff; display: flex; flex-direction: column; align-items: center; justify-content: center; flex-shrink: 0; }
.hero-ticket text:first-child { font-size: 36rpx; font-weight: 900; }
.hero-ticket text:last-child { margin-top: 4rpx; font-size: 20rpx; }
.guide-card { position: relative; margin-top: 22rpx; padding: 28rpx; border-radius: 38rpx; background: linear-gradient(145deg, rgba(255,255,255,0.95), rgba(255,249,225,0.76)); border: 3rpx solid rgba(255,184,77,0.38); box-shadow: 0 20rpx 48rpx rgba(255,184,77,0.13); overflow: hidden; }
.guide-card:before { content: ''; position: absolute; right: -70rpx; top: -90rpx; width: 240rpx; height: 240rpx; border-radius: 50%; background: radial-gradient(circle, rgba(255,184,77,0.24), transparent 68%); }
.guide-title-row, .section-head { position: relative; z-index: 1; display: flex; align-items: flex-start; justify-content: space-between; gap: 18rpx; }
.guide-kicker, .section-kicker { color: #59CBB5; font-size: 19rpx; font-weight: 900; letter-spacing: 2rpx; }
.guide-title, .section-title { margin-top: 6rpx; color: #12342F; font-size: 34rpx; font-weight: 900; }
.guide-tag, .section-pill { flex-shrink: 0; padding: 8rpx 16rpx; border-radius: 999rpx; background: linear-gradient(135deg, #FFB84D, #35D6A6); color: #FFFFFF; font-size: 21rpx; font-weight: 900; box-shadow: 0 10rpx 22rpx rgba(53,214,166,0.16); }
.guide-list { position: relative; z-index: 1; margin-top: 22rpx; display: flex; flex-direction: column; gap: 14rpx; }
.guide-item { display: flex; align-items: center; gap: 16rpx; padding: 18rpx; border-radius: 28rpx; background: rgba(255,255,255,0.72); border: 1rpx solid rgba(255,255,255,0.9); }
.guide-item.primary { background: linear-gradient(135deg, rgba(229,255,241,0.86), rgba(255,255,255,0.72)); }
.guide-item.tower { background: linear-gradient(135deg, rgba(232,246,255,0.86), rgba(255,255,255,0.72)); }
.guide-item.task { background: linear-gradient(135deg, rgba(255,247,221,0.9), rgba(255,255,255,0.72)); }
.guide-icon { width: 56rpx; height: 56rpx; line-height: 56rpx; border-radius: 20rpx; text-align: center; background: rgba(255,255,255,0.82); font-size: 30rpx; box-shadow: 0 8rpx 18rpx rgba(18,52,47,0.06); flex-shrink: 0; }
.guide-item view { display: flex; flex-direction: column; }
.guide-item view { flex: 1; min-width: 0; }
.guide-item view text:first-child { color: #12342F; font-size: 27rpx; font-weight: 900; }
.guide-item view text:last-child { margin-top: 6rpx; color: #6B817D; font-size: 22rpx; line-height: 1.4; }
.guide-action { flex-shrink: 0; padding: 9rpx 16rpx; border-radius: 999rpx; background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; font-size: 21rpx; font-weight: 900; box-shadow: 0 10rpx 20rpx rgba(53,214,166,0.18); }
.section-card { margin-top: 24rpx; padding: 26rpx; border-radius: 40rpx; background: rgba(255,255,255,0.72); border: 2rpx solid rgba(255,255,255,0.9); box-shadow: 0 18rpx 44rpx rgba(53,214,166,0.1); }
.operate-section { background: linear-gradient(155deg, rgba(255,255,255,0.88), rgba(239,255,249,0.72)); }
.task-section { background: linear-gradient(155deg, rgba(255,255,255,0.88), rgba(232,246,255,0.72)); }
.section-pill.warn { background: linear-gradient(135deg, #FFB84D, #FF7A59); }
.task-tip { margin-top: 20rpx; padding: 18rpx 20rpx; border-radius: 26rpx; background: rgba(255,184,77,0.14); border: 2rpx solid rgba(255,184,77,0.24); color: #7A5420; font-size: 23rpx; line-height: 1.45; font-weight: 800; display: flex; gap: 12rpx; }
.task-tip text:first-child { flex-shrink: 0; }
.supply-anchor { height: 1rpx; }
.tree-input-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 80; background: rgba(18,52,47,0.36); display: flex; align-items: center; justify-content: center; padding: 0 44rpx; }
.tree-input-card { width: 100%; padding: 38rpx 34rpx 30rpx; border-radius: 42rpx; background: linear-gradient(155deg, rgba(255,255,255,0.98), rgba(239,255,249,0.92)); box-shadow: 0 34rpx 80rpx rgba(18,52,47,0.18); border: 2rpx solid rgba(255,255,255,0.92); }
.tree-input-kicker { color: #59CBB5; font-size: 20rpx; font-weight: 900; letter-spacing: 3rpx; }
.tree-input-title { margin-top: 10rpx; color: #12342F; font-size: 38rpx; font-weight: 900; }
.tree-input-sub { margin-top: 18rpx; display: flex; gap: 12rpx; flex-wrap: wrap; }
.tree-input-sub text { padding: 8rpx 16rpx; border-radius: 999rpx; background: rgba(53,214,166,0.12); color: #22B889; font-size: 22rpx; font-weight: 900; }
.tree-input { margin-top: 24rpx; height: 88rpx; padding: 0 24rpx; border-radius: 24rpx; background: rgba(18,52,47,0.06); color: #12342F; font-size: 30rpx; font-weight: 900; }
.tree-quick-row { margin-top: 18rpx; display: flex; gap: 14rpx; }
.tree-quick-row view { flex: 1; height: 58rpx; line-height: 58rpx; text-align: center; border-radius: 999rpx; background: rgba(255,184,77,0.16); color: #C27724; font-size: 23rpx; font-weight: 900; }
.tree-input-tip { margin-top: 20rpx; padding: 18rpx; border-radius: 24rpx; background: rgba(79,183,255,0.1); color: #42635E; font-size: 23rpx; line-height: 1.45; font-weight: 800; display: flex; gap: 12rpx; }
.tree-input-actions { margin-top: 28rpx; display: flex; gap: 16rpx; }
.tree-input-actions view { flex: 1; height: 76rpx; line-height: 76rpx; border-radius: 999rpx; text-align: center; font-size: 27rpx; font-weight: 900; background: rgba(18,52,47,0.07); color: #42635E; }
.tree-input-actions view:last-child { background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; box-shadow: 0 16rpx 32rpx rgba(53,214,166,0.22); }
.bottom-space { height: 60rpx; }
</style>

123
package1/planet/pkHall.vue

@ -9,7 +9,7 @@
<view class="hero">
<view class="hero-kicker">CAMPUS ARENA</view>
<view class="hero-title">同一关卡拼手速和决策</view>
<view class="hero-sub">独立于每日助推房间内所有人使用同一个 seed每人一次机会第一名通吃奖池</view>
<view class="hero-sub">独立于每日排位房间内所有人使用同一个 seed每人一次机会第一名通吃奖池</view>
<view class="create-btn" @tap="showCreate = true">创建房间</view>
</view>
@ -27,7 +27,7 @@
<view class="room-card" v-for="room in myRooms" :key="room._key" @tap="joinRoom(room)">
<view class="room-main">
<view class="room-name">{{room.roomName || '星球擂台'}}</view>
<view class="room-meta">{{room.playerCount || 0}}/{{room.maxPlayers || 2}} · 入场 {{room.entryTickets || 1}} · {{room._statusText}}</view>
<view class="room-meta">{{room.playerCount || 0}}/{{room.maxPlayers || 2}} · 入场 {{room.entryTickets || 1}} · {{room._statusText}} · {{room._countdownText}}</view>
</view>
<view class="room-pool">
<text>{{room.poolTickets || 0}}</text>
@ -63,7 +63,7 @@
<view class="room-card" v-for="room in hotRooms" :key="room._key" @tap="joinRoom(room)">
<view class="room-main">
<view class="room-name">{{room.roomName || '星球擂台'}}</view>
<view class="room-meta">{{room.playerCount || 0}}/{{room.maxPlayers || 2}} · 入场 {{room.entryTickets || 1}} </view>
<view class="room-meta">{{room.playerCount || 0}}/{{room.maxPlayers || 2}} · 入场 {{room.entryTickets || 1}} · {{room._countdownText}}</view>
</view>
<view class="room-pool">
<text>{{room.poolTickets || 0}}</text>
@ -119,6 +119,8 @@
myRecordsPageSize: 10,
myRecordsTotalPages: 1,
myRecordsLoading: false,
myTicketCount: null,
countdownTimer: null,
showCreate: false,
shareRoom: null,
form: {
@ -168,6 +170,7 @@
} catch (e) {}
this.college = uni.getStorageSync('planetCollege_' + this.regionId) || ''
if (this.regionId) {
this.loadTicketCount()
this.loadMyRooms(true)
this.loadMyRecords(true)
this.loadHotRooms(true)
@ -175,6 +178,16 @@
if (options.roomId) {
this.joinRoom({ id: options.roomId })
}
this.startCountdownTimer()
},
onUnload() {
this.clearCountdownTimer()
},
onHide() {
this.clearCountdownTimer()
},
onShow() {
if (this.userId) this.startCountdownTimer()
},
onShareAppMessage() {
const room = this.shareRoom || {}
@ -250,6 +263,7 @@
if (res.code == 200 && res.result) {
const records = this.withKeys(res.result.records || [], config.prefix + '_' + nextPage)
this[config.listKey] = reset ? records : this[config.listKey].concat(records)
this.refreshCountdowns()
this[config.pageKey] = Number(res.result.current || nextPage)
this[config.totalKey] = Math.max(1, Number(res.result.pages || 1))
}
@ -266,6 +280,16 @@
loadHotRooms(reset) {
this.loadPageList('hotRooms', reset)
},
loadTicketCount() {
return this.tui.request('/app/planet/home', 'POST', {
userId: this.userId,
regionId: this.regionId
}, false, false, true).then(res => {
if (res.code == 200 && res.result) {
this.myTicketCount = Number(res.result.myTicketCount || 0)
}
})
},
loadMoreActive() {
if (this.activeTab === 'myRooms') this.loadMyRooms(false)
else if (this.activeTab === 'myRecords') this.loadMyRecords(false)
@ -276,6 +300,7 @@
item.password = ''
item._key = item.id || (prefix + '_' + index)
item._statusText = this.roomStatus(item.status)
item._countdownText = this.roomCountdownText(item)
if (prefix.indexOf('record') === 0) {
item._title = this.recordTitle(item)
item._meta = this.recordMeta(item)
@ -285,10 +310,45 @@
return item
})
},
startCountdownTimer() {
if (this.countdownTimer) return
this.refreshCountdowns()
this.countdownTimer = setInterval(() => {
this.refreshCountdowns()
}, 1000)
},
clearCountdownTimer() {
if (!this.countdownTimer) return
clearInterval(this.countdownTimer)
this.countdownTimer = null
},
refreshCountdowns() {
this.hotRooms = this.hotRooms.map(room => Object.assign({}, room, {
_countdownText: this.roomCountdownText(room)
}))
this.myRooms = this.myRooms.map(room => Object.assign({}, room, {
_countdownText: this.roomCountdownText(room)
}))
},
setShareRoom(room) {
this.shareRoom = room
},
createRoom() {
const tickets = Number(this.form.tickets || 1)
if (tickets <= 0) {
this.tui.toast('请输入入场券数量')
return
}
if (this.myTicketCount === null) {
this.loadTicketCount().then(() => {
this.createRoom()
})
return
}
if (this.myTicketCount < tickets) {
this.tui.toast('星球券不足,无法创建房间')
return
}
this.tui.request('/app/planet/adventure/pk/create', 'POST', {
userId: this.userId,
regionId: this.regionId,
@ -299,13 +359,15 @@
maxPlayers: this.form.maxPlayers,
publicFlag: this.form.password ? 0 : 1,
password: this.form.password,
tickets: this.form.tickets
tickets: tickets
}).then(res => {
if (res.code != 200 || !res.result) {
this.tui.toast(res.message)
this.loadTicketCount()
return
}
this.showCreate = false
this.myTicketCount = Math.max(0, this.myTicketCount - tickets)
this.activeTab = 'myRooms'
this.loadMyRooms(true)
this.loadHotRooms(true)
@ -313,9 +375,38 @@
})
},
joinRoom(room) {
this.tui.request('/app/planet/adventure/pk/join', 'POST', {
this.tui.request('/app/planet/adventure/pk/roomInfo', 'POST', {
userId: this.userId,
regionId: this.regionId,
roomId: room.id
}, false, false, true).then(infoRes => {
if (infoRes.code != 200 || !infoRes.result || !infoRes.result.room) {
this.tui.toast(infoRes.message)
this.loadHotRooms(true)
this.loadMyRooms(true)
return
}
const latestRoom = infoRes.result.room
const joined = !!infoRes.result.player
if (latestRoom.status === 2 || latestRoom.status === 3) {
this.tui.toast('房间已结束')
this.loadHotRooms(true)
this.loadMyRooms(true)
return
}
if (!joined && (latestRoom.playerCount || 0) >= (latestRoom.maxPlayers || 2)) {
this.tui.toast('房间人数已满,无法进入')
this.loadHotRooms(true)
this.loadMyRooms(true)
return
}
this.doJoinRoom(Object.assign({}, room, latestRoom), joined)
})
},
doJoinRoom(room, joined) {
this.tui.request('/app/planet/adventure/pk/join', 'POST', {
userId: this.userId,
regionId: room.regionId || this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college,
@ -324,8 +415,10 @@
}).then(res => {
if (res.code != 200 || !res.result) {
this.tui.toast(res.message)
this.loadTicketCount()
return
}
this.loadTicketCount()
uni.navigateTo({
url: '/package1/planet/adventure?mode=pk&roomId=' + room.id + '&seed=' + encodeURIComponent(res.result.seed || '')
})
@ -337,6 +430,26 @@
if (status === 3) return '已取消'
return '等待中'
},
roomCountdownText(room) {
if (!room || room.status === 2 || room.status === 3) return '已结束'
const createdAt = this.parseRoomTime(room.createTime)
if (!createdAt) return '30分钟后自动结算'
const remain = createdAt + 30 * 60 * 1000 - Date.now()
if (remain <= 0) return '结算中'
const minutes = Math.floor(remain / 60000)
const seconds = Math.floor((remain % 60000) / 1000)
return '剩余 ' + this.pad2(minutes) + ':' + this.pad2(seconds) + ' 自动结算'
},
parseRoomTime(value) {
if (!value) return 0
if (typeof value === 'number') return value
const time = Date.parse(String(value).replace(/-/g, '/'))
return isNaN(time) ? 0 : time
},
pad2(value) {
value = Number(value || 0)
return value < 10 ? '0' + value : String(value)
},
recordTitle(record) {
if (record.cleared === 1) return (record.durationSeconds || 0) + '秒通关'
return '挑战失败'

30
package1/planet/rank.vue

@ -58,7 +58,10 @@
v-if="podiumSecond"
@tap="onHunt(podiumSecond)">
<view class="crown silver">2</view>
<image class="podium-avatar" :class="{stealth: podiumSecond.stealth}" :src="podiumSecond.stealth ? defaultAvatar : (podiumSecond.avatar || defaultAvatar)" mode="aspectFill"></image>
<view class="podium-avatar-wrap">
<image class="podium-avatar" :class="{stealth: podiumSecond.stealth}" :src="podiumSecond.stealth ? defaultAvatar : (podiumSecond.avatar || defaultAvatar)" mode="aspectFill"></image>
<view v-if="podiumSecond.hasTower && !podiumSecond.stealth" class="tower-badge"></view>
</view>
<view class="podium-name">{{podiumName(podiumSecond)}}</view>
<view class="podium-ticket">{{podiumSecond.ticketCount || 0}}</view>
<view class="podium-base">第二名</view>
@ -69,7 +72,10 @@
@tap="onHunt(podiumFirst)">
<view class="crown gold">1</view>
<view class="king-aura"></view>
<image class="podium-avatar champion" :class="{stealth: podiumFirst.stealth}" :src="podiumFirst.stealth ? defaultAvatar : (podiumFirst.avatar || defaultAvatar)" mode="aspectFill"></image>
<view class="podium-avatar-wrap">
<image class="podium-avatar champion" :class="{stealth: podiumFirst.stealth}" :src="podiumFirst.stealth ? defaultAvatar : (podiumFirst.avatar || defaultAvatar)" mode="aspectFill"></image>
<view v-if="podiumFirst.hasTower && !podiumFirst.stealth" class="tower-badge champion"></view>
</view>
<view class="podium-name champion-name">{{podiumName(podiumFirst)}}</view>
<view class="podium-ticket hot">{{podiumFirst.ticketCount || 0}}</view>
<view class="podium-base king">第一名</view>
@ -79,7 +85,10 @@
v-if="podiumThird"
@tap="onHunt(podiumThird)">
<view class="crown bronze">3</view>
<image class="podium-avatar" :class="{stealth: podiumThird.stealth}" :src="podiumThird.stealth ? defaultAvatar : (podiumThird.avatar || defaultAvatar)" mode="aspectFill"></image>
<view class="podium-avatar-wrap">
<image class="podium-avatar" :class="{stealth: podiumThird.stealth}" :src="podiumThird.stealth ? defaultAvatar : (podiumThird.avatar || defaultAvatar)" mode="aspectFill"></image>
<view v-if="podiumThird.hasTower && !podiumThird.stealth" class="tower-badge"></view>
</view>
<view class="podium-name">{{podiumName(podiumThird)}}</view>
<view class="podium-ticket">{{podiumThird.ticketCount || 0}}</view>
<view class="podium-base">第三名</view>
@ -94,7 +103,10 @@
v-for="(item, index) in normalList"
:key="item._rankKey">
<view class="rank-no">#{{item._displayRankNo}}</view>
<image class="avatar" :class="{stealth: item.stealth}" :src="item.stealth ? defaultAvatar : (item.avatar || defaultAvatar)" mode="aspectFill"></image>
<view class="avatar-wrap">
<image class="avatar" :class="{stealth: item.stealth}" :src="item.stealth ? defaultAvatar : (item.avatar || defaultAvatar)" mode="aspectFill"></image>
<view v-if="item.hasTower && !item.stealth" class="tower-badge small"></view>
</view>
<view class="user-main">
<view class="user-name">
<text>{{item.stealth ? '隐身侠' : (item.nickname || '神秘同学')}}</text>
@ -244,12 +256,9 @@
}
if (reset) this.loading = true
this.listLoading = true
this.tui.request('/app/planet/home', 'POST', {
this.tui.request('/app/planet/rank/page', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college,
pageNumber: this.pageNum,
pageSize: this.pageSize
}, false, false, true).then((res) => {
@ -403,6 +412,7 @@
.crown.gold { background: linear-gradient(145deg, #FFF7B8, #FFB84D); }
.crown.silver { background: linear-gradient(145deg, #FFFFFF, #BFD7FF); color: #28415F; }
.crown.bronze { background: linear-gradient(145deg, #FFE0B8, #C87A35); color: #4A2500; }
.podium-avatar-wrap { position: relative; z-index: 2; }
.podium-avatar { position: relative; z-index: 2; width: 112rpx; height: 112rpx; border-radius: 38rpx; border: 6rpx solid rgba(255,255,255,0.88); box-shadow: 0 18rpx 36rpx rgba(0,0,0,0.22); background: rgba(255,255,255,0.22); }
.podium-avatar.champion { width: 136rpx; height: 136rpx; border-radius: 46rpx; border-color: #FFE08A; box-shadow: 0 20rpx 46rpx rgba(255,184,77,0.36); animation: championFloat 2.1s ease-in-out infinite; }
@keyframes championFloat { 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(-8rpx); } }
@ -417,8 +427,12 @@
.rank-list { margin-top: 18rpx; }
.rank-row { margin-top: 16rpx; padding: 22rpx; border-radius: 34rpx; background: rgba(255,255,255,0.82); border: 2rpx solid rgba(255,255,255,0.94); box-shadow: 0 16rpx 36rpx rgba(53,214,166,0.1); display: flex; align-items: center; }
.rank-no { min-width: 70rpx; color: #22B889; font-size: 28rpx; font-weight: 900; }
.avatar-wrap { position: relative; flex-shrink: 0; }
.avatar { width: 86rpx; height: 86rpx; border-radius: 28rpx; border: 4rpx solid rgba(255,255,255,0.94); box-shadow: 0 12rpx 24rpx rgba(18,52,47,0.12); flex-shrink: 0; }
.avatar.stealth { opacity: .58; filter: grayscale(100%); }
.tower-badge { position: absolute; right: -10rpx; bottom: -8rpx; z-index: 4; min-width: 42rpx; height: 42rpx; line-height: 42rpx; padding: 0 6rpx; border-radius: 14rpx; background: linear-gradient(135deg, #36516F, #7C8DA6); color: #FFFFFF; font-size: 20rpx; font-weight: 900; text-align: center; border: 3rpx solid rgba(255,255,255,0.95); box-shadow: 0 8rpx 18rpx rgba(18,52,47,0.22); }
.tower-badge.champion { right: -8rpx; bottom: -4rpx; background: linear-gradient(135deg, #4A2A00, #FFB84D); }
.tower-badge.small { right: -12rpx; bottom: -8rpx; min-width: 38rpx; height: 38rpx; line-height: 38rpx; border-radius: 13rpx; font-size: 18rpx; }
.user-main { flex: 1; min-width: 0; margin-left: 18rpx; }
.user-name { display: flex; align-items: center; color: #12342F; font-size: 28rpx; font-weight: 900; }
.user-name text:first-child { max-width: 220rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

2
package1/planet/ticketLog.vue

@ -35,7 +35,7 @@
</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-face">🏴</view>
<view class="raider-mid">
<view class="raider-name">{{item.fromUserName || '神秘掠夺者'}}</view>
<view class="raider-desc">{{raiderDesc(item)}}</view>

Loading…
Cancel
Save