|
|
|
@ -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> |
|
|
|
|
|
|
|
<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> |
|
|
|
|
|
|
|
<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> |
|
|
|
|
|
|
|
<planet-tasks |
|
|
|
:tasks="home.tasks" |
|
|
|
@claim="onClaimTask"> |
|
|
|
</planet-tasks> |
|
|
|
|
|
|
|
</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,7 +149,25 @@ |
|
|
|
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() { |
|
|
|
@ -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('请输入正确数量') |
|
|
|
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 |
|
|
|
} |
|
|
|
callback(tickets) |
|
|
|
} |
|
|
|
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> |
|
|
|
|