You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

427 lines
19 KiB

<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="loadMoreActive">
<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="arena-tabs">
<view class="arena-tab" :class="{active: activeTab === 'hotRooms'}" @tap="switchTab('hotRooms')">热门房间</view>
<view class="arena-tab" :class="{active: activeTab === 'myRooms'}" @tap="switchTab('myRooms')">我的房间</view>
<view class="arena-tab" :class="{active: activeTab === 'myRecords'}" @tap="switchTab('myRecords')">我的战绩</view>
</view>
<view class="section hot-section" v-if="activeTab === 'myRooms'">
<view class="section-head">
<text>我的房间</text>
<text @tap="loadMyRooms(true)">刷新</text>
</view>
<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>
<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="!myRooms.length && !myRoomsLoading">暂无房间,先开一间等同学来挑战</view>
<view class="load-more" v-if="myRooms.length">{{myRoomsLoadText}}</view>
</view>
<view class="section hot-section" v-if="activeTab === 'myRecords'">
<view class="section-head">
<text>我的战绩</text>
<text @tap="loadMyRecords(true)">刷新</text>
</view>
<view class="record-card" v-for="record in myRecords" :key="record._key">
<view class="record-main">
<view class="record-title">{{record._title}}</view>
<view class="record-meta">{{record._meta}}</view>
</view>
<view class="record-badge" :class="record._class">{{record._result}}</view>
</view>
<view class="empty" v-if="!myRecords.length && !myRecordsLoading">暂无战绩,去擂台打一局</view>
<view class="load-more" v-if="myRecords.length">{{myRecordsLoadText}}</view>
</view>
<view class="section hot-section" v-if="activeTab === 'hotRooms'">
<view class="section-head">
<text>热门房间</text>
<text @tap="loadHotRooms(true)">刷新</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 && !hotLoading">还没有公开擂台,先开一间等同学来挑战</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: '',
activeTab: 'hotRooms',
hotRooms: [],
myRooms: [],
myRecords: [],
hotPageNum: 1,
hotPageSize: 10,
hotTotalPages: 1,
hotLoading: false,
myRoomsPageNum: 1,
myRoomsPageSize: 10,
myRoomsTotalPages: 1,
myRoomsLoading: false,
myRecordsPageNum: 1,
myRecordsPageSize: 10,
myRecordsTotalPages: 1,
myRecordsLoading: false,
showCreate: false,
shareRoom: null,
form: {
roomName: '今晚冲榜局',
maxPlayers: '',
tickets: '',
password: ''
}
}
},
computed: {
hotLoadText() {
if (this.hotLoading) return '正在加载更多房间...'
if (this.hotPageNum >= this.hotTotalPages) return '没有更多擂台了'
return '上滑加载更多擂台'
},
myRoomsLoadText() {
if (this.myRoomsLoading) return '正在加载我的房间...'
if (this.myRoomsPageNum >= this.myRoomsTotalPages) return '没有更多房间了'
return '上滑加载更多房间'
},
myRecordsLoadText() {
if (this.myRecordsLoading) return '正在加载我的战绩...'
if (this.myRecordsPageNum >= this.myRecordsTotalPages) 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.loadMyRooms(true)
this.loadMyRecords(true)
this.loadHotRooms(true)
}
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
},
switchTab(tab) {
this.activeTab = tab
if (tab === 'myRooms' && !this.myRooms.length) this.loadMyRooms(true)
if (tab === 'myRecords' && !this.myRecords.length) this.loadMyRecords(true)
if (tab === 'hotRooms' && !this.hotRooms.length) this.loadHotRooms(true)
},
loadPageList(type, reset) {
const config = {
myRooms: {
url: '/app/planet/adventure/pk/myRooms',
listKey: 'myRooms',
pageKey: 'myRoomsPageNum',
sizeKey: 'myRoomsPageSize',
totalKey: 'myRoomsTotalPages',
loadingKey: 'myRoomsLoading',
prefix: 'mine'
},
myRecords: {
url: '/app/planet/adventure/pk/myRecords',
listKey: 'myRecords',
pageKey: 'myRecordsPageNum',
sizeKey: 'myRecordsPageSize',
totalKey: 'myRecordsTotalPages',
loadingKey: 'myRecordsLoading',
prefix: 'record'
},
hotRooms: {
url: '/app/planet/adventure/pk/hotRooms',
listKey: 'hotRooms',
pageKey: 'hotPageNum',
sizeKey: 'hotPageSize',
totalKey: 'hotTotalPages',
loadingKey: 'hotLoading',
prefix: 'hot'
}
}[type]
if (!config || this[config.loadingKey]) return
if (!reset && this[config.pageKey] >= this[config.totalKey]) return
const nextPage = reset ? 1 : this[config.pageKey] + 1
if (reset) {
this[config.pageKey] = 1
this[config.totalKey] = 1
this[config.listKey] = []
}
this[config.loadingKey] = true
this.tui.request(config.url, 'POST', {
userId: this.userId,
regionId: this.regionId,
pageNumber: nextPage,
pageSize: this[config.sizeKey]
}, false, false, true).then(res => {
this[config.loadingKey] = false
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[config.pageKey] = Number(res.result.current || nextPage)
this[config.totalKey] = Math.max(1, Number(res.result.pages || 1))
}
}).catch(() => {
this[config.loadingKey] = false
})
},
loadMyRooms(reset) {
this.loadPageList('myRooms', reset)
},
loadMyRecords(reset) {
this.loadPageList('myRecords', reset)
},
loadHotRooms(reset) {
this.loadPageList('hotRooms', reset)
},
loadMoreActive() {
if (this.activeTab === 'myRooms') this.loadMyRooms(false)
else if (this.activeTab === 'myRecords') this.loadMyRecords(false)
else this.loadHotRooms(false)
},
withKeys(list, prefix) {
return list.map((item, index) => {
item.password = ''
item._key = item.id || (prefix + '_' + index)
item._statusText = this.roomStatus(item.status)
if (prefix.indexOf('record') === 0) {
item._title = this.recordTitle(item)
item._meta = this.recordMeta(item)
item._result = this.recordResult(item)
item._class = this.recordClass(item)
}
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.activeTab = 'myRooms'
this.loadMyRooms(true)
this.loadHotRooms(true)
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 '等待中'
},
recordTitle(record) {
if (record.cleared === 1) return (record.durationSeconds || 0) + '秒通关'
return '挑战失败'
},
recordMeta(record) {
const ticketText = '入场 ' + (record.entryTickets || 0) + ' 券'
if (record.rewardTickets > 0) return ticketText + ' · 奖励 +' + record.rewardTickets
return ticketText + ' · ' + (record.finishTime || record.createTime || '')
},
recordResult(record) {
if (record.status === 1 || record.status === 0) return '待结算'
if (record.rewardTickets > 0) return '胜利'
if (record.status === 2 && record.cleared === 1 && record.rankNo === 1) return '胜利'
if ((record.status === 2 || record.status === 3 || record.status === 4) && record.rewardTickets === record.entryTickets && (!record.rankNo || record.rankNo === 0)) return '无人通关'
return '失败'
},
recordClass(record) {
const result = this.recordResult(record)
if (result === '胜利') return 'win'
if (result === '无人通关') return 'refund'
if (result === '待结算') return 'pending'
return 'lose'
},
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); }
.arena-tabs { margin-bottom: 24rpx; padding: 8rpx; border-radius: 999rpx; background: rgba(255,255,255,.72); border: 2rpx solid rgba(255,255,255,.9); display: flex; gap: 8rpx; box-shadow: 0 14rpx 34rpx rgba(124,58,237,.08); }
.arena-tab { flex: 1; height: 66rpx; line-height: 66rpx; border-radius: 999rpx; text-align: center; color: #667085; font-size: 24rpx; font-weight: 900; }
.arena-tab.active { color: #fff; background: linear-gradient(135deg, #7C3AED, #38BDF8); box-shadow: 0 12rpx 26rpx 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; }
.record-card { margin-top: 16rpx; padding: 24rpx; border-radius: 30rpx; background: linear-gradient(135deg, rgba(255,255,255,.94), rgba(247,250,255,.84)); display: flex; align-items: center; justify-content: space-between; border: 2rpx solid rgba(124,58,237,.10); }
.record-main { flex: 1; min-width: 0; }
.record-title { color: #172033; font-size: 29rpx; font-weight: 900; }
.record-meta { margin-top: 8rpx; color: #667085; font-size: 22rpx; }
.record-badge { margin-left: 18rpx; min-width: 110rpx; height: 58rpx; line-height: 58rpx; border-radius: 999rpx; text-align: center; font-size: 23rpx; font-weight: 900; background: #EEF2FF; color: #7C3AED; }
.record-badge.win { background: linear-gradient(135deg, #FFB84D, #FF7A59); color: #fff; box-shadow: 0 12rpx 24rpx rgba(255,122,89,.18); }
.record-badge.lose { background: #FEE2E2; color: #EF4444; }
.record-badge.refund { background: #E0F2FE; color: #0284C7; }
.record-badge.pending { background: #F4F3FF; color: #7C3AED; }
.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>