wangfukang 5 days ago
parent
commit
cbc5d239a6
  1. 108
      package1/components/planet/planet-daily-loop.vue
  2. 157
      package1/components/planet/planet-operate.vue
  3. 153
      package1/planet/index.vue

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

@ -0,0 +1,108 @@
<template>
<view class="dl">
<view class="dl-top">
<view>
<view class="dl-kicker">2 MIN DAILY</view>
<view class="dl-title">今天就做这几步</view>
<view class="dl-sub">收券看事件反搜投奖池90秒走人</view>
</view>
<view class="dl-clock">{{data.minutesToDraw || 0}}min</view>
</view>
<view class="dl-pool">
<view class="dl-pool-main">
<text class="dl-pool-label">今晚现金奖池</text>
<text class="dl-pool-money">{{poolAmount}}</text>
</view>
<view class="dl-prob">
<text>{{data.myProbability || 0}}%</text>
<text>我的概率</text>
</view>
</view>
<view class="dl-stat">
<view>
<text>{{data.todayCollectedTickets || 0}}</text>
<text>今日收券</text>
</view>
<view>
<text>{{data.myPoolTickets || 0}}</text>
<text>已投奖池</text>
</view>
<view>
<text>{{data.poolJoinCount || 0}}</text>
<text>参与同学</text>
</view>
</view>
<view class="dl-events">
<view class="dl-event" v-for="(e,i) in events" :key="i">{{e}}</view>
</view>
<view class="dl-actions">
<view class="dl-action collect" @tap="$emit('collect')">
<text>1</text>
<text>{{collectText}}</text>
</view>
<view class="dl-action revenge" @tap="$emit('revenge')">
<text>2</text>
<text>{{data.hasRevengeTarget ? '反搜一下' : '随机搜查'}}</text>
</view>
<view class="dl-action join" @tap="$emit('join')">
<text>3</text>
<text>{{joinText}}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
data: { type: Object, default: () => ({}) },
poolAmount: { type: [String, Number], default: 0 }
},
computed: {
events() {
return this.data.events || []
},
collectText() {
if (!this.data.signedToday) return '签到收券'
if (this.data.boxAvailable) return '开补给箱'
return '已收完'
},
joinText() {
const n = this.data.suggestedAddTickets || 1
return '投' + n + '券冲奖池'
}
}
}
</script>
<style lang="scss" scoped>
.dl { margin-top: 24rpx; 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: hidden; 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-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-clock { padding: 14rpx 18rpx; border-radius: 999rpx; background: rgba(255,255,255,0.82); color: #22B889; font-size: 24rpx; font-weight: 900; }
.dl-pool { position: relative; z-index: 1; margin-top: 28rpx; display: flex; justify-content: space-between; align-items: center; padding: 26rpx; border-radius: 34rpx; background: linear-gradient(135deg, rgba(255,255,255,0.86), rgba(232,246,255,0.72)); }
.dl-pool-main { display: flex; flex-direction: column; }
.dl-pool-label { color: #6B817D; font-size: 24rpx; }
.dl-pool-money { margin-top: 8rpx; color: #12342F; font-size: 50rpx; font-weight: 900; }
.dl-prob { width: 132rpx; height: 132rpx; border-radius: 46rpx; background: linear-gradient(145deg, #35D6A6, #4FB7FF); color: #fff; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 18rpx 40rpx rgba(53,214,166,0.24); }
.dl-prob text:first-child { font-size: 34rpx; font-weight: 900; }
.dl-prob text:last-child { font-size: 20rpx; margin-top: 4rpx; }
.dl-stat { margin-top: 18rpx; display: flex; gap: 14rpx; }
.dl-stat view { flex: 1; padding: 18rpx 10rpx; border-radius: 26rpx; background: rgba(255,255,255,0.66); display: flex; flex-direction: column; align-items: center; }
.dl-stat text:first-child { color: #12342F; font-size: 30rpx; font-weight: 900; }
.dl-stat text:last-child { color: #6B817D; font-size: 20rpx; margin-top: 4rpx; }
.dl-events { margin-top: 18rpx; }
.dl-event { margin-top: 10rpx; padding: 14rpx 18rpx; border-radius: 24rpx; background: rgba(255,255,255,0.62); color: #42635E; font-size: 24rpx; }
.dl-actions { margin-top: 22rpx; display: flex; gap: 14rpx; }
.dl-action { flex: 1; min-height: 94rpx; border-radius: 30rpx; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #12342F; font-size: 23rpx; font-weight: 900; background: rgba(255,255,255,0.78); }
.dl-action text:first-child { width: 34rpx; height: 34rpx; line-height: 34rpx; text-align: center; border-radius: 50%; background: #22B889; color: #fff; font-size: 20rpx; margin-bottom: 8rpx; }
.dl-action.join { background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #fff; box-shadow: 0 18rpx 36rpx rgba(53,214,166,0.22); }
.dl-action.join text:first-child { background: rgba(255,255,255,0.28); }
</style>

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

@ -0,0 +1,157 @@
<template>
<view class="po">
<view class="po-head">
<view>
<view class="po-kicker">CAMPUS FARM</view>
<view class="po-title">松鼠星际农场</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>
</view>
<view class="po-grid">
<view class="po-card tree">
<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>
<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>
</view>
</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-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">
<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>
</template>
<script>
export default {
props: {
data: { type: Object, default: () => ({}) }
},
computed: {
state() {
return (this.data && this.data.state) || {}
},
treeConfigs() {
return (this.data && this.data.treeConfigs) || []
},
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 '校园增益'
}
}
}
</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-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-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-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-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-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-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-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; }
.po-order-btn { padding: 14rpx 20rpx; border-radius: 999rpx; background: #22B889; color: #fff; font-size: 22rpx; font-weight: 800; }
</style>

153
package1/planet/index.vue

@ -38,6 +38,14 @@
@draw="goDrawResult"> @draw="goDrawResult">
</planet-header> </planet-header>
<planet-daily-loop
:data="home.dailyLoop"
:pool-amount="home.poolAmount"
@collect="onDailyCollect"
@revenge="onDailyRevenge"
@join="onJoinPool">
</planet-daily-loop>
<planet-news :list="home.newsList"></planet-news> <planet-news :list="home.newsList"></planet-news>
<planet-me <planet-me
@ -52,6 +60,18 @@
@open="openBox"> @open="openBox">
</planet-box> </planet-box>
<planet-operate
:data="home.operate"
@plant="onPlantTree"
@harvest="onHarvestTree"
@store="onStoreWarehouse"
@take="onTakeWarehouse"
@upgradeWarehouse="onUpgradeWarehouse"
@upgradeTower="onUpgradeTower"
@search="onRandomSearch"
@bid="onBidLandmark">
</planet-operate>
<planet-tasks <planet-tasks
:tasks="home.tasks" :tasks="home.tasks"
@claim="onClaimTask"> @claim="onClaimTask">
@ -103,6 +123,8 @@
import huntModal from '@/package1/components/planet/hunt-modal.vue' import huntModal from '@/package1/components/planet/hunt-modal.vue'
import buffShop from '@/package1/components/planet/buff-shop.vue' import buffShop from '@/package1/components/planet/buff-shop.vue'
import planetNews from '@/package1/components/planet/planet-news.vue' import planetNews from '@/package1/components/planet/planet-news.vue'
import planetOperate from '@/package1/components/planet/planet-operate.vue'
import planetDailyLoop from '@/package1/components/planet/planet-daily-loop.vue'
export default { export default {
components: { components: {
@ -113,7 +135,9 @@
planetRank, planetRank,
huntModal, huntModal,
buffShop, buffShop,
planetNews planetNews,
planetOperate,
planetDailyLoop
}, },
data() { data() {
return { return {
@ -134,7 +158,9 @@
rankList: [], rankList: [],
buffShop: [], buffShop: [],
newsList: [], newsList: [],
myBuffs: [] myBuffs: [],
operate: {},
dailyLoop: {}
}, },
boxOpening: false, boxOpening: false,
boxResult: { boxResult: {
@ -313,6 +339,129 @@
}) })
}) })
}, },
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)
}
})
},
operateRequest(url, data, successText) {
this.tui.request(url, 'POST', Object.assign({
userId: this.userId,
regionId: this.regionId
}, data || {})).then((res) => {
this.tui.toast(res.code == 200 ? successText : res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
})
},
onPlantTree(config) {
this.promptTickets(`投入 ${config.cycleHours}h 券树`, (tickets) => {
this.operateRequest('/app/planet/tree/plant', { configId: config.id, tickets }, '种植成功')
})
},
onHarvestTree(order) {
if (order.status === 2) {
this.tui.toast('这棵券树已经收获')
return
}
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: '随机同校区玩家' })
},
onDailyCollect() {
if (!this.home.dailyLoop || !this.home.dailyLoop.signedToday) {
this.onSign()
return
}
if (this.home.dailyLoop.boxAvailable) {
this.openBox()
return
}
this.tui.toast('今天的顺手券已经收完')
},
onDailyRevenge() {
const loop = this.home.dailyLoop || {}
if (loop.hasRevengeTarget && loop.revengeTarget && loop.revengeTarget.fromUserId) {
this.startSearch('/app/planet/search/target', { toUserId: loop.revengeTarget.fromUserId }, loop.revengeTarget)
} else {
this.onRandomSearch()
}
},
startSearch(url, data, target) {
this.huntModal.show = true
this.huntModal.phase = 'searching'
this.huntModal.result = null
this.huntModal.target = target || { nickname: '同校区玩家' }
setTimeout(() => { this.huntModal.phase = 'locking' }, 900)
setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800)
this.tui.request(url, 'POST', Object.assign({
userId: this.userId,
regionId: this.regionId
}, data || {}), false, false, true).then((res) => {
setTimeout(() => {
if (res.code == 200 && res.result) {
this.huntModal.result = {
result: res.result.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
})
},
onJoinPool() {
const loop = this.home.dailyLoop || {}
const n = loop.suggestedAddTickets || 1
this.tui.modal('投入现金奖池', `建议投入 ${n} 张星球券,预计概率提升到 ${loop.suggestedProbability || 0}%。投入后星球券会被回收,确认上车?`, true, (ok) => {
if (!ok) return
this.operateRequest('/app/planet/pool/join', { tickets: n }, '投入成功')
}, '#22B889', '立即上车')
},
onBidLandmark(item) {
this.promptTickets(`争夺${item.name}`, (tickets) => {
this.operateRequest('/app/planet/landmark/bid', { landmarkId: item.id, tickets }, '投入成功')
})
},
goDrawResult() { goDrawResult() {
uni.navigateTo({ uni.navigateTo({
url: '/package1/planet/drawResult' url: '/package1/planet/drawResult'

Loading…
Cancel
Save