- 发起组局页 games 初始改为空,仅从 nav_categories(status=1) 加载;加载失败/为空显示重试提示,禁止提交空品类 - 合规商圈初始改为空,仅从 safety_places(status=正常营业) 加载;无数据时提示手动填写地点 - 活动列表品类导航移除默认 3 个兜底品类 - 117 套件/764 用例全绿
954 行
24 KiB
Vue
954 行
24 KiB
Vue
<template>
|
||
<view class="activity-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }" :style="navStyle">
|
||
<!-- 活动大厅骨架屏 (首屏加载中流光占位,杜绝空白) -->
|
||
<skeleton-screen v-if="isLoading" type="activity" />
|
||
|
||
<!-- 真实内容 (首屏数据加载完成后平滑展示) -->
|
||
<view v-else class="activity-body-wrapper">
|
||
<!-- 顶部综合搜索栏 (支持搜组局标题、美食/KTV品类、商圈地点、局长昵称) -->
|
||
<view class="header-search">
|
||
<view class="search-box">
|
||
<image class="search-icon-img" :src="ICONS.search" mode="aspectFit" />
|
||
<input
|
||
class="search-input"
|
||
v-model="searchKeyword"
|
||
confirm-type="search"
|
||
placeholder="搜索组局、美食火锅、商圈地点、局长..."
|
||
placeholder-class="placeholder-text"
|
||
/>
|
||
<view class="clear-btn btn-active" v-if="searchKeyword" @click="clearSearch">
|
||
<text class="clear-text">x</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 顶部模式切换选项卡 (矢量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 === 'MINE' }"
|
||
@click="switchTab('MINE')"
|
||
>
|
||
<image class="tab-svg" :src="ICONS.gameCrown" mode="aspectFit" />
|
||
<text class="tab-text">我发起的</text>
|
||
<view class="tab-indicator" v-if="currentTab === 'MINE'"></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="category-capsule-bar">
|
||
<view
|
||
class="capsule-item btn-active"
|
||
:class="{ active: currentCategory === 'ALL' }"
|
||
@click="switchCategory('ALL')"
|
||
>
|
||
<text class="capsule-text">全部品类</text>
|
||
</view>
|
||
<view
|
||
v-for="cat in navCapsules"
|
||
:key="cat.code"
|
||
class="capsule-item btn-active"
|
||
:class="{ active: currentCategory === cat.code || currentCategory === cat.catKey }"
|
||
@click="switchCategory(cat.code)"
|
||
>
|
||
<image class="capsule-svg" :src="cat.iconSvg" mode="aspectFit" v-if="cat.iconSvg" />
|
||
<text class="capsule-text">{{ cat.name }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 顶部状态小提示 (矢量盾牌图标) -->
|
||
<view class="tips-bar">
|
||
<image class="tips-svg" :src="ICONS.shield" mode="aspectFit" />
|
||
<text class="tips-text">AA制聚会人均费用透明平摊,项目制为房主全额请客或悬赏组局,凭4位门票码现场核销</text>
|
||
</view>
|
||
|
||
<!-- 活动列表 -->
|
||
<view class="activity-list">
|
||
<view
|
||
v-for="act in filteredActivities"
|
||
:key="act.id"
|
||
class="activity-card card-active"
|
||
:class="{ 'is-cancelled': act.status === 'CANCELLED' }"
|
||
@click="goToDetail(act.id)"
|
||
>
|
||
<!-- 卡片头部 -->
|
||
<view class="card-header">
|
||
<view class="game-tag" :class="getCategoryTagClass(act.game)">{{ 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 || 'RECRUITING'">
|
||
{{ getStatusText(act.status) }}
|
||
</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.mapPin" 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 v-if="filteredActivities.length === 0" class="empty-tip">
|
||
<text v-if="searchKeyword">未找到与“{{ searchKeyword }}”匹配的组局</text>
|
||
<text v-else>暂无符合条件的组局活动,快来发起吧!</text>
|
||
<button class="empty-reset-btn btn-active" v-if="searchKeyword" @click="clearSearch">
|
||
清空搜索条件
|
||
</button>
|
||
</view>
|
||
|
||
<view v-if="isLoadingMore" class="load-more-tip">
|
||
<text>正在加载更多…</text>
|
||
</view>
|
||
<view v-else-if="!hasMore && filteredActivities.length > 0" class="load-more-tip">
|
||
<text>没有更多了</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部自定义导航栏 (发单人端:活动中心 / 我要发单 / 友圈 / 个人中心) -->
|
||
<AppTabBar current="activity" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
|
||
import { onShow, onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||
import AppTabBar from '@/components/CustomTabBar.vue';
|
||
import SkeletonScreen from '@/components/SkeletonScreen.vue';
|
||
import { getActivities, getNavCategories, normalizeUserId } from '@/common/api.js';
|
||
import { ICONS } from '@/common/icons.js';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getNavBarInfo } from '@/common/nav.js';
|
||
|
||
const CURRENT_USER_ID = normalizeUserId();
|
||
|
||
const isLoading = ref(true);
|
||
const isLoadingMore = ref(false);
|
||
const activities = ref([]);
|
||
const currentTab = ref('ALL');
|
||
const currentCategory = ref('ALL');
|
||
const dynamicCategories = ref([]);
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const searchKeyword = ref('');
|
||
const navInfo = ref(getNavBarInfo());
|
||
const page = ref(1);
|
||
const pageSize = 20;
|
||
const hasMore = ref(false);
|
||
let searchTimer = null;
|
||
let requestSeq = 0;
|
||
|
||
const navStyle = computed(() => {
|
||
return {
|
||
'--safe-header-gap': `${navInfo.value.safeHeaderGap}px`,
|
||
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
|
||
'--capsule-height': `${navInfo.value.capsuleHeight}px`
|
||
};
|
||
});
|
||
|
||
const clearSearch = () => {
|
||
searchKeyword.value = '';
|
||
};
|
||
|
||
const loadInitialActivities = async () => {
|
||
try {
|
||
await fetchLiveActivities(true);
|
||
} finally {
|
||
setTimeout(() => {
|
||
isLoading.value = false;
|
||
}, 200);
|
||
}
|
||
};
|
||
|
||
const fetchLiveActivities = async (reset = true) => {
|
||
if (!reset && isLoadingMore.value) return;
|
||
const seq = ++requestSeq;
|
||
const targetPage = reset ? 1 : page.value + 1;
|
||
isLoadingMore.value = true;
|
||
try {
|
||
const categoryMeta = navCapsules.value.find((c) => c.code === currentCategory.value);
|
||
const res = await getActivities({
|
||
page: targetPage,
|
||
pageSize,
|
||
keyword: searchKeyword.value.trim(),
|
||
payType: currentTab.value === 'AA' || currentTab.value === 'PROJECT' ? currentTab.value : '',
|
||
creatorId: currentTab.value === 'MINE' ? CURRENT_USER_ID : 0,
|
||
category: currentCategory.value === 'ALL' ? '' : currentCategory.value,
|
||
categoryName: categoryMeta ? categoryMeta.name : ''
|
||
});
|
||
if (seq !== requestSeq) return;
|
||
const list = Array.isArray(res) ? res : (res && Array.isArray(res.list) ? res.list : []);
|
||
if (reset) {
|
||
activities.value = list;
|
||
hasMore.value = Array.isArray(res) ? false : Boolean(res && res.hasMore);
|
||
} else {
|
||
const seen = new Set(activities.value.map((a) => a.id));
|
||
activities.value = activities.value.concat(list.filter((a) => !seen.has(a.id)));
|
||
hasMore.value = Boolean(res && res.hasMore);
|
||
}
|
||
page.value = targetPage;
|
||
} catch (err) {
|
||
console.warn('[ActivityList] 获取线上数据异常,保持缓存:', err);
|
||
if (seq === requestSeq && reset) {
|
||
activities.value = [];
|
||
hasMore.value = false;
|
||
}
|
||
} finally {
|
||
if (seq === requestSeq) {
|
||
isLoadingMore.value = false;
|
||
}
|
||
}
|
||
};
|
||
|
||
const fetchLiveCategories = async () => {
|
||
try {
|
||
const res = await getNavCategories();
|
||
if (Array.isArray(res) && res.length > 0) {
|
||
dynamicCategories.value = res.filter((c) => c.status === 1 && c.code !== 'more');
|
||
}
|
||
} catch (e) {
|
||
console.warn('[ActivityList] 获取品类导航异常降级:', e);
|
||
}
|
||
};
|
||
|
||
const navCapsules = computed(() => {
|
||
if (dynamicCategories.value && dynamicCategories.value.length > 0) {
|
||
return dynamicCategories.value.map((c) => {
|
||
let icon = ICONS.gameSwords;
|
||
if (c.code === 'meal') icon = ICONS.utensils;
|
||
else if (c.code === 'sing') icon = ICONS.micVocal;
|
||
else if (c.code === 'drink' || c.code === 'bar') icon = ICONS.coffee;
|
||
else if (c.code === 'esports') icon = ICONS.gamepad;
|
||
|
||
return {
|
||
code: c.code.toUpperCase(),
|
||
catKey: c.cat,
|
||
name: c.name,
|
||
iconSvg: icon
|
||
};
|
||
});
|
||
}
|
||
|
||
// 品类完全以后台配置为准,不再使用本地兜底数据
|
||
return [];
|
||
});
|
||
|
||
onLoad((options) => {
|
||
if (options && options.category) {
|
||
currentCategory.value = options.category;
|
||
}
|
||
if (options && options.tab) {
|
||
currentTab.value = options.tab;
|
||
}
|
||
});
|
||
|
||
const onThemeChanged = (key) => {
|
||
currentThemeKey.value = key;
|
||
applyThemeToNativeUI(key);
|
||
};
|
||
|
||
onMounted(() => {
|
||
navInfo.value = getNavBarInfo();
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
uni.$on('theme-change', onThemeChanged);
|
||
loadInitialActivities();
|
||
fetchLiveCategories();
|
||
});
|
||
|
||
onShow(() => {
|
||
navInfo.value = getNavBarInfo();
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
uni.hideTabBar({ animation: false, fail: () => {} });
|
||
const pendingTab = uni.getStorageSync('ACTIVITY_ACTIVE_TAB');
|
||
if (pendingTab) {
|
||
currentTab.value = pendingTab;
|
||
uni.removeStorageSync('ACTIVITY_ACTIVE_TAB');
|
||
}
|
||
fetchLiveActivities();
|
||
fetchLiveCategories();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
uni.$off('theme-change', onThemeChanged);
|
||
if (searchTimer) {
|
||
clearTimeout(searchTimer);
|
||
searchTimer = null;
|
||
}
|
||
});
|
||
|
||
const getCategoryTagClass = (game) => {
|
||
if (game === '美食陪吃') return 'tag-meal';
|
||
if (game === '同城陪唱') return 'tag-sing';
|
||
if (game === '下午茶陪喝' || game === '同城微醺') return 'tag-drink';
|
||
return '';
|
||
};
|
||
|
||
const getStatusText = (status) => {
|
||
if (status === 'CANCELLED') return '已解散';
|
||
if (status === 'COMPLETED') return '已完成';
|
||
if (status === 'FULL') return '已满员';
|
||
if (status === 'IN_PROGRESS' || status === 'RUNNING') return '进行中';
|
||
return '招募中';
|
||
};
|
||
|
||
// 筛选与搜索已交由服务端分页完成,这里直接展示当前页数据
|
||
const filteredActivities = computed(() => activities.value);
|
||
|
||
const switchTab = (tab) => {
|
||
if (currentTab.value === tab) return;
|
||
currentTab.value = tab;
|
||
fetchLiveActivities(true);
|
||
};
|
||
|
||
const switchCategory = (cat) => {
|
||
if (currentCategory.value === cat) return;
|
||
currentCategory.value = cat;
|
||
fetchLiveActivities(true);
|
||
};
|
||
|
||
onReachBottom(() => {
|
||
if (hasMore.value && !isLoadingMore.value) {
|
||
fetchLiveActivities(false);
|
||
}
|
||
});
|
||
|
||
watch(searchKeyword, () => {
|
||
if (searchTimer) clearTimeout(searchTimer);
|
||
searchTimer = setTimeout(() => fetchLiveActivities(true), 350);
|
||
});
|
||
|
||
const goToDetail = (id) => {
|
||
uni.navigateTo({
|
||
url: `/pages/activity/detail?id=${id}`
|
||
});
|
||
};
|
||
|
||
const createActivity = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/activity/create'
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.activity-container {
|
||
min-height: 100vh;
|
||
background-color: #0A0F1D;
|
||
padding: 0 32rpx calc(180rpx + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.header-search {
|
||
padding-top: var(--safe-header-gap, 36rpx);
|
||
padding-bottom: 24rpx;
|
||
background-color: #0A0F1D;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
/* #ifdef MP-WEIXIN */
|
||
padding-right: calc(var(--capsule-right-space, 210rpx));
|
||
/* #endif */
|
||
|
||
.search-box {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #151D30;
|
||
height: 80rpx;
|
||
/* #ifdef MP-WEIXIN */
|
||
height: var(--capsule-height, 64rpx);
|
||
/* #endif */
|
||
border-radius: 40rpx;
|
||
padding: 0 28rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
transition: all 0.2s ease;
|
||
|
||
&:focus-within {
|
||
border-color: #6366F1;
|
||
box-shadow: 0 0 16rpx rgba(99, 102, 241, 0.35);
|
||
}
|
||
|
||
.search-icon-img {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-right: 16rpx;
|
||
opacity: 0.8;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
font-size: 26rpx;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
.placeholder-text {
|
||
color: #64748B;
|
||
}
|
||
|
||
.clear-btn {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-left: 12rpx;
|
||
flex-shrink: 0;
|
||
|
||
.clear-text {
|
||
font-size: 22rpx;
|
||
color: #94A3B8;
|
||
line-height: 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-capsule-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
margin-bottom: 24rpx;
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
-webkit-overflow-scrolling: touch;
|
||
|
||
&::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.capsule-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
white-space: nowrap;
|
||
gap: 8rpx;
|
||
padding: 14rpx 28rpx;
|
||
background: #151D30;
|
||
border-radius: 30rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
transition: all 0.2s ease;
|
||
|
||
.capsule-svg {
|
||
width: 26rpx;
|
||
height: 26rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.capsule-text {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
&.active {
|
||
background: rgba(99, 102, 241, 0.2);
|
||
border-color: #6366F1;
|
||
|
||
.capsule-text {
|
||
color: #FFFFFF;
|
||
font-weight: 700;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
|
||
.activity-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
|
||
.load-more-tip {
|
||
text-align: center;
|
||
padding: 20rpx 0;
|
||
font-size: 24rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
.empty-tip {
|
||
text-align: center;
|
||
padding: 60rpx 0;
|
||
font-size: 26rpx;
|
||
color: #64748B;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.empty-reset-btn {
|
||
font-size: 24rpx;
|
||
color: #6366F1;
|
||
background: rgba(99, 102, 241, 0.12);
|
||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||
padding: 8rpx 28rpx;
|
||
border-radius: 30rpx;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.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);
|
||
transition: all 0.2s ease;
|
||
|
||
&.is-cancelled {
|
||
opacity: 0.65;
|
||
filter: grayscale(0.2);
|
||
}
|
||
|
||
.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;
|
||
|
||
&.tag-meal {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
color: #FBBF24;
|
||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||
}
|
||
&.tag-sing {
|
||
background: rgba(244, 63, 94, 0.15);
|
||
color: #FB7185;
|
||
border: 1px solid rgba(244, 63, 94, 0.3);
|
||
}
|
||
&.tag-drink {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
color: #34D399;
|
||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
&.FULL {
|
||
background: rgba(245, 158, 11, 0.2);
|
||
color: #F59E0B;
|
||
}
|
||
&.IN_PROGRESS,
|
||
&.RUNNING {
|
||
background: rgba(16, 185, 129, 0.2);
|
||
color: #10B981;
|
||
}
|
||
&.COMPLETED {
|
||
background: rgba(148, 163, 184, 0.2);
|
||
color: #94A3B8;
|
||
}
|
||
&.CANCELLED {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
color: #F87171;
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 481px) {
|
||
.activity-container {
|
||
max-width: 450px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
|
||
.activity-container.theme-warm {
|
||
background-color: #FDFBF7;
|
||
|
||
.header-search {
|
||
background-color: #FDFBF7;
|
||
|
||
.search-box {
|
||
background-color: #FFFFFF;
|
||
border-color: #F0ECE4;
|
||
box-shadow: 0 4rpx 16rpx rgba(28, 25, 23, 0.04);
|
||
|
||
&:focus-within {
|
||
border-color: #EA580C;
|
||
box-shadow: 0 0 16rpx rgba(234, 88, 12, 0.2);
|
||
}
|
||
|
||
.search-input {
|
||
color: #1C1917;
|
||
}
|
||
|
||
.placeholder-text {
|
||
color: #78716C;
|
||
}
|
||
|
||
.clear-btn {
|
||
background: #F8F5F0;
|
||
|
||
.clear-text {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.tab-header {
|
||
background-color: #FFFFFF;
|
||
border-color: #F0ECE4;
|
||
box-shadow: 0 4rpx 16rpx rgba(28, 25, 23, 0.04);
|
||
|
||
.tab-item {
|
||
.tab-text {
|
||
color: #78716C;
|
||
}
|
||
&.active {
|
||
background: rgba(234, 88, 12, 0.1);
|
||
|
||
.tab-text {
|
||
color: #EA580C;
|
||
font-weight: 700;
|
||
}
|
||
.tab-indicator {
|
||
background: #EA580C;
|
||
box-shadow: 0 0 10rpx rgba(234, 88, 12, 0.4);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-capsule-bar {
|
||
.capsule-item {
|
||
background: #FFFFFF;
|
||
border: 1px solid #F0ECE4;
|
||
.capsule-text {
|
||
color: #57534E;
|
||
}
|
||
&.active {
|
||
background: linear-gradient(135deg, #F97316, #EA580C);
|
||
box-shadow: 0 4rpx 14rpx rgba(234, 88, 12, 0.35);
|
||
.capsule-text {
|
||
color: #FFFFFF;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.tips-bar {
|
||
background: rgba(234, 88, 12, 0.08);
|
||
border-color: rgba(234, 88, 12, 0.2);
|
||
.tips-text {
|
||
color: #EA580C;
|
||
}
|
||
}
|
||
|
||
.activity-list .activity-card {
|
||
background-color: #FFFFFF;
|
||
border-color: #F0ECE4;
|
||
box-shadow: 0 6rpx 20rpx rgba(28, 25, 23, 0.05);
|
||
|
||
.activity-title {
|
||
color: #1C1917;
|
||
}
|
||
.meta-row .meta-item {
|
||
color: #78716C;
|
||
}
|
||
.card-footer {
|
||
border-top-color: #F5F2EC;
|
||
.creator-name {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-tip {
|
||
color: #78716C;
|
||
.empty-reset-btn {
|
||
color: #EA580C;
|
||
background: rgba(234, 88, 12, 0.1);
|
||
border-color: rgba(234, 88, 12, 0.3);
|
||
}
|
||
}
|
||
}
|
||
</style>
|