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

567 行
14 KiB
Vue

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

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

<template>
<view class="activity-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
<!-- 顶部模式切换选项卡 (矢量SVG模式图标) -->
<view class="tab-header">
<view
class="tab-item btn-active"
:class="{ active: currentTab === 'ALL' }"
@click="switchTab('ALL')"
>
<text class="tab-text">全部活动</text>
<view class="tab-indicator" v-if="currentTab === 'ALL'"></view>
</view>
<view
class="tab-item btn-active"
:class="{ active: currentTab === 'AA' }"
@click="switchTab('AA')"
>
<image class="tab-svg" :src="ICONS.bolt" mode="aspectFit" />
<text class="tab-text">AA制车队</text>
<view class="tab-indicator" v-if="currentTab === 'AA'"></view>
</view>
<view
class="tab-item btn-active"
:class="{ active: currentTab === 'PROJECT' }"
@click="switchTab('PROJECT')"
>
<image class="tab-svg" :src="ICONS.gameTrophy" mode="aspectFit" />
<text class="tab-text">项目制赛事</text>
<view class="tab-indicator" v-if="currentTab === 'PROJECT'"></view>
</view>
</view>
<!-- 顶部状态小提示 (矢量盾牌图标) -->
<view class="tips-bar">
<image class="tips-svg" :src="ICONS.shield" mode="aspectFit" />
<text class="tips-text">同城组局主打美食陪吃、同城陪唱、下午茶陪喝,凭4位核销码现场签到履约</text>
</view>
<!-- 品类横滑筛选胶囊 -->
<scroll-view scroll-x class="cat-filter-scroll" :show-scrollbar="false">
<view class="cat-filter-list">
<view
v-for="cat in categoryOptions"
:key="cat.key"
class="cat-chip btn-active"
:class="{ active: currentCategory === cat.key }"
@click="selectCategory(cat.key)"
>
<text class="chip-text">{{ cat.label }}</text>
</view>
</view>
</scroll-view>
<!-- 活动列表 -->
<view class="activity-list">
<view
v-for="act in filteredActivities"
:key="act.id"
class="activity-card card-active"
@click="goToDetail(act.id)"
>
<!-- 卡片头部 -->
<view class="card-header">
<view class="game-tag">{{ act.game }}</view>
<view class="mode-tag" :class="act.payType === 'AA' ? 'aa-mode' : 'project-mode'">
{{ act.payType === 'AA' ? 'AA制平摊' : '项目制悬赏' }}
</view>
<view class="status-pill" :class="act.status">
{{ act.status === 'RECRUITING' ? '招募中' : '进行中' }}
</view>
</view>
<!-- 卡片标题 -->
<view class="activity-title">{{ act.title }}</view>
<!-- 详细信息条 (矢量时钟与麦克风) -->
<view class="meta-row">
<view class="meta-item">
<image class="meta-svg" :src="ICONS.clock" mode="aspectFit" />
<text class="meta-val">{{ act.startTime }}</text>
</view>
<view class="meta-item">
<image class="meta-svg" :src="ICONS.mic" mode="aspectFit" />
<text class="meta-val">{{ act.location }}</text>
</view>
</view>
<!-- 进度与头像 -->
<view class="progress-section">
<view class="progress-info">
<text class="progress-label">组队进度</text>
<text class="progress-count tabular-num">{{ act.currentParticipants }} / {{ act.maxParticipants }}人</text>
</view>
<view class="progress-track">
<view
class="progress-fill"
:style="{ width: (act.currentParticipants / act.maxParticipants * 100) + '%' }"
></view>
</view>
</view>
<!-- 底部房主与价格 -->
<view class="card-footer">
<view class="creator-info">
<image class="creator-avatar" :src="act.creatorAvatar" mode="aspectFill" />
<text class="creator-name">房主:{{ act.creatorNickname }}</text>
</view>
<view class="fee-info tabular-num">
<template v-if="act.payType === 'AA'">
<text class="fee-label">人均预估</text>
<text class="fee-amount">¥{{ act.aaFee }}</text>
</template>
<template v-else>
<text class="fee-label">奖金池</text>
<text class="fee-amount project-gold">¥{{ act.totalFee }}</text>
</template>
</view>
</view>
</view>
</view>
<!-- 悬浮发起活动按钮 (脉冲微流光) -->
<view class="fab-btn btn-active" @click="createActivity">
<text class="fab-icon">+</text>
<text class="fab-text">发起组队</text>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { ACTIVITIES_LIST } from '@/common/mock.js';
import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey } from '@/common/theme.js';
const activities = ref(ACTIVITIES_LIST);
const currentTab = ref('ALL');
const currentCategory = ref('ALL');
const currentThemeKey = ref(getCurrentThemeKey());
const onThemeChanged = (key) => {
currentThemeKey.value = key;
};
onMounted(() => {
currentThemeKey.value = getCurrentThemeKey();
uni.$on('theme-change', onThemeChanged);
});
onUnmounted(() => {
uni.$off('theme-change', onThemeChanged);
});
const categoryOptions = [
{ key: 'ALL', label: '全部品类' },
{ key: 'MEAL', label: '美食陪吃' },
{ key: 'SING', label: '同城陪唱' },
{ key: 'DRINK', label: '下午茶陪喝' },
{ key: 'GAME', label: '电竞开黑' },
];
const selectCategory = (key) => {
currentCategory.value = key;
};
const filteredActivities = computed(() => {
let list = activities.value;
if (currentTab.value !== 'ALL') {
list = list.filter(a => a.payType === currentTab.value);
}
if (currentCategory.value !== 'ALL') {
const map = {
MEAL: '美食陪吃',
SING: '同城陪唱',
DRINK: '下午茶陪喝',
GAME: '王者荣耀',
};
const target = map[currentCategory.value];
if (target) {
list = list.filter(a => a.game === target);
}
}
return list;
});
const switchTab = (tab) => {
currentTab.value = tab;
};
const goToDetail = (id) => {
uni.navigateTo({
url: `/pages/activity/detail?id=${id}`
});
};
const createActivity = () => {
uni.navigateTo({
url: '/pages/activity/create'
});
};
</script>
<style lang="scss" scoped>
.activity-container {
min-height: 100vh;
background-color: #0A0F1D;
padding: 20rpx 32rpx calc(180rpx + env(safe-area-inset-bottom));
}
.tab-header {
display: flex;
background-color: #151D30;
border-radius: 20rpx;
padding: 8rpx;
margin-bottom: 24rpx;
border: 1px solid rgba(255, 255, 255, 0.06);
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 18rpx 0;
position: relative;
border-radius: 14rpx;
transition: all 0.2s ease;
.tab-svg {
width: 26rpx;
height: 26rpx;
}
.tab-text {
font-size: 26rpx;
color: #94A3B8;
font-weight: 500;
}
&.active {
background: rgba(99, 102, 241, 0.18);
.tab-text {
color: #FFFFFF;
font-weight: 700;
}
.tab-indicator {
position: absolute;
bottom: 4rpx;
left: 50%;
transform: translateX(-50%);
width: 32rpx;
height: 6rpx;
background: #6366F1;
border-radius: 3rpx;
box-shadow: 0 0 10rpx #6366F1;
}
}
}
}
.tips-bar {
display: flex;
align-items: center;
background: rgba(99, 102, 241, 0.08);
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: 12rpx;
padding: 12rpx 20rpx;
margin-bottom: 28rpx;
.tips-svg {
width: 28rpx;
height: 28rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.tips-text {
font-size: 22rpx;
color: #818CF8;
line-height: 1.4;
}
}
.cat-filter-scroll {
white-space: nowrap;
margin-bottom: 24rpx;
.cat-filter-list {
display: flex;
gap: 16rpx;
.cat-chip {
padding: 10rpx 24rpx;
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
transition: all 0.2s ease;
.chip-text {
font-size: 24rpx;
color: #94A3B8;
font-weight: 500;
}
&.active {
background: rgba(99, 102, 241, 0.2);
border-color: #6366F1;
.chip-text {
color: #FFF;
font-weight: 700;
}
}
}
}
}
.activity-list {
display: flex;
flex-direction: column;
gap: 24rpx;
.activity-card {
background-color: #151D30;
border-radius: 24rpx;
padding: 28rpx;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.3);
.card-header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 16rpx;
.game-tag {
background: rgba(255, 255, 255, 0.08);
color: #E2E8F0;
font-size: 22rpx;
padding: 4rpx 14rpx;
border-radius: 8rpx;
}
.mode-tag {
font-size: 20rpx;
padding: 4rpx 14rpx;
border-radius: 8rpx;
font-weight: 600;
&.aa-mode {
background: rgba(16, 185, 129, 0.15);
color: #10B981;
border: 1px solid rgba(16, 185, 129, 0.3);
}
&.project-mode {
background: rgba(245, 158, 11, 0.15);
color: #F59E0B;
border: 1px solid rgba(245, 158, 11, 0.3);
}
}
.status-pill {
margin-left: auto;
font-size: 20rpx;
padding: 4rpx 14rpx;
border-radius: 12rpx;
&.RECRUITING {
background: rgba(99, 102, 241, 0.2);
color: #818CF8;
}
&.RUNNING {
background: rgba(16, 185, 129, 0.2);
color: #10B981;
}
}
}
.activity-title {
font-size: 30rpx;
font-weight: 700;
color: #F8FAFC;
line-height: 1.4;
margin-bottom: 16rpx;
}
.meta-row {
display: flex;
gap: 28rpx;
margin-bottom: 20rpx;
.meta-item {
display: flex;
align-items: center;
font-size: 22rpx;
color: #94A3B8;
.meta-svg {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
opacity: 0.8;
}
}
}
.progress-section {
margin-bottom: 24rpx;
.progress-info {
display: flex;
justify-content: space-between;
margin-bottom: 10rpx;
font-size: 22rpx;
.progress-label {
color: #64748B;
}
.progress-count {
color: #10B981;
font-weight: 600;
}
}
.progress-track {
height: 10rpx;
background: rgba(255, 255, 255, 0.08);
border-radius: 5rpx;
overflow: hidden;
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #10B981, #059669);
border-radius: 5rpx;
box-shadow: 0 0 10rpx rgba(16, 185, 129, 0.5);
}
}
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid rgba(255, 255, 255, 0.06);
padding-top: 18rpx;
.creator-info {
display: flex;
align-items: center;
.creator-avatar {
width: 48rpx;
height: 48rpx;
border-radius: 24rpx;
margin-right: 12rpx;
}
.creator-name {
font-size: 22rpx;
color: #94A3B8;
}
}
.fee-info {
display: flex;
align-items: baseline;
gap: 8rpx;
.fee-label {
font-size: 20rpx;
color: #64748B;
}
.fee-amount {
font-size: 32rpx;
font-weight: 700;
color: #10B981;
&.project-gold {
color: #F59E0B;
}
}
}
}
}
}
.fab-btn {
position: fixed;
right: 36rpx;
bottom: calc(140rpx + env(safe-area-inset-bottom));
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
border-radius: 44rpx;
padding: 18rpx 34rpx;
display: flex;
align-items: center;
box-shadow: 0 8rpx 28rpx rgba(99, 102, 241, 0.55);
border: 1px solid rgba(255, 255, 255, 0.2);
z-index: 100;
animation: fabPulse 3s infinite ease-in-out;
.fab-icon {
font-size: 32rpx;
color: #FFF;
font-weight: 700;
margin-right: 8rpx;
}
.fab-text {
font-size: 26rpx;
color: #FFF;
font-weight: 600;
}
}
@keyframes fabPulse {
0%, 100% { box-shadow: 0 8rpx 24rpx rgba(99, 102, 241, 0.45); }
50% { box-shadow: 0 8rpx 36rpx rgba(99, 102, 241, 0.75); }
}
.activity-container.theme-warm {
background-color: #140E0A;
.tab-header {
background-color: #221711;
border-color: rgba(249, 115, 22, 0.12);
.tab-item.active {
background: rgba(249, 115, 22, 0.2);
.tab-text {
color: #FFF7ED;
}
.tab-indicator {
background: #F97316;
box-shadow: 0 0 10rpx #F97316;
}
}
}
.tips-bar {
background: rgba(249, 115, 22, 0.1);
border-color: rgba(249, 115, 22, 0.25);
.tips-text {
color: #FB923C;
}
}
.cat-filter-list .cat-chip.active {
background: linear-gradient(135deg, #F97316, #F59E0B);
box-shadow: 0 4rpx 14rpx rgba(249, 115, 22, 0.4);
.chip-text {
color: #FFF7ED;
}
}
.activity-list .activity-card {
background-color: #221711;
border-color: rgba(249, 115, 22, 0.12);
}
.fab-btn {
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
box-shadow: 0 8rpx 28rpx rgba(249, 115, 22, 0.55);
}
}
</style>