feat(theme): 全面完善暖色系双主题动态适配,优化同城组局品类筛选与试听条防折行规范
这个提交包含在:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="create-activity-container">
|
||||
<view class="create-activity-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
|
||||
<!-- 模式选择 Tab (AA制 / 项目制) -->
|
||||
<view class="mode-switch-wrap">
|
||||
<view
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<image class="mode-svg" :src="ICONS.bolt" mode="aspectFit" />
|
||||
<view class="mode-text-box">
|
||||
<text class="mode-title">AA制车队</text>
|
||||
<text class="mode-title">AA制拼桌</text>
|
||||
<text class="mode-desc">报名人均平摊费用</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -20,8 +20,8 @@
|
||||
>
|
||||
<image class="mode-svg" :src="ICONS.trophy" mode="aspectFit" />
|
||||
<view class="mode-text-box">
|
||||
<text class="mode-title">项目制悬赏</text>
|
||||
<text class="mode-desc">房主全额悬赏奖金池</text>
|
||||
<text class="mode-title">房主请客</text>
|
||||
<text class="mode-desc">房主全额买单请客</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -49,7 +49,7 @@
|
||||
<input
|
||||
class="row-input"
|
||||
v-model="form.title"
|
||||
placeholder="如:三里屯火锅探店 / 百星五排极速冲分"
|
||||
placeholder="如:三里屯铜锅涮肉AA探店 / 工体纯K麦霸欢唱局"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
@@ -65,11 +65,11 @@
|
||||
</view>
|
||||
|
||||
<view class="input-row">
|
||||
<text class="row-label">地点/频道</text>
|
||||
<text class="row-label">地点/商圈</text>
|
||||
<input
|
||||
class="row-input"
|
||||
v-model="form.location"
|
||||
placeholder="如:朝阳区聚宝源 / 开黑语音 1 号房间"
|
||||
placeholder="如:北京市朝阳区三里屯聚宝源 / 工体纯KTV 888包厢"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
@@ -80,7 +80,7 @@
|
||||
<view class="stepper-row">
|
||||
<view class="stepper-info">
|
||||
<text class="row-label">招募席位 (人)</text>
|
||||
<text class="sub-label">包含房主在内的车队总人数</text>
|
||||
<text class="sub-label">包含房主在内的组局总人数</text>
|
||||
</view>
|
||||
<view class="stepper-box">
|
||||
<button class="step-btn btn-active" :disabled="form.maxParticipants <= 2" @click="changeParticipants(-1)">-</button>
|
||||
@@ -95,18 +95,18 @@
|
||||
class="row-input"
|
||||
type="digit"
|
||||
v-model="form.aaFee"
|
||||
placeholder="如:30"
|
||||
placeholder="如:50"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="input-row" v-else>
|
||||
<text class="row-label">悬赏总奖金池 (元)</text>
|
||||
<text class="row-label">房主买单预算 (元)</text>
|
||||
<input
|
||||
class="row-input"
|
||||
type="digit"
|
||||
v-model="form.totalFee"
|
||||
placeholder="如:300"
|
||||
placeholder="如:500"
|
||||
placeholder-class="ph"
|
||||
/>
|
||||
</view>
|
||||
@@ -114,7 +114,7 @@
|
||||
<view class="calc-preview">
|
||||
<text class="preview-label">预算测算:</text>
|
||||
<text class="preview-val highlight">
|
||||
{{ form.payType === 'AA' ? `总费用预估约 ¥${calcTotalFee} (人均 ¥${form.aaFee || 0})` : `参与者免费报名,全员争夺 ¥${form.totalFee || 0} 奖金池` }}
|
||||
{{ form.payType === 'AA' ? `总费用预估约 ¥${calcTotalFee} (人均 ¥${form.aaFee || 0})` : `参与者免费报名,房主请客全包约 ¥${form.totalFee || 0}` }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -122,28 +122,43 @@
|
||||
<!-- 底部发起操作栏 (适配安全区) -->
|
||||
<view class="bottom-bar">
|
||||
<button class="btn-submit btn-active" @click="handleSubmit">
|
||||
立即发起组队活动
|
||||
立即发起同城组局
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
import { ACTIVITIES_LIST } from '@/common/mock.js';
|
||||
import { getCurrentThemeKey } from '@/common/theme.js';
|
||||
|
||||
const games = ['王者荣耀', '和平精英', '美食陪吃', '同城陪玩', '下午茶陪喝', '英雄联盟'];
|
||||
const games = ['美食陪吃', '同城陪唱', '下午茶陪喝', '同城微醺', '同城桌游', '热门电竞'];
|
||||
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 form = ref({
|
||||
payType: 'AA',
|
||||
game: '王者荣耀',
|
||||
game: '美食陪吃',
|
||||
title: '',
|
||||
startTime: '今晚 20:30',
|
||||
location: '开黑语音 1 号频道',
|
||||
maxParticipants: 5,
|
||||
aaFee: 25,
|
||||
totalFee: 125,
|
||||
startTime: '周六 18:30',
|
||||
location: '北京市朝阳区三里屯商圈',
|
||||
maxParticipants: 4,
|
||||
aaFee: 50,
|
||||
totalFee: 200,
|
||||
});
|
||||
|
||||
const calcTotalFee = computed(() => {
|
||||
@@ -159,7 +174,7 @@ const changeParticipants = (delta) => {
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!form.value.title.trim()) {
|
||||
form.value.title = `【${form.value.game}】今晚极速开黑车队`;
|
||||
form.value.title = `【${form.value.game}】同城聚会组局`;
|
||||
}
|
||||
|
||||
const newActivity = {
|
||||
@@ -173,12 +188,12 @@ const handleSubmit = () => {
|
||||
maxParticipants: form.value.maxParticipants,
|
||||
startTime: form.value.startTime,
|
||||
location: form.value.location,
|
||||
creatorNickname: '超能电竞玩家',
|
||||
creatorNickname: '同城局长',
|
||||
creatorAvatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=150&h=150&fit=crop',
|
||||
participants: [
|
||||
{
|
||||
id: 'u_888',
|
||||
nickname: '超能电竞玩家',
|
||||
nickname: '同城局长',
|
||||
avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=150&h=150&fit=crop',
|
||||
checkedIn: true
|
||||
}
|
||||
@@ -188,7 +203,7 @@ const handleSubmit = () => {
|
||||
ACTIVITIES_LIST.unshift(newActivity);
|
||||
|
||||
uni.showToast({
|
||||
title: '活动发起成功!车队已就绪',
|
||||
title: '组局发起成功!等待好友加入',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
@@ -421,4 +436,33 @@ const handleSubmit = () => {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.create-activity-container.theme-warm {
|
||||
background-color: #140E0A;
|
||||
|
||||
.mode-switch-wrap .mode-item {
|
||||
background-color: #221711;
|
||||
border-color: rgba(249, 115, 22, 0.12);
|
||||
|
||||
&.active {
|
||||
background: rgba(249, 115, 22, 0.15);
|
||||
border-color: #F97316;
|
||||
}
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background-color: #221711;
|
||||
border-color: rgba(249, 115, 22, 0.12);
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
background-color: rgba(34, 23, 17, 0.95);
|
||||
border-top-color: rgba(249, 115, 22, 0.15);
|
||||
|
||||
.btn-submit {
|
||||
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
|
||||
box-shadow: 0 4rpx 20rpx rgba(249, 115, 22, 0.45);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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制平摊车队' : '项目制悬赏' }}
|
||||
{{ activity.payType === 'AA' ? 'AA制平摊组局' : '房主请客组局' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="act-title">{{ activity.title }}</view>
|
||||
@@ -13,12 +13,12 @@
|
||||
<!-- 费用看板 -->
|
||||
<view class="fee-board">
|
||||
<view class="fee-col">
|
||||
<text class="col-label">总费用</text>
|
||||
<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-label">{{ activity.payType === 'AA' ? '人均分摊' : '房主请客' }}</text>
|
||||
<text class="col-val highlight">
|
||||
¥{{ activity.payType === 'AA' ? activity.aaFee : activity.totalFee }}
|
||||
</text>
|
||||
@@ -31,19 +31,19 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 房主与开黑信息 -->
|
||||
<!-- 房主与阵地信息 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-header">
|
||||
<text class="sec-title">开黑阵地与房主</text>
|
||||
<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>
|
||||
<text class="host-badge">同城局长</text>
|
||||
</view>
|
||||
<text class="creator-sub">前职业选手 · 胜率保障车头</text>
|
||||
<text class="creator-sub">同城组局召集人 · 氛围担当</text>
|
||||
</view>
|
||||
<button class="btn-chat btn-active" @click="chatWithHost">私聊</button>
|
||||
</view>
|
||||
@@ -54,7 +54,7 @@
|
||||
<text class="info-val">{{ activity.startTime }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">语音频道</text>
|
||||
<text class="info-label">活动地点</text>
|
||||
<view class="copy-val" @click="copyLocation">
|
||||
<text class="info-val">{{ activity.location }}</text>
|
||||
<text class="copy-btn">复制</text>
|
||||
@@ -62,16 +62,16 @@
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">核销机制</text>
|
||||
<text class="info-val green-text">入队出示4位动态门票码核销</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>
|
||||
<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">
|
||||
@@ -81,12 +81,12 @@
|
||||
<text class="member-seat">#{{ index + 1 }}号位</text>
|
||||
</view>
|
||||
<view class="checkin-badge" :class="{ checked: p.checkedIn }">
|
||||
{{ p.checkedIn ? '已核销入队' : '已购票待验' }}
|
||||
{{ p.checkedIn ? '已核销到场' : '待现场核销' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-members">
|
||||
<text>暂无其他队员,快抢先上车!</text>
|
||||
<text>暂无其他成员,快抢先加入同城聚会!</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<text class="ticket-text">我的门票</text>
|
||||
</view>
|
||||
<button class="btn-join btn-active" @click="handleJoin">
|
||||
立即上车 (¥{{ activity.payType === 'AA' ? activity.aaFee : 0 }})
|
||||
立即加入 (¥{{ activity.payType === 'AA' ? activity.aaFee : 0 }})
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -124,13 +124,13 @@ const copyLocation = () => {
|
||||
uni.setClipboardData({
|
||||
data: activity.value.location,
|
||||
success: () => {
|
||||
uni.showToast({ title: '语音频道已复制', icon: 'success' });
|
||||
uni.showToast({ title: '活动地点已复制', icon: 'success' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const chatWithHost = () => {
|
||||
uni.showToast({ title: '已建立与房主的加密通道', icon: 'none' });
|
||||
uni.showToast({ title: '已建立与局长的私聊会话', icon: 'none' });
|
||||
};
|
||||
|
||||
const goToMyTicket = () => {
|
||||
@@ -142,8 +142,8 @@ const goToMyTicket = () => {
|
||||
const handleJoin = () => {
|
||||
uni.showModal({
|
||||
title: '确认报名',
|
||||
content: `报名该【${activity.value.payType === 'AA' ? 'AA制' : '项目制'}】活动,需支付分摊款 ¥${activity.value.aaFee}。支付成功后自动生成4位入队核销码。`,
|
||||
confirmText: '微信支付',
|
||||
content: `报名该【${activity.value.payType === 'AA' ? 'AA制' : '房主请客'}】组局,${activity.value.payType === 'AA' ? `需支付分摊款 ¥${activity.value.aaFee}` : '由房主全额买单请客'}。报名后自动生成4位入局核销码。`,
|
||||
confirmText: '确认报名',
|
||||
confirmColor: '#6366F1',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
@click="switchTab('AA')"
|
||||
>
|
||||
<image class="tab-svg" :src="ICONS.bolt" mode="aspectFit" />
|
||||
<text class="tab-text">AA制车队</text>
|
||||
<text class="tab-text">AA制拼桌</text>
|
||||
<view class="tab-indicator" v-if="currentTab === 'AA'"></view>
|
||||
</view>
|
||||
<view
|
||||
@@ -25,32 +25,52 @@
|
||||
@click="switchTab('PROJECT')"
|
||||
>
|
||||
<image class="tab-svg" :src="ICONS.gameTrophy" mode="aspectFit" />
|
||||
<text class="tab-text">项目制赛事</text>
|
||||
<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
|
||||
class="capsule-item btn-active"
|
||||
:class="{ active: currentCategory === 'MEAL' }"
|
||||
@click="switchCategory('MEAL')"
|
||||
>
|
||||
<image class="capsule-svg" :src="ICONS.utensils" mode="aspectFit" />
|
||||
<text class="capsule-text">美食陪吃</text>
|
||||
</view>
|
||||
<view
|
||||
class="capsule-item btn-active"
|
||||
:class="{ active: currentCategory === 'SING' }"
|
||||
@click="switchCategory('SING')"
|
||||
>
|
||||
<image class="capsule-svg" :src="ICONS.micVocal" mode="aspectFit" />
|
||||
<text class="capsule-text">同城陪唱</text>
|
||||
</view>
|
||||
<view
|
||||
class="capsule-item btn-active"
|
||||
:class="{ active: currentCategory === 'DRINK' }"
|
||||
@click="switchCategory('DRINK')"
|
||||
>
|
||||
<image class="capsule-svg" :src="ICONS.coffee" mode="aspectFit" />
|
||||
<text class="capsule-text">下午茶陪喝</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部状态小提示 (矢量盾牌图标) -->
|
||||
<view class="tips-bar">
|
||||
<image class="tips-svg" :src="ICONS.shield" mode="aspectFit" />
|
||||
<text class="tips-text">同城组局主打美食陪吃、同城陪唱、下午茶陪喝,凭4位核销码现场签到履约</text>
|
||||
<text class="tips-text">AA制聚会人均费用透明平摊,项目制为房主全额请客或悬赏组局,凭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
|
||||
@@ -61,9 +81,9 @@
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="game-tag">{{ act.game }}</view>
|
||||
<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制平摊' : '项目制悬赏' }}
|
||||
{{ act.payType === 'AA' ? 'AA制平摊' : '房主请客' }}
|
||||
</view>
|
||||
<view class="status-pill" :class="act.status">
|
||||
{{ act.status === 'RECRUITING' ? '招募中' : '进行中' }}
|
||||
@@ -73,14 +93,14 @@
|
||||
<!-- 卡片标题 -->
|
||||
<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" />
|
||||
<image class="meta-svg" :src="ICONS.mapPin" mode="aspectFit" />
|
||||
<text class="meta-val">{{ act.location }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -88,7 +108,7 @@
|
||||
<!-- 进度与头像 -->
|
||||
<view class="progress-section">
|
||||
<view class="progress-info">
|
||||
<text class="progress-label">组队进度</text>
|
||||
<text class="progress-label">组局进度</text>
|
||||
<text class="progress-count tabular-num">{{ act.currentParticipants }} / {{ act.maxParticipants }}人</text>
|
||||
</view>
|
||||
<view class="progress-track">
|
||||
@@ -103,7 +123,7 @@
|
||||
<view class="card-footer">
|
||||
<view class="creator-info">
|
||||
<image class="creator-avatar" :src="act.creatorAvatar" mode="aspectFill" />
|
||||
<text class="creator-name">房主:{{ act.creatorNickname }}</text>
|
||||
<text class="creator-name">局长:{{ act.creatorNickname }}</text>
|
||||
</view>
|
||||
|
||||
<view class="fee-info tabular-num">
|
||||
@@ -112,18 +132,21 @@
|
||||
<text class="fee-amount">¥{{ act.aaFee }}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="fee-label">奖金池</text>
|
||||
<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>暂无符合条件的组局活动,快来发起吧!</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮发起活动按钮 (脉冲微流光) -->
|
||||
<view class="fab-btn btn-active" @click="createActivity">
|
||||
<text class="fab-icon">+</text>
|
||||
<text class="fab-text">发起组队</text>
|
||||
<text class="fab-text">发起组局</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -152,42 +175,38 @@ 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 getCategoryTagClass = (game) => {
|
||||
if (game === '美食陪吃') return 'tag-meal';
|
||||
if (game === '同城陪唱') return 'tag-sing';
|
||||
if (game === '下午茶陪喝' || game === '同城微醺') return 'tag-drink';
|
||||
return '';
|
||||
};
|
||||
|
||||
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 activities.value.filter(a => {
|
||||
// 支付模式筛选
|
||||
const matchTab = currentTab.value === 'ALL' || a.payType === currentTab.value;
|
||||
// 品类筛选 (美食陪吃 / 同城陪唱 / 下午茶陪喝/微醺)
|
||||
let matchCat = true;
|
||||
if (currentCategory.value === 'MEAL') {
|
||||
matchCat = a.game === '美食陪吃';
|
||||
} else if (currentCategory.value === 'SING') {
|
||||
matchCat = a.game === '同城陪唱';
|
||||
} else if (currentCategory.value === 'DRINK') {
|
||||
matchCat = a.game === '下午茶陪喝' || a.game === '同城微醺';
|
||||
}
|
||||
}
|
||||
return list;
|
||||
return matchTab && matchCat;
|
||||
});
|
||||
});
|
||||
|
||||
const switchTab = (tab) => {
|
||||
currentTab.value = tab;
|
||||
};
|
||||
|
||||
const switchCategory = (cat) => {
|
||||
currentCategory.value = cat;
|
||||
};
|
||||
|
||||
const goToDetail = (id) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/activity/detail?id=${id}`
|
||||
@@ -261,6 +280,47 @@ const createActivity = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.category-capsule-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
.capsule-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
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;
|
||||
}
|
||||
|
||||
.capsule-text {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.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;
|
||||
@@ -283,45 +343,18 @@ const createActivity = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 60rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
@@ -341,6 +374,22 @@ const createActivity = () => {
|
||||
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 {
|
||||
@@ -537,6 +586,14 @@ const createActivity = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.category-capsule-bar .capsule-item.active {
|
||||
background: linear-gradient(135deg, #F97316, #F59E0B);
|
||||
box-shadow: 0 4rpx 14rpx rgba(249, 115, 22, 0.4);
|
||||
.capsule-text {
|
||||
color: #FFF7ED;
|
||||
}
|
||||
}
|
||||
|
||||
.tips-bar {
|
||||
background: rgba(249, 115, 22, 0.1);
|
||||
border-color: rgba(249, 115, 22, 0.25);
|
||||
@@ -545,14 +602,6 @@ const createActivity = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<view class="ticket-header">
|
||||
<view class="game-badge">{{ ticketData.game }}</view>
|
||||
<view class="stamp-badge" :class="{ verified: isCheckedIn }">
|
||||
{{ isCheckedIn ? '已核销入队' : '待核销' }}
|
||||
{{ isCheckedIn ? '已核销到场' : '待现场核销' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
|
||||
<view class="ticket-meta">
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">开黑时间</text>
|
||||
<text class="meta-k">聚会时间</text>
|
||||
<text class="meta-v">{{ ticketData.startTime }}</text>
|
||||
</view>
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">语音频道</text>
|
||||
<text class="meta-k">活动地点</text>
|
||||
<text class="meta-v">{{ ticketData.location }}</text>
|
||||
</view>
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">房主昵称</text>
|
||||
<text class="meta-k">局长昵称</text>
|
||||
<text class="meta-v">{{ ticketData.creatorNickname }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<!-- 核心亮点:4位数字核销码专属卡槽 -->
|
||||
<view class="code-section">
|
||||
<view class="code-tips">进房向房主出示 4 位入队核销码</view>
|
||||
<view class="code-tips">到场向局长出示 4 位组局核销码</view>
|
||||
<view class="code-slots">
|
||||
<view v-for="(char, idx) in verifyCodeDigits" :key="idx" class="digit-box">
|
||||
<text class="digit-text">{{ char }}</text>
|
||||
@@ -64,7 +64,7 @@
|
||||
<!-- 演示操作按钮 -->
|
||||
<view class="action-wrap">
|
||||
<button class="btn-verify-demo btn-active" @click="toggleCheckIn">
|
||||
{{ isCheckedIn ? '重置为待核销状态' : '模拟房主核销此门票' }}
|
||||
{{ isCheckedIn ? '重置为待核销状态' : '模拟局长核销此门票' }}
|
||||
</button>
|
||||
<button class="btn-back btn-active" @click="goBack">返回活动详情</button>
|
||||
</view>
|
||||
|
||||
@@ -625,6 +625,10 @@ const navTo = (url) => {
|
||||
padding: 0 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.playing {
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
|
||||
@@ -71,4 +71,22 @@ describe('UI/UX 规范门禁 (TDD 测试套件: 全面杜绝系统 Emoji)', () =
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('4. 首页卡片试听条单行防折行与文本截断规范校验', () => {
|
||||
it('首页试听条必须配置单行不折行、弹性伸缩与省略号截断', () => {
|
||||
const indexVuePath = path.join(uniPagesDir, 'index', 'index.vue');
|
||||
const content = fs.readFileSync(indexVuePath, 'utf-8');
|
||||
|
||||
// 提取 .voice-bar 样式块
|
||||
const voiceBarMatch = content.match(/\.voice-bar\s*\{([\s\S]*?)\n\s*\.card-footer/);
|
||||
expect(voiceBarMatch).not.toBeNull();
|
||||
const styleBlock = voiceBarMatch![1];
|
||||
|
||||
// 必须防止文字折行破坏胶囊造型
|
||||
expect(styleBlock).toContain('white-space: nowrap');
|
||||
expect(styleBlock).toContain('text-overflow: ellipsis');
|
||||
expect(styleBlock).toContain('overflow: hidden');
|
||||
expect(styleBlock).toContain('flex-shrink: 0');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户