feat:完善应援活动

This commit is contained in:
zheng020 2026-06-22 12:37:59 +08:00
parent 9410fd53e1
commit a6ce0f5865
7 changed files with 121 additions and 45 deletions

View File

@ -1,7 +1,7 @@
# 开发环境配置
# HBuilderX「运行」时自动加载;CLI 用 --mode development
# VITE_API_BASE_URL=http://192.168.110.60:8080
VITE_API_BASE_URL=https://api.topfans.online
VITE_API_BASE_URL=http://192.168.110.60:8080
# VITE_API_BASE_URL=https://api.topfans.online
# WebSocket 地址:如与 API 同源可省略(自动从 VITE_API_BASE_URL 推导 http→ws、https→wss
# 独立部署时直接覆盖例如ws://192.168.110.60:8081
VITE_WS_BASE_URL=ws://192.168.110.60:8080

View File

@ -42,7 +42,7 @@
<!-- 藏品卡片区域 -->
<view class="card-section">
<view class="card-wrapper" :class="{ 'card-wrapper--lenticular': isLenticularAsset }">
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFit">
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFill">
</image>
<view v-if="isLenticularAsset" class="detail-lenticular-slot">
<LenticularCard class="detail-lenticular-card" :layers="lenticularLayers"

View File

@ -96,9 +96,15 @@
mode="aspectFill"
:lazy-load="true"
/>
<view v-else class="cover-fallback">{{
item.title || "活动"
}}</view>
<view class="date-row">
<text class="date-text">{{ formatDateRange(item) }}</text>
<text v-if="!isEnded(item)" class="countdown-text">
距离活动结束还有{{ getDaysLeft(item) }}
</text>
</view>
<!-- <view v-else class="cover-fallback"
>{{ item.title || "活动" }}
</view> -->
<view
v-if="item.status === 'expired' || item.status === 'completed'"
class="cover-ended"
@ -115,14 +121,6 @@
<text class="name-text">{{ getShortName(item) }}</text>
</view>
</view>
<!-- <view class="date-row">
<text class="date-text">{{ formatDateRange(item) }}</text>
<text v-if="!isEnded(item)" class="countdown-text">
距离活动结束还有{{ getDaysLeft(item) }}
</text>
</view> -->
<view class="stat-row">
<view class="stat-block">
<text class="block-value">{{
@ -739,23 +737,37 @@ onShow(() => {
}
.date-row {
display: flex;
flex-direction: column;
gap: 4rpx;
height: 72rpx;
position: absolute;
z-index: 2;
inset: 0;
top: 88rpx;
}
.date-text {
font-size: 18rpx;
color: #fffabd;
text-shadow: -2rpx 2rpx 8rpx rgba(206, 9, 9, 0.84);
font-weight: bold;
text-shadow: -1px 1px 4px rgba(206, 9, 9, 0.84);
font-family: "Baloo Bhai";
font-size: 20rpx;
font-style: normal;
font-weight: 400;
line-height: normal;
position: absolute;
top: 0;
left: 24rpx;
}
.countdown-text {
font-size: 14rpx;
color: #fffabd;
text-shadow: -2rpx 2rpx 8rpx rgba(206, 9, 9, 0.84);
opacity: 0.85;
text-shadow: -1px 1px 4px rgba(206, 9, 9, 0.84);
font-family: "Abhaya Libre ExtraBold";
font-size: 14rpx;
font-style: normal;
font-weight: 800;
line-height: normal;
position: absolute;
right: 24rpx;
bottom: 0;
}
.stat-row {

View File

@ -31,7 +31,7 @@
<text class="item-x">X</text>
<text
class="item-count"
:class="`count-${(record.combo_count > 1 ? record.combo_count : record.quantity).toString().length}-digit`"
:class="getCountSizeClass(record.combo_count > 1 ? record.combo_count : record.quantity)"
>{{
record.combo_count > 1 ? record.combo_count : record.quantity
}}</text
@ -96,6 +96,19 @@ const estimateTextWidth = (text, fontSize) => {
return Math.round(width);
};
/**
* 根据数量值映射数字字号档位
* - >= 999 count-largest最大
* - 520 ~ 998 count-mid次大
* - < 520 count-small最小
*/
const getCountSizeClass = (value) => {
const n = Number(value) || 0;
if (n >= 999) return "count-largest";
if (n >= 520) return "count-mid";
return "count-small";
};
/**
* 根据昵称+赠送道具名动态计算胶囊宽度
* 组成左右内边距(32) + 头像(78) + 头像间距(16) + 文字宽度 + 文字右侧间距(16)
@ -258,7 +271,7 @@ const getItemWidth = (record) => {
width: 96rpx;
height: 96rpx;
display: block;
transform: rotate(11deg);
transform: rotate(10deg);
transform-origin: center center;
}
@ -285,19 +298,15 @@ const getItemWidth = (record) => {
font-style: italic;
}
/* 数字位数不同对应不同尺寸,对照 Figma 设计3位 76rpx / 2位 52rpx / 1位 48rpx */
.count-1-digit {
font-size: 48rpx;
}
.count-2-digit {
font-size: 52rpx;
}
.count-3-digit {
/* 数量档位样式999+ 顶格、520~998 中档、其他常规 */
.count-largest {
font-size: 76rpx;
}
.count-4-digit,
.count-5-digit {
font-size: 64rpx;
.count-mid {
font-size: 52rpx;
}
.count-small {
font-size: 48rpx;
}
@keyframes fadeIn {

View File

@ -38,12 +38,12 @@ watch(
.message-board {
width: 100%;
position: fixed;
bottom: 0;
bottom: 128rpx;
padding: 0 34rpx; /* 对应 Figma 中 left: 17px */
}
.message-board-scroll {
height: 480rpx;
height: 352rpx;
width: 100%;
display: flex;
flex-direction: column;
@ -53,10 +53,10 @@ watch(
/* 留言气泡:对应 Figma 留言板 的圆角矩形 */
.message-bubble {
display: inline-flex;
/* display: inline-flex; */
align-items: baseline;
flex-wrap: wrap;
max-width: 100%;
max-width: max-content;
padding: 6rpx 16rpx; /* 适当内边距,让文字与气泡边距舒适 */
background: rgba(42, 17, 17, 0.3);
border-radius: 16rpx; /* 8px = 16rpx */

View File

@ -22,11 +22,16 @@
/>
</view>
<!-- 骰子表情 (17x17) -->
<!-- 骰子表情 (17x17):点击按顺序循环替换 text -->
<image
class="message-row__emoji"
:class="{ 'is-pressing': pressing }"
src="/static/rank/activity-support-icon/message-row/emoji.png"
mode="aspectFit"
@click.stop="handleEmoji"
@touchstart="pressing = true"
@touchend="pressing = false"
@touchcancel="pressing = false"
/>
<!-- 发送箭头 (21x21) -->
@ -41,15 +46,41 @@
</template>
<script setup>
import { computed } from "vue";
import { computed, ref } from "vue";
/**
* 点击骰子 emoji 时按顺序循环选用的祝福语
* 顺序循环:首次取第一条,再点取下一条,到末尾后回到开头
* 调整 / 增删文案直接改这里即可
*/
const GREETINGS = [
"今天第一天姐妹们大家冲鸭!!!",
"冲冲冲!!!生日应援走起~",
"永远支持你!生日快乐 🎂",
"前排打卡!爱了爱了 ❤️",
"姐妹们冲鸭,今天也要元气满满~",
"生日快乐 🎉 永远守护你!",
"第一第一!!我永远在你身后",
"鸭鸭冲鸭!为爱发电不停歇~",
];
// : 0 ,
let greetingCursor = 0;
function pickNextGreeting() {
if (GREETINGS.length === 0) return "";
const next = GREETINGS[greetingCursor % GREETINGS.length];
greetingCursor += 1;
return next;
}
const props = defineProps({
/**
* 展示的文案:父组件可传入最新一条留言,默认使用 Figma 设计稿文案
* 推荐通过 v-model:text 双向绑定,以便点击 emoji 时组件能通知父组件更新
*/
text: {
type: String,
default: "今天第一天姐妹们大家冲鸭!!!",
default: "",
},
/**
* 头像 URL,未传时回退到默认头像
@ -67,11 +98,14 @@ const props = defineProps({
},
});
const emit = defineEmits(["send", "tap"]);
const emit = defineEmits(["send", "tap", "update:text"]);
const defaultAvatar =
"/static/rank/activity-support-icon/message-row/avatar.png";
// : / true,
const pressing = ref(false);
const displayText = computed(() => {
if (props.text && props.text.trim().length > 0) return props.text;
if (props.placeholder && props.placeholder.trim().length > 0)
@ -83,6 +117,12 @@ function handleTap() {
emit("tap");
}
function handleEmoji() {
// update:text v-model:text ; props.text ,
// v-model ""
emit("update:text", pickNextGreeting());
}
function handleSend() {
// :, /
emit("send", displayText.value);
@ -179,6 +219,13 @@ function handleSend() {
width: 34rpx; /* 17px */
height: 34rpx;
filter: drop-shadow(0 4rpx 8rpx rgba(138, 6, 6, 0.74));
transition: transform 0.12s ease;
}
/* emoji 按下反馈(顺序循环选祝福语的可点击提示) */
.message-row__emoji.is-pressing,
.message-row__emoji:active {
transform: scale(0.85);
}
/* 右侧发送箭头 (可点击) */

View File

@ -106,7 +106,11 @@
</view>
<!-- 留言输入框:对齐底部确认赠送按钮位置 -->
<MessageInput placeholder="留下你的祝福..." @send="handleSendMessage" />
<MessageInput
v-model:text="messageDraft"
placeholder="留下你的祝福..."
@send="handleSendMessage"
/>
<!-- 底部操作栏弹出框 -->
<view v-if="actionBarVisible" class="action-bar-popup">
@ -232,6 +236,10 @@ const messageBoardVisible = ref(false);
const rankingModalVisible = ref(false);
const currentActivityTitle = ref("");
// 稿:MessageInput v-model:text ,
// emoji
const messageDraft = ref("今天第一天姐妹们大家冲鸭!!!");
// mock
const messageList = ref([
{