feat(theme): 全面完善暖色系双主题动态适配,优化同城组局品类筛选与试听条防折行规范

这个提交包含在:
wikiboss
2026-09-16 20:20:25 +08:00
父节点 21549b1f13
当前提交 21792f10b3
共有 6 个文件被更改,包括 259 次插入 和 144 次删除

查看文件

@@ -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');
});
});
});