文件
shanghaipeiwan/pwuniapp/pages/activity/detail.vue

482 行
12 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<view class="detail-container">
<!-- 头部活动基本信息 -->
<view class="hero-card">
<view class="badge-row">
<text class="game-tag">{{ activity.game }}</text>
<text class="mode-tag" :class="activity.payType === 'AA' ? 'aa-mode' : 'project-mode'">
{{ activity.payType === 'AA' ? 'AA制平摊车队' : '项目制悬赏' }}
</text>
</view>
<view class="act-title">{{ activity.title }}</view>
<!-- 费用看板 -->
<view class="fee-board">
<view class="fee-col">
<text class="col-label">总费用</text>
<text class="col-val">¥{{ activity.totalFee }}</text>
</view>
<view class="fee-divider"></view>
<view class="fee-col">
<text class="col-label">{{ activity.payType === 'AA' ? '人均分摊' : '奖金池' }}</text>
<text class="col-val highlight">
¥{{ activity.payType === 'AA' ? activity.aaFee : activity.totalFee }}
</text>
</view>
<view class="fee-divider"></view>
<view class="fee-col">
<text class="col-label">招募席位</text>
<text class="col-val">{{ activity.currentParticipants }}/{{ activity.maxParticipants }}人</text>
</view>
</view>
</view>
<!-- 房主与开黑信息 -->
<view class="section-card">
<view class="sec-header">
<text class="sec-title">开黑阵地与房主</text>
</view>
<view class="creator-box">
<image class="creator-avatar" :src="activity.creatorAvatar" mode="aspectFill" />
<view class="creator-info">
<view class="creator-name-row">
<text class="creator-name">{{ activity.creatorNickname }}</text>
<text class="host-badge">活动房主</text>
</view>
<text class="creator-sub">前职业选手 · 胜率保障车头</text>
</view>
<button class="btn-chat btn-active" @click="chatWithHost">私聊</button>
</view>
<view class="info-list">
<view class="info-row">
<text class="info-label">开始时间</text>
<text class="info-val">{{ activity.startTime }}</text>
</view>
<view class="info-row">
<text class="info-label">语音频道</text>
<view class="copy-val" @click="copyLocation">
<text class="info-val">{{ activity.location }}</text>
<text class="copy-btn">复制</text>
</view>
</view>
<view class="info-row">
<text class="info-label">核销机制</text>
<text class="info-val green-text">入队出示4位动态门票码核销</text>
</view>
</view>
</view>
<!-- 已报名队友成员列表 -->
<view class="section-card">
<view class="sec-header">
<text class="sec-title">已报名队友 ({{ activity.participants ? activity.participants.length : 0 }})</text>
<text class="sec-sub">满员即锁车</text>
</view>
<view class="member-list" v-if="activity.participants && activity.participants.length > 0">
<view v-for="(p, index) in activity.participants" :key="p.id" class="member-item">
<image class="member-avatar" :src="p.avatar" mode="aspectFill" />
<view class="member-info">
<text class="member-name">{{ p.nickname }}</text>
<text class="member-seat">#{{ index + 1 }}号位</text>
</view>
<view class="checkin-badge" :class="{ checked: p.checkedIn }">
{{ p.checkedIn ? '已核销入队' : '已购票待验' }}
</view>
</view>
</view>
<view v-else class="empty-members">
<text>暂无其他队员,快抢先上车!</text>
</view>
</view>
<!-- 底部固定操作栏 -->
<view class="bottom-bar">
<view class="ticket-link btn-active" @click="goToMyTicket">
<image class="ticket-svg" :src="ICONS.ticket" mode="aspectFit" />
<text class="ticket-text">我的门票</text>
</view>
<button class="btn-join btn-active" @click="handleJoin">
立即上车 (¥{{ activity.payType === 'AA' ? activity.aaFee : 0 }})
</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { ACTIVITIES_LIST } from '@/common/mock.js';
import { ICONS } from '@/common/icons.js';
const activity = ref(ACTIVITIES_LIST[0]);
onLoad((query) => {
if (query && query.id) {
const found = ACTIVITIES_LIST.find(a => a.id === query.id);
if (found) {
activity.value = found;
}
}
});
const copyLocation = () => {
uni.setClipboardData({
data: activity.value.location,
success: () => {
uni.showToast({ title: '语音频道已复制', icon: 'success' });
}
});
};
const chatWithHost = () => {
uni.showToast({ title: '已建立与房主的加密通道', icon: 'none' });
};
const goToMyTicket = () => {
uni.navigateTo({
url: `/pages/activity/my-ticket?id=${activity.value.id}`
});
};
const handleJoin = () => {
uni.showModal({
title: '确认报名',
content: `报名该【${activity.value.payType === 'AA' ? 'AA制' : '项目制'}】活动,需支付分摊款 ¥${activity.value.aaFee}。支付成功后自动生成4位入队核销码。`,
confirmText: '微信支付',
confirmColor: '#6366F1',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '报名成功!已生成门票', icon: 'success' });
setTimeout(() => {
goToMyTicket();
}, 1200);
}
}
});
};
</script>
<style lang="scss" scoped>
.detail-container {
min-height: 100vh;
background-color: #0A0F1D;
padding: 24rpx 32rpx calc(160rpx + env(safe-area-inset-bottom));
}
.hero-card {
background: linear-gradient(135deg, #1E1B4B 0%, #151D30 100%);
border: 1px solid rgba(99, 102, 241, 0.25);
border-radius: 28rpx;
padding: 32rpx;
margin-bottom: 24rpx;
.badge-row {
display: flex;
gap: 12rpx;
margin-bottom: 16rpx;
.game-tag {
background: rgba(255, 255, 255, 0.1);
color: #F8FAFC;
font-size: 22rpx;
padding: 4rpx 14rpx;
border-radius: 8rpx;
}
.mode-tag {
font-size: 22rpx;
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-weight: 600;
&.aa-mode {
background: rgba(16, 185, 129, 0.2);
color: #10B981;
border: 1px solid rgba(16, 185, 129, 0.4);
}
&.project-mode {
background: rgba(245, 158, 11, 0.2);
color: #F59E0B;
border: 1px solid rgba(245, 158, 11, 0.4);
}
}
}
.act-title {
font-size: 34rpx;
font-weight: 700;
color: #FFFFFF;
line-height: 1.4;
margin-bottom: 28rpx;
}
.fee-board {
display: flex;
justify-content: space-around;
align-items: center;
background: rgba(10, 15, 29, 0.6);
border-radius: 18rpx;
padding: 20rpx 0;
.fee-col {
display: flex;
flex-direction: column;
align-items: center;
.col-label {
font-size: 22rpx;
color: #94A3B8;
margin-bottom: 6rpx;
}
.col-val {
font-size: 30rpx;
font-weight: 700;
color: #F8FAFC;
&.highlight {
color: #10B981;
font-size: 34rpx;
}
}
}
.fee-divider {
width: 1px;
height: 40rpx;
background: rgba(255, 255, 255, 0.1);
}
}
}
.section-card {
background-color: #151D30;
border-radius: 24rpx;
padding: 28rpx;
margin-bottom: 24rpx;
border: 1px solid rgba(255, 255, 255, 0.06);
.sec-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.sec-title {
font-size: 28rpx;
font-weight: 700;
color: #F8FAFC;
}
.sec-sub {
font-size: 22rpx;
color: #64748B;
}
}
.creator-box {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.03);
border-radius: 16rpx;
padding: 16rpx;
margin-bottom: 24rpx;
.creator-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
margin-right: 16rpx;
}
.creator-info {
flex: 1;
.creator-name-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 6rpx;
.creator-name {
font-size: 26rpx;
font-weight: 600;
color: #F8FAFC;
}
.host-badge {
background: rgba(99, 102, 241, 0.2);
color: #818CF8;
font-size: 18rpx;
padding: 2rpx 10rpx;
border-radius: 6rpx;
}
}
.creator-sub {
font-size: 20rpx;
color: #94A3B8;
}
}
.btn-chat {
background: rgba(99, 102, 241, 0.15);
border: 1px solid rgba(99, 102, 241, 0.3);
color: #818CF8;
font-size: 22rpx;
padding: 0 24rpx;
height: 52rpx;
line-height: 50rpx;
border-radius: 26rpx;
margin: 0;
}
}
.info-list {
display: flex;
flex-direction: column;
gap: 16rpx;
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 24rpx;
.info-label {
color: #64748B;
}
.info-val {
color: #E2E8F0;
font-weight: 500;
&.green-text {
color: #10B981;
}
}
.copy-val {
display: flex;
align-items: center;
gap: 12rpx;
.copy-btn {
color: #6366F1;
font-size: 22rpx;
text-decoration: underline;
}
}
}
}
.member-list {
display: flex;
flex-direction: column;
gap: 16rpx;
.member-item {
display: flex;
align-items: center;
padding: 12rpx 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
&:last-child {
border-bottom: none;
}
.member-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 32rpx;
margin-right: 16rpx;
}
.member-info {
flex: 1;
.member-name {
font-size: 24rpx;
font-weight: 600;
color: #F8FAFC;
display: block;
}
.member-seat {
font-size: 20rpx;
color: #64748B;
}
}
.checkin-badge {
font-size: 20rpx;
padding: 4rpx 14rpx;
border-radius: 12rpx;
background: rgba(100, 116, 139, 0.2);
color: #94A3B8;
&.checked {
background: rgba(16, 185, 129, 0.15);
color: #10B981;
border: 1px solid rgba(16, 185, 129, 0.3);
}
}
}
}
.empty-members {
text-align: center;
padding: 30rpx 0;
color: #64748B;
font-size: 24rpx;
}
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: calc(120rpx + env(safe-area-inset-bottom));
padding: 0 32rpx env(safe-area-inset-bottom);
box-sizing: border-box;
background-color: rgba(21, 29, 48, 0.95);
backdrop-filter: blur(12px);
border-top: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
align-items: center;
gap: 24rpx;
z-index: 100;
max-width: 450px;
margin: 0 auto;
.ticket-link {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 120rpx;
.ticket-svg {
width: 38rpx;
height: 38rpx;
}
.ticket-text {
font-size: 20rpx;
color: #94A3B8;
margin-top: 4rpx;
}
}
.btn-join {
flex: 1;
background: linear-gradient(135deg, #10B981 0%, #059669 100%);
color: #FFFFFF;
font-size: 28rpx;
font-weight: 700;
border-radius: 44rpx;
height: 80rpx;
line-height: 80rpx;
box-shadow: 0 4rpx 20rpx rgba(16, 185, 129, 0.35);
margin: 0;
font-variant-numeric: tabular-nums;
}
}
</style>