13 changed files with 1863 additions and 335 deletions
@ -0,0 +1,298 @@ |
|||
<template> |
|||
<view class="more" :style="{'--statusbar': statusBarHeight + 'px'}"> |
|||
<view class="nav" :style="{paddingTop: statusBarHeight + 'px'}"> |
|||
<view class="nav-back" @tap="goBack"><text>‹</text></view> |
|||
<view class="nav-title">星球补给地图</view> |
|||
</view> |
|||
|
|||
<scroll-view scroll-y class="page" :style="{paddingTop: (statusBarHeight + 58) + 'px'}"> |
|||
<view v-if="loading" class="loading-card">正在加载星球补给...</view> |
|||
<block v-else> |
|||
<view class="hero"> |
|||
<view> |
|||
<view class="hero-kicker">PLANET SUPPLY</view> |
|||
<view class="hero-title">把长期玩法都放在这里</view> |
|||
<view class="hero-sub">券树、仓库、防御塔、地标、任务和能量站,想深入经营时再进来。</view> |
|||
</view> |
|||
<view class="hero-ticket"> |
|||
<text>{{home.myTicketCount || 0}}</text> |
|||
<text>星球券</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<planet-news :list="home.newsList"></planet-news> |
|||
|
|||
<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 |
|||
:tasks="home.tasks" |
|||
@claim="onClaimTask"> |
|||
</planet-tasks> |
|||
|
|||
<buff-shop |
|||
:list="home.buffShop" |
|||
:my-ticket="home.myTicketCount" |
|||
@buy="onBuyBuff"> |
|||
</buff-shop> |
|||
|
|||
<view class="bottom-space"></view> |
|||
</block> |
|||
</scroll-view> |
|||
|
|||
<hunt-modal |
|||
:show="huntModal.show" |
|||
:phase="huntModal.phase" |
|||
:result="huntModal.result" |
|||
:target="huntModal.target" |
|||
@close="closeHunt"> |
|||
</hunt-modal> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import planetTasks from '@/package1/components/planet/planet-tasks.vue' |
|||
import huntModal from '@/package1/components/planet/hunt-modal.vue' |
|||
import buffShop from '@/package1/components/planet/buff-shop.vue' |
|||
import planetNews from '@/package1/components/planet/planet-news.vue' |
|||
import planetOperate from '@/package1/components/planet/planet-operate.vue' |
|||
|
|||
export default { |
|||
components: { |
|||
planetTasks, |
|||
huntModal, |
|||
buffShop, |
|||
planetNews, |
|||
planetOperate |
|||
}, |
|||
data() { |
|||
return { |
|||
statusBarHeight: 20, |
|||
loading: true, |
|||
userId: '', |
|||
regionId: '', |
|||
nickname: '', |
|||
avatar: '', |
|||
college: '', |
|||
home: { |
|||
myTicketCount: 0, |
|||
tasks: [], |
|||
buffShop: [], |
|||
newsList: [], |
|||
operate: {} |
|||
}, |
|||
huntModal: { |
|||
show: false, |
|||
phase: 'searching', |
|||
result: null, |
|||
target: {} |
|||
} |
|||
} |
|||
}, |
|||
onLoad() { |
|||
const sys = uni.getSystemInfoSync() |
|||
this.statusBarHeight = sys.statusBarHeight || 20 |
|||
this.userId = uni.getStorageSync('id') || '' |
|||
this.nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || '' |
|||
this.avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || '' |
|||
try { |
|||
const area = uni.getStorageSync('area') |
|||
if (area) this.regionId = JSON.parse(area).id || '' |
|||
} catch (e) {} |
|||
this.loadHome() |
|||
}, |
|||
methods: { |
|||
loadHome(silent) { |
|||
if (!this.userId) { |
|||
this.tui.toast('请先登录') |
|||
return |
|||
} |
|||
if (!this.regionId) { |
|||
this.loading = false |
|||
this.tui.toast('未获取到校区信息') |
|||
return |
|||
} |
|||
if (!silent) this.loading = true |
|||
this.tui.request('/app/planet/home', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
nickname: this.nickname, |
|||
avatar: this.avatar, |
|||
college: this.college |
|||
}, false, false, true).then((res) => { |
|||
this.loading = false |
|||
if (res.code == 200 && res.result) { |
|||
this.home = res.result |
|||
} else if (res.message) { |
|||
this.tui.toast(res.message) |
|||
} |
|||
}).catch(() => { |
|||
this.loading = false |
|||
}) |
|||
}, |
|||
onClaimTask(task) { |
|||
this.tui.request('/app/planet/task/claim', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
taskCode: task.code |
|||
}).then((res) => { |
|||
this.tui.toast(res.message, 1500, res.code == 200) |
|||
if (res.code == 200) this.loadHome(true) |
|||
}) |
|||
}, |
|||
onBuyBuff(buff) { |
|||
if (buff.active) { |
|||
this.tui.toast('该增益正在生效中') |
|||
return |
|||
} |
|||
if (this.home.myTicketCount < buff.costTickets) { |
|||
this.tui.toast('星球券不足') |
|||
return |
|||
} |
|||
this.tui.modal('确认购买', `消耗 ${buff.costTickets} 张星球券购买「${buff.name}」?`, true, (ok) => { |
|||
if (!ok) return |
|||
this.operateRequest('/app/planet/buff/buy', { buffId: buff.id }, '购买成功') |
|||
}) |
|||
}, |
|||
promptTickets(title, callback) { |
|||
uni.showModal({ |
|||
title, |
|||
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: '随机同校区玩家' }) |
|||
}, |
|||
onBidLandmark(item) { |
|||
this.promptTickets(`争夺${item.name}`, (tickets) => { |
|||
this.operateRequest('/app/planet/landmark/bid', { landmarkId: item.id, tickets }, '投入成功') |
|||
}) |
|||
}, |
|||
startSearch(url, data, target) { |
|||
this.huntModal.show = true |
|||
this.huntModal.phase = 'searching' |
|||
this.huntModal.result = null |
|||
this.huntModal.target = target || { nickname: '同校区玩家' } |
|||
setTimeout(() => { this.huntModal.phase = 'locking' }, 900) |
|||
setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800) |
|||
this.tui.request(url, 'POST', Object.assign({ |
|||
userId: this.userId, |
|||
regionId: this.regionId |
|||
}, data || {}), false, false, true).then((res) => { |
|||
setTimeout(() => { |
|||
if (res.code == 200 && res.result) { |
|||
this.huntModal.result = { |
|||
result: res.result.intercepted ? 'shield' : 'success', |
|||
message: res.result.message, |
|||
gainTickets: res.result.gainTickets, |
|||
totalGain: res.result.gainTickets, |
|||
remainHunt: res.result.remainSearchCount |
|||
} |
|||
this.huntModal.phase = 'result' |
|||
this.loadHome(true) |
|||
} else { |
|||
this.huntModal.show = false |
|||
this.tui.toast(res.message) |
|||
} |
|||
}, 2200) |
|||
}).catch(() => { |
|||
this.huntModal.show = false |
|||
}) |
|||
}, |
|||
closeHunt() { |
|||
this.huntModal.show = false |
|||
this.huntModal.result = null |
|||
}, |
|||
goBack() { |
|||
uni.navigateBack({ |
|||
delta: 1, |
|||
fail() { |
|||
uni.navigateTo({ url: '/package1/planet/index' }) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.more { |
|||
min-height: 100vh; |
|||
background: linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 46%, #FFF8DE 100%); |
|||
color: #12342F; |
|||
} |
|||
.nav { position: fixed; top: 0; left: 0; right: 0; height: 44px; z-index: 20; display: flex; align-items: center; justify-content: center; box-sizing: content-box; background: linear-gradient(180deg, rgba(243,255,244,0.96), rgba(243,255,244,0)); } |
|||
.nav-back { position: absolute; left: 20rpx; bottom: 0; height: 44px; width: 60rpx; display: flex; align-items: center; justify-content: center; } |
|||
.nav-back text { color: #12342F; font-size: 48rpx; font-weight: 300; } |
|||
.nav-title { color: #12342F; font-size: 34rpx; font-weight: 900; align-self: flex-end; padding-bottom: 6rpx; } |
|||
.page { height: 100vh; box-sizing: border-box; padding-left: 22rpx; padding-right: 22rpx; } |
|||
.loading-card { margin-top: 24rpx; height: 220rpx; border-radius: 38rpx; background: rgba(255,255,255,0.76); display: flex; align-items: center; justify-content: center; color: #6B817D; font-size: 26rpx; } |
|||
.hero { margin-top: 24rpx; padding: 32rpx; border-radius: 42rpx; background: linear-gradient(145deg, rgba(255,255,255,0.92), rgba(239,255,249,0.74)); border: 2rpx solid rgba(255,255,255,0.92); box-shadow: 0 22rpx 54rpx rgba(53,214,166,0.12); display: flex; justify-content: space-between; gap: 20rpx; } |
|||
.hero-kicker { color: #59CBB5; font-size: 20rpx; font-weight: 900; letter-spacing: 3rpx; } |
|||
.hero-title { margin-top: 8rpx; color: #12342F; font-size: 38rpx; font-weight: 900; } |
|||
.hero-sub { margin-top: 10rpx; color: #6B817D; font-size: 24rpx; line-height: 1.45; } |
|||
.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; } |
|||
.bottom-space { height: 60rpx; } |
|||
</style> |
|||
@ -0,0 +1,306 @@ |
|||
<template> |
|||
<view class="arena"> |
|||
<view class="nav" :style="{height: navHeight + 'px', paddingTop: statusBarHeight + 'px'}"> |
|||
<view class="nav-back" @tap="goBack"><text>‹</text></view> |
|||
<view class="nav-title">星球擂台</view> |
|||
</view> |
|||
|
|||
<scroll-view scroll-y class="page" :style="{paddingTop: (navHeight + 10) + 'px'}" @scrolltolower="loadMoreHotRooms"> |
|||
<view class="hero"> |
|||
<view class="hero-kicker">CAMPUS ARENA</view> |
|||
<view class="hero-title">同一关卡,拼手速和决策</view> |
|||
<view class="hero-sub">独立于每日助推赛,房间内所有人使用同一个 seed。每人一次机会,第一名通吃奖池。</view> |
|||
<view class="create-btn" @tap="showCreate = true">创建房间</view> |
|||
</view> |
|||
|
|||
<view class="section duo"> |
|||
<view class="panel"> |
|||
<view class="section-head"><text>我的房间</text></view> |
|||
<view class="mini-row" v-for="room in myRooms" :key="room._key" @tap="joinRoom(room)"> |
|||
<text>{{room.roomName || '星球擂台'}}</text> |
|||
<text>{{roomStatus(room.status)}}</text> |
|||
<button class="mini-share" open-type="share" @tap.stop="setShareRoom(room)">分享</button> |
|||
</view> |
|||
<view class="empty small" v-if="!myRooms.length">暂无房间</view> |
|||
</view> |
|||
<view class="panel"> |
|||
<view class="section-head"><text>我的战绩</text></view> |
|||
<view class="mini-row" v-for="record in myRecords" :key="record._key"> |
|||
<text>{{record.cleared ? (record.durationSeconds + '秒通关') : '挑战失败'}}</text> |
|||
<text>{{record.rankNo ? ('第' + record.rankNo) : '待结算'}}</text> |
|||
</view> |
|||
<view class="empty small" v-if="!myRecords.length">暂无战绩</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section hot-section"> |
|||
<view class="section-head"> |
|||
<text>热门房间</text> |
|||
<text @tap="loadHall">刷新</text> |
|||
</view> |
|||
<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> |
|||
<view class="room-pool"> |
|||
<text>{{room.poolTickets || 0}}</text> |
|||
<text>奖池</text> |
|||
</view> |
|||
<button class="share-mini" open-type="share" @tap.stop="setShareRoom(room)">邀请</button> |
|||
</view> |
|||
<view class="empty" v-if="!hotRooms.length">还没有公开擂台,先开一间等同学来挑战</view> |
|||
<view class="load-more" v-if="hotRooms.length">{{hotLoadText}}</view> |
|||
</view> |
|||
</scroll-view> |
|||
|
|||
<view class="modal" v-if="showCreate"> |
|||
<view class="modal-card"> |
|||
<view class="modal-title">创建星球擂台</view> |
|||
<input class="field" v-model="form.roomName" placeholder="房间名称" /> |
|||
<input class="field" v-model.number="form.maxPlayers" type="number" placeholder="人数 2-5" /> |
|||
<input class="field" v-model.number="form.tickets" type="number" placeholder="入场券数量" /> |
|||
<input class="field" v-model="form.password" placeholder="私密房密码,不填则公开" /> |
|||
<view class="modal-actions"> |
|||
<view class="sub-btn" @tap="showCreate = false">取消</view> |
|||
<view class="main-btn" @tap="createRoom">创建</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
statusBarHeight: 20, |
|||
navHeight: 64, |
|||
userId: '', |
|||
regionId: '', |
|||
nickname: '', |
|||
avatar: '', |
|||
college: '', |
|||
hotRooms: [], |
|||
myRooms: [], |
|||
myRecords: [], |
|||
hotPageNum: 1, |
|||
hotPageSize: 10, |
|||
hotTotalPages: 1, |
|||
hotLoading: false, |
|||
showCreate: false, |
|||
shareRoom: null, |
|||
form: { |
|||
roomName: '今晚冲榜局', |
|||
maxPlayers: 2, |
|||
tickets: 1, |
|||
password: '' |
|||
} |
|||
} |
|||
}, |
|||
computed: { |
|||
hotLoadText() { |
|||
if (this.hotLoading) return '正在加载更多房间...' |
|||
if (this.hotPageNum >= this.hotTotalPages) return '没有更多擂台了' |
|||
return '上滑加载更多擂台' |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
const sys = uni.getSystemInfoSync() |
|||
this.statusBarHeight = sys.statusBarHeight || 20 |
|||
this.initNavHeight() |
|||
options = options || {} |
|||
if (!uni.getStorageSync('id')) { |
|||
const path = options.roomId ? ('/package1/planet/pkHall?roomId=' + options.roomId) : '/package1/planet/pkHall' |
|||
uni.setStorageSync('planetPendingSharePath', path) |
|||
uni.redirectTo({ |
|||
url: '/package2/login/login?redirect=planetPendingSharePath' |
|||
}) |
|||
return |
|||
} |
|||
this.userId = uni.getStorageSync('id') || '' |
|||
this.nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || '' |
|||
this.avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || '' |
|||
try { |
|||
const area = uni.getStorageSync('area') |
|||
if (area) this.regionId = JSON.parse(area).id || '' |
|||
} catch (e) {} |
|||
this.college = uni.getStorageSync('planetCollege_' + this.regionId) || '' |
|||
if (this.regionId) this.loadHall() |
|||
if (options.roomId) { |
|||
this.joinRoom({ id: options.roomId }) |
|||
} |
|||
}, |
|||
onShareAppMessage() { |
|||
const room = this.shareRoom || {} |
|||
const roomId = room.id || '' |
|||
return { |
|||
title: `星球擂台开战:${room.roomName || '同一关卡来PK'}`, |
|||
path: '/package1/planet/adventure?mode=pk&roomId=' + roomId |
|||
} |
|||
}, |
|||
methods: { |
|||
initNavHeight() { |
|||
try { |
|||
const menu = uni.getMenuButtonBoundingClientRect && uni.getMenuButtonBoundingClientRect() |
|||
if (menu && menu.top && menu.height) { |
|||
this.navHeight = menu.top + menu.height + 8 |
|||
return |
|||
} |
|||
} catch (e) {} |
|||
this.navHeight = this.statusBarHeight + 44 |
|||
}, |
|||
loadHall() { |
|||
this.hotPageNum = 1 |
|||
this.hotTotalPages = 1 |
|||
this.hotRooms = [] |
|||
this.tui.request('/app/planet/adventure/pk/hall', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
pageNumber: 1, |
|||
pageSize: this.hotPageSize |
|||
}, false, false, true).then(res => { |
|||
if (res.code == 200 && res.result) { |
|||
this.hotRooms = this.withKeys(res.result.hotRooms || [], 'hot_1') |
|||
this.hotPageNum = Number(res.result.hotPageNumber || 1) |
|||
this.hotTotalPages = Math.max(1, Number(res.result.hotTotalPages || 1)) |
|||
this.myRooms = this.withKeys(res.result.myRooms || [], 'mine') |
|||
this.myRecords = this.withKeys(res.result.myRecords || [], 'record') |
|||
} |
|||
}) |
|||
}, |
|||
loadHotRooms(reset) { |
|||
if (this.hotLoading) return |
|||
if (!reset && this.hotPageNum >= this.hotTotalPages) return |
|||
const nextPage = reset ? 1 : this.hotPageNum + 1 |
|||
this.hotLoading = true |
|||
this.tui.request('/app/planet/adventure/pk/hall', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
pageNumber: nextPage, |
|||
pageSize: this.hotPageSize |
|||
}, false, false, true).then(res => { |
|||
this.hotLoading = false |
|||
if (res.code == 200 && res.result) { |
|||
const list = this.withKeys(res.result.hotRooms || [], 'hot_' + nextPage) |
|||
this.hotRooms = reset ? list : [...this.hotRooms, ...list] |
|||
this.hotPageNum = Number(res.result.hotPageNumber || nextPage) |
|||
this.hotTotalPages = Math.max(1, Number(res.result.hotTotalPages || 1)) |
|||
} |
|||
}).catch(() => { |
|||
this.hotLoading = false |
|||
}) |
|||
}, |
|||
loadMoreHotRooms() { |
|||
this.loadHotRooms(false) |
|||
}, |
|||
withKeys(list, prefix) { |
|||
return list.map((item, index) => { |
|||
item.password = '' |
|||
item._key = item.id || (prefix + '_' + index) |
|||
return item |
|||
}) |
|||
}, |
|||
setShareRoom(room) { |
|||
this.shareRoom = room |
|||
}, |
|||
createRoom() { |
|||
this.tui.request('/app/planet/adventure/pk/create', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
nickname: this.nickname, |
|||
avatar: this.avatar, |
|||
college: this.college, |
|||
roomName: this.form.roomName, |
|||
maxPlayers: this.form.maxPlayers, |
|||
publicFlag: this.form.password ? 0 : 1, |
|||
password: this.form.password, |
|||
tickets: this.form.tickets |
|||
}).then(res => { |
|||
if (res.code != 200 || !res.result) { |
|||
this.tui.toast(res.message) |
|||
return |
|||
} |
|||
this.showCreate = false |
|||
this.loadHall() |
|||
this.joinRoom(res.result) |
|||
}) |
|||
}, |
|||
joinRoom(room) { |
|||
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: room.id, |
|||
password: room.password || '' |
|||
}).then(res => { |
|||
if (res.code != 200 || !res.result) { |
|||
this.tui.toast(res.message) |
|||
return |
|||
} |
|||
uni.navigateTo({ |
|||
url: '/package1/planet/adventure?mode=pk&roomId=' + room.id + '&seed=' + encodeURIComponent(res.result.seed || '') |
|||
}) |
|||
}) |
|||
}, |
|||
roomStatus(status) { |
|||
if (status === 2) return '已结算' |
|||
if (status === 1) return '进行中' |
|||
if (status === 3) return '已取消' |
|||
return '等待中' |
|||
}, |
|||
goBack() { |
|||
const pages = getCurrentPages ? getCurrentPages() : [] |
|||
if (pages.length > 1) { |
|||
uni.navigateBack({ delta: 1 }) |
|||
return |
|||
} |
|||
uni.redirectTo({ url: '/package1/planet/index' }) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.arena { min-height: 100vh; background: linear-gradient(155deg, #F7F2FF 0%, #EAF8FF 46%, #FFF7DE 100%); color: #172033; } |
|||
.nav { position: fixed; top: 0; left: 0; right: 0; z-index: 20; box-sizing: border-box; display: flex; align-items: center; justify-content: center; background: rgba(247,242,255,.9); } |
|||
.nav-back { position: absolute; left: 24rpx; bottom: 12rpx; width: 56rpx; height: 56rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(255,255,255,.72); color: #172033; box-shadow: 0 8rpx 20rpx rgba(23,32,51,.08); } |
|||
.nav-back text { font-size: 42rpx; line-height: 1; transform: translateY(-2rpx); } |
|||
.nav-title { padding-bottom: 14rpx; font-size: 32rpx; font-weight: 900; } |
|||
.page { height: 100vh; box-sizing: border-box; padding-left: 24rpx; padding-right: 24rpx; } |
|||
.hero, .section, .panel { margin-bottom: 24rpx; border-radius: 42rpx; background: rgba(255,255,255,.84); padding: 30rpx; box-shadow: 0 18rpx 48rpx rgba(124,58,237,.10); } |
|||
.hot-section { margin-bottom: 46rpx; } |
|||
.hero { background: radial-gradient(circle at 90% 10%, rgba(124,58,237,.22), transparent 36%), linear-gradient(135deg, rgba(255,255,255,.96), rgba(232,246,255,.78)); } |
|||
.hero-kicker { color: #7C3AED; font-size: 22rpx; font-weight: 900; letter-spacing: 2rpx; } |
|||
.hero-title { margin-top: 10rpx; font-size: 42rpx; font-weight: 900; } |
|||
.hero-sub { margin-top: 14rpx; color: #667085; font-size: 24rpx; line-height: 1.55; } |
|||
.create-btn, .main-btn { margin-top: 22rpx; height: 76rpx; line-height: 76rpx; border-radius: 999rpx; text-align: center; color: #fff; font-size: 28rpx; font-weight: 900; background: linear-gradient(135deg, #7C3AED, #38BDF8); box-shadow: 0 16rpx 34rpx rgba(124,58,237,.18); } |
|||
.section-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18rpx; color: #7C3AED; font-size: 23rpx; font-weight: 900; } |
|||
.section-head text:first-child { color: #172033; font-size: 31rpx; } |
|||
.room-card { margin-top: 16rpx; padding: 22rpx; border-radius: 30rpx; background: linear-gradient(135deg, rgba(255,255,255,.92), rgba(238,247,255,.82)); display: flex; align-items: center; justify-content: space-between; border: 2rpx solid rgba(124,58,237,.10); } |
|||
.room-main { flex: 1; min-width: 0; } |
|||
.room-name { font-size: 29rpx; font-weight: 900; } |
|||
.room-meta { margin-top: 8rpx; color: #667085; font-size: 22rpx; } |
|||
.room-pool { min-width: 108rpx; height: 88rpx; border-radius: 26rpx; background: #FFF3D7; color: #B7791F; display: flex; flex-direction: column; align-items: center; justify-content: center; font-weight: 900; } |
|||
.room-pool text:first-child { font-size: 32rpx; } |
|||
.room-pool text:last-child { font-size: 19rpx; margin-top: 4rpx; } |
|||
.share-mini { margin: 0 0 0 14rpx; padding: 0 18rpx; min-width: 0; height: 56rpx; line-height: 56rpx; border-radius: 999rpx; background: #EEF2FF; color: #7C3AED; font-size: 22rpx; font-weight: 900; } |
|||
.share-mini:after, .mini-share:after { border: 0; } |
|||
.duo { display: flex; gap: 18rpx; background: transparent; box-shadow: none; padding: 0; } |
|||
.panel { flex: 1; min-width: 0; margin-bottom: 0; padding: 22rpx; border-radius: 34rpx; } |
|||
.mini-row { margin-top: 14rpx; padding: 16rpx; border-radius: 22rpx; background: rgba(248,250,252,.9); display: flex; justify-content: space-between; gap: 12rpx; font-size: 22rpx; color: #667085; } |
|||
.mini-row text:first-child { color: #172033; font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
|||
.mini-share { margin: 0; padding: 0 14rpx; min-width: 0; height: 40rpx; line-height: 40rpx; border-radius: 999rpx; background: #F4F3FF; color: #7C3AED; font-size: 18rpx; font-weight: 900; } |
|||
.empty { margin-top: 20rpx; color: #8A94A6; font-size: 24rpx; } |
|||
.empty.small { font-size: 21rpx; } |
|||
.load-more { margin-top: 24rpx; height: 58rpx; line-height: 58rpx; text-align: center; border-radius: 999rpx; background: rgba(238,242,255,.76); color: #7C3AED; font-size: 23rpx; font-weight: 900; } |
|||
.modal { position: fixed; inset: 0; z-index: 99; background: rgba(23,32,51,.22); display: flex; align-items: center; justify-content: center; padding: 46rpx; } |
|||
.modal-card { width: 100%; border-radius: 42rpx; background: #fff; padding: 36rpx; } |
|||
.modal-title { text-align: center; font-size: 36rpx; font-weight: 900; margin-bottom: 22rpx; } |
|||
.field { height: 78rpx; margin-bottom: 16rpx; padding: 0 22rpx; border-radius: 22rpx; background: #F5F7FB; font-size: 25rpx; } |
|||
.modal-actions { display: flex; gap: 16rpx; margin-top: 10rpx; } |
|||
.sub-btn, .main-btn { flex: 1; margin-top: 0; height: 76rpx; line-height: 76rpx; border-radius: 999rpx; text-align: center; font-size: 27rpx; font-weight: 900; } |
|||
.sub-btn { background: #F2F4F7; color: #667085; } |
|||
</style> |
|||
@ -0,0 +1,317 @@ |
|||
<template> |
|||
<view class="rank-page" :style="{'--statusbar': statusBarHeight + 'px'}"> |
|||
<view class="nav" :style="{paddingTop: statusBarHeight + 'px'}"> |
|||
<view class="nav-back" @tap="goBack"><text>‹</text></view> |
|||
<view class="nav-title">星球排行榜</view> |
|||
</view> |
|||
|
|||
<scroll-view scroll-y enhanced :show-scrollbar="false" class="page" :style="{paddingTop: (statusBarHeight + 58) + 'px'}" @scrolltolower="loadMore"> |
|||
<view v-if="loading" class="loading-card">正在扫描财富坐标...</view> |
|||
<block v-else> |
|||
<view class="radar-card"> |
|||
<view class="radar-visual"> |
|||
<view class="radar-sweep"></view> |
|||
<view class="radar-ring ring-a"></view> |
|||
<view class="radar-ring ring-b"></view> |
|||
<view class="radar-dot"></view> |
|||
</view> |
|||
<view class="radar-copy"> |
|||
<view class="radar-kicker">SCHOOL RICH LIST</view> |
|||
<view class="radar-title">全校首富雷达</view> |
|||
<view class="radar-sub">从榜单里选定目标,发起星际追查,抢回今日补给权。</view> |
|||
</view> |
|||
<view class="radar-count"> |
|||
<text>{{home.remainHunt || 0}}</text> |
|||
<text>次追查</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="rank-head"> |
|||
<view> |
|||
<view class="rank-kicker">WEALTH COORDINATES</view> |
|||
<view class="rank-title">财富坐标榜</view> |
|||
</view> |
|||
<view class="rank-page-no">已加载 {{pageNum}} / {{totalPages}} 页</view> |
|||
</view> |
|||
|
|||
<view v-if="pagedList.length" class="rank-list"> |
|||
<view |
|||
class="rank-row" |
|||
v-for="(item, index) in pagedList" |
|||
: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="user-main"> |
|||
<view class="user-name"> |
|||
<text>{{item.stealth ? '隐身侠' : (item.nickname || '神秘同学')}}</text> |
|||
<text v-if="item.stealth" class="stealth-tag">隐身</text> |
|||
<text v-if="item.self" class="me-tag">ME</text> |
|||
</view> |
|||
<view class="user-meta"> |
|||
<text>{{item.ticketCount || 0}} 张星球券</text> |
|||
<text v-if="!item.stealth && item.rankKeepDays > 0">霸榜 {{item.rankKeepDays}} 天</text> |
|||
</view> |
|||
</view> |
|||
<view |
|||
class="hunt-btn" |
|||
:class="{disabled: item.self || item.stealth || item.shielded || (home.remainHunt || 0) <= 0}" |
|||
@tap="onHunt(item)"> |
|||
{{huntText(item)}} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view v-else class="empty-card">暂无上榜居民,攒券登顶成为今日主角</view> |
|||
|
|||
<view v-if="rankList.length" class="load-more">{{loadMoreText}}</view> |
|||
|
|||
<view class="bottom-space"></view> |
|||
</block> |
|||
</scroll-view> |
|||
|
|||
<hunt-modal |
|||
:show="huntModal.show" |
|||
:phase="huntModal.phase" |
|||
:result="huntModal.result" |
|||
:target="huntModal.target" |
|||
@close="closeHunt"> |
|||
</hunt-modal> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import huntModal from '@/package1/components/planet/hunt-modal.vue' |
|||
|
|||
export default { |
|||
components: { |
|||
huntModal |
|||
}, |
|||
data() { |
|||
return { |
|||
statusBarHeight: 20, |
|||
loading: true, |
|||
userId: '', |
|||
regionId: '', |
|||
nickname: '', |
|||
avatar: '', |
|||
college: '', |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
rankRows: [], |
|||
listLoading: false, |
|||
noMore: false, |
|||
defaultAvatar: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/41cfb56caff4419b94b69d0f2303b602.png', |
|||
home: { |
|||
remainHunt: 0, |
|||
rankList: [] |
|||
}, |
|||
huntModal: { |
|||
show: false, |
|||
phase: 'searching', |
|||
result: null, |
|||
target: {} |
|||
}, |
|||
huntTimers: [] |
|||
} |
|||
}, |
|||
computed: { |
|||
rankList() { |
|||
return (this.rankRows || []).map((item, index) => { |
|||
return Object.assign({}, item, { |
|||
_rankKey: String(item.userId || item.rankNo || index), |
|||
_displayRankNo: item.rankNo || (index + 1) |
|||
}) |
|||
}) |
|||
}, |
|||
totalPages() { |
|||
return Math.max(1, Number(this.home.rankTotalPages || 1)) |
|||
}, |
|||
pagedList() { |
|||
return this.rankList |
|||
}, |
|||
loadMoreText() { |
|||
if (this.listLoading) return '正在加载更多坐标...' |
|||
if (this.noMore) return '没有更多财富坐标了' |
|||
return '上滑加载更多坐标' |
|||
} |
|||
}, |
|||
onLoad() { |
|||
const sys = uni.getSystemInfoSync() |
|||
this.statusBarHeight = sys.statusBarHeight || 20 |
|||
this.userId = uni.getStorageSync('id') || '' |
|||
this.nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || '' |
|||
this.avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || '' |
|||
try { |
|||
const area = uni.getStorageSync('area') |
|||
if (area) this.regionId = JSON.parse(area).id || '' |
|||
} catch (e) {} |
|||
this.loadHome(true) |
|||
}, |
|||
onUnload() { |
|||
this.clearHuntTimers() |
|||
}, |
|||
methods: { |
|||
loadHome(reset) { |
|||
if (this.listLoading) return |
|||
if (!this.userId) { |
|||
this.tui.toast('请先登录') |
|||
return |
|||
} |
|||
if (!this.regionId) { |
|||
this.loading = false |
|||
this.tui.toast('未获取到校区信息') |
|||
return |
|||
} |
|||
if (reset) { |
|||
this.pageNum = 1 |
|||
this.noMore = false |
|||
this.rankRows = [] |
|||
} |
|||
if (reset) this.loading = true |
|||
this.listLoading = true |
|||
this.tui.request('/app/planet/home', '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) => { |
|||
this.loading = false |
|||
this.listLoading = false |
|||
if (res.code == 200 && res.result) { |
|||
this.home = res.result |
|||
this.pageNum = Number(res.result.rankPageNumber || this.pageNum) |
|||
if (this.pageNum > this.totalPages) this.pageNum = this.totalPages |
|||
const records = res.result.rankList || [] |
|||
this.rankRows = reset ? records : this.rankRows.concat(records) |
|||
this.noMore = this.pageNum >= this.totalPages || records.length < this.pageSize |
|||
} else if (res.message) { |
|||
this.tui.toast(res.message) |
|||
} |
|||
}).catch(() => { |
|||
this.loading = false |
|||
this.listLoading = false |
|||
}) |
|||
}, |
|||
loadMore() { |
|||
if (this.listLoading || this.noMore) return |
|||
if (this.pageNum >= this.totalPages) { |
|||
this.noMore = true |
|||
return |
|||
} |
|||
this.pageNum += 1 |
|||
this.loadHome(false) |
|||
}, |
|||
huntText(item) { |
|||
if (item.self) return '榜上有我' |
|||
if (item.stealth) return '已隐身' |
|||
if (item.shielded) return '防护中' |
|||
if ((this.home.remainHunt || 0) <= 0) return '次数用完' |
|||
return '发起追查' |
|||
}, |
|||
onHunt(item) { |
|||
if (item.self) { |
|||
this.tui.toast('不能追捕自己') |
|||
return |
|||
} |
|||
if (item.stealth) { |
|||
this.tui.toast('目标正在隐身,暂时无法追查') |
|||
return |
|||
} |
|||
if (item.shielded) { |
|||
this.tui.toast('目标已开启防护罩') |
|||
return |
|||
} |
|||
if ((this.home.remainHunt || 0) <= 0) { |
|||
this.tui.toast('今日追捕次数已用完') |
|||
return |
|||
} |
|||
this.clearHuntTimers() |
|||
this.huntModal.show = true |
|||
this.huntModal.phase = 'searching' |
|||
this.huntModal.result = null |
|||
this.huntModal.target = item |
|||
this.huntTimers.push(setTimeout(() => { this.huntModal.phase = 'locking' }, 900)) |
|||
this.huntTimers.push(setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800)) |
|||
this.tui.request('/app/planet/hunt', 'POST', { |
|||
userId: this.userId, |
|||
regionId: this.regionId, |
|||
toUserId: item.userId |
|||
}, false, false, true).then((res) => { |
|||
this.huntTimers.push(setTimeout(() => { |
|||
if (res.code == 200 && res.result) { |
|||
this.huntModal.result = res.result |
|||
this.huntModal.phase = 'result' |
|||
this.loadHome(true) |
|||
} else { |
|||
this.huntModal.show = false |
|||
this.tui.toast(res.message) |
|||
} |
|||
}, 2600)) |
|||
}).catch(() => { |
|||
this.huntModal.show = false |
|||
}) |
|||
}, |
|||
clearHuntTimers() { |
|||
this.huntTimers.forEach((timer) => clearTimeout(timer)) |
|||
this.huntTimers = [] |
|||
}, |
|||
closeHunt() { |
|||
this.huntModal.show = false |
|||
this.huntModal.result = null |
|||
}, |
|||
goBack() { |
|||
uni.navigateBack({ |
|||
delta: 1, |
|||
fail() { |
|||
uni.navigateTo({ url: '/package1/planet/index' }) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.rank-page { min-height: 100vh; background: linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 46%, #FFF7DD 100%); color: #12342F; } |
|||
.nav { position: fixed; top: 0; left: 0; right: 0; height: 44px; z-index: 20; display: flex; align-items: center; justify-content: center; box-sizing: content-box; background: linear-gradient(180deg, rgba(243,255,244,0.98), rgba(243,255,244,0)); } |
|||
.nav-back { position: absolute; left: 20rpx; bottom: 0; height: 44px; width: 60rpx; display: flex; align-items: center; justify-content: center; } |
|||
.nav-back text { color: #12342F; font-size: 48rpx; font-weight: 300; } |
|||
.nav-title { color: #12342F; font-size: 34rpx; font-weight: 900; align-self: flex-end; padding-bottom: 6rpx; } |
|||
.page { height: 100vh; box-sizing: border-box; padding-left: 22rpx; padding-right: 22rpx; } |
|||
.loading-card, .empty-card { margin-top: 24rpx; height: 220rpx; border-radius: 38rpx; background: rgba(255,255,255,0.76); display: flex; align-items: center; justify-content: center; color: #6B817D; font-size: 26rpx; } |
|||
.radar-card { position: relative; margin-top: 24rpx; min-height: 210rpx; padding: 30rpx 24rpx; border-radius: 46rpx; background: radial-gradient(circle at 18% 50%, rgba(255,122,89,0.16), transparent 34%), linear-gradient(135deg, rgba(255,255,255,0.9), rgba(255,246,219,0.7)); border: 2rpx solid rgba(255,255,255,0.94); box-shadow: 0 24rpx 58rpx rgba(255,122,89,0.12); display: flex; align-items: center; overflow: hidden; } |
|||
.radar-visual { position: relative; width: 138rpx; height: 138rpx; border-radius: 50%; border: 2rpx solid rgba(255,122,89,0.22); background: radial-gradient(circle, rgba(255,122,89,0.13), rgba(255,255,255,0.48)); flex-shrink: 0; overflow: hidden; } |
|||
.radar-ring { position: absolute; border-radius: 50%; border: 1rpx solid rgba(255,122,89,0.22); } |
|||
.ring-a { left: 24rpx; top: 24rpx; width: 88rpx; height: 88rpx; } |
|||
.ring-b { left: 48rpx; top: 48rpx; width: 40rpx; height: 40rpx; } |
|||
.radar-sweep { position: absolute; left: 50%; top: 50%; width: 64rpx; height: 5rpx; border-radius: 999rpx; transform-origin: left center; background: linear-gradient(90deg, rgba(255,122,89,0.76), rgba(255,122,89,0)); animation: sweep 1.8s linear infinite; } |
|||
.radar-dot { position: absolute; right: 30rpx; top: 38rpx; width: 16rpx; height: 16rpx; border-radius: 50%; background: #FF7A59; box-shadow: 0 0 18rpx rgba(255,122,89,0.55); } |
|||
@keyframes sweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } |
|||
.radar-copy { flex: 1; min-width: 0; margin-left: 22rpx; } |
|||
.radar-kicker, .rank-kicker { color: #FF7A59; font-size: 18rpx; font-weight: 900; letter-spacing: 3rpx; } |
|||
.radar-title { margin-top: 6rpx; color: #12342F; font-size: 38rpx; font-weight: 900; } |
|||
.radar-sub { margin-top: 8rpx; color: #7E9691; font-size: 23rpx; line-height: 1.42; font-weight: 600; } |
|||
.radar-count { width: 112rpx; height: 112rpx; border-radius: 34rpx; background: linear-gradient(135deg, #FF7A59, #FFB84D); color: #FFFFFF; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 12rpx 26rpx rgba(255,122,89,0.18); transform: rotate(4deg); flex-shrink: 0; } |
|||
.radar-count text:first-child { font-size: 40rpx; font-weight: 900; line-height: 42rpx; } |
|||
.radar-count text:last-child { font-size: 19rpx; font-weight: 800; margin-top: 4rpx; } |
|||
.rank-head { margin-top: 30rpx; display: flex; align-items: flex-end; justify-content: space-between; } |
|||
.rank-title { margin-top: 6rpx; color: #12342F; font-size: 40rpx; font-weight: 900; } |
|||
.rank-page-no { color: #6B817D; font-size: 23rpx; font-weight: 800; } |
|||
.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 { 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%); } |
|||
.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; } |
|||
.me-tag { margin-left: 10rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: linear-gradient(135deg, #35D6A6, #4FB7FF); color: #FFFFFF; font-size: 18rpx; } |
|||
.stealth-tag { margin-left: 10rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: #EEF2FF; color: #7C3AED; font-size: 18rpx; } |
|||
.user-meta { margin-top: 8rpx; display: flex; flex-wrap: wrap; gap: 10rpx; color: #6B817D; font-size: 22rpx; font-weight: 700; } |
|||
.hunt-btn { width: 132rpx; height: 62rpx; line-height: 62rpx; border-radius: 999rpx; text-align: center; background: linear-gradient(135deg, #FF7A59, #FFB84D); color: #FFFFFF; font-size: 22rpx; font-weight: 900; box-shadow: 0 14rpx 28rpx rgba(255,122,89,0.2); flex-shrink: 0; } |
|||
.hunt-btn.disabled { background: rgba(18,52,47,0.08); color: #9AA9A5; box-shadow: none; } |
|||
.load-more { margin-top: 28rpx; height: 66rpx; line-height: 66rpx; text-align: center; border-radius: 999rpx; background: rgba(255,255,255,0.72); color: #22B889; font-size: 24rpx; font-weight: 900; box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.1); } |
|||
.bottom-space { height: 70rpx; } |
|||
</style> |
|||
Loading…
Reference in new issue