diff --git a/frontend/pages/support-activity/center.vue b/frontend/pages/support-activity/center.vue index 088eec6..75f26db 100644 --- a/frontend/pages/support-activity/center.vue +++ b/frontend/pages/support-activity/center.vue @@ -8,7 +8,9 @@ :showTaskIcon="false" :showStarActivityIcon="false" /> --> - + + + @@ -224,6 +226,20 @@ const filteredActivities = computed(() => { ); }); +const goBack = () => { + // 获取页面栈 + const pages = getCurrentPages(); + if (pages.length > 1) { + // 有上一页,执行返回 + uni.navigateBack(); + } else { + // 没有上一页,跳转到square页面 + uni.reLaunch({ + url: "/pages/square/square", + }); + } +}; + function isEnded(item) { return item.status === "expired" || item.status === "completed"; } @@ -395,6 +411,24 @@ onShow(() => { } } +.nav-back { + display: flex; + align-items: center; + justify-content: center; + /* background: rgba(255,255,255,0.5); + border-radius: 50%; */ + position: fixed; + top: 88rpx; + left: 32rpx; + z-index: 4; +} + +.nav-back-icon { + font-size: 48rpx; + font-weight: bold; + color: #fff; +} + .status-bar { width: 100%; background: transparent; diff --git a/frontend/pages/support-activity/components/ActionBar.vue b/frontend/pages/support-activity/components/ActionBar.vue index 25ff3a4..74a002f 100644 --- a/frontend/pages/support-activity/components/ActionBar.vue +++ b/frontend/pages/support-activity/components/ActionBar.vue @@ -502,8 +502,8 @@ async function contributeItem(item, isRetry = false, silent = false, qty = 1) { await updateLocalBalanceFromResult(result.remainingBalance); } - // 通知父组件更新进度(使用返回的当前进度) - emit("contribute", item.type, result.currentProgress); + // 通知父组件更新水晶余额(使用返回的剩余余额) + emit("contribute", item.type, result.remainingBalance); // 如果是重试成功或静默模式,不单独弹 toast if (!isRetry && !silent) { diff --git a/frontend/pages/support-activity/components/ContributionList.vue b/frontend/pages/support-activity/components/ContributionList.vue index efd4b2b..32778f2 100644 --- a/frontend/pages/support-activity/components/ContributionList.vue +++ b/frontend/pages/support-activity/components/ContributionList.vue @@ -1,8 +1,7 @@ \ No newline at end of file + diff --git a/frontend/pages/support-activity/components/MessageBoard.vue b/frontend/pages/support-activity/components/MessageBoard.vue new file mode 100644 index 0000000..f041e73 --- /dev/null +++ b/frontend/pages/support-activity/components/MessageBoard.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/frontend/pages/support-activity/components/MessageInput.vue b/frontend/pages/support-activity/components/MessageInput.vue new file mode 100644 index 0000000..475765b --- /dev/null +++ b/frontend/pages/support-activity/components/MessageInput.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/frontend/pages/support-activity/components/TopRanking.vue b/frontend/pages/support-activity/components/TopRanking.vue new file mode 100644 index 0000000..d4ae32f --- /dev/null +++ b/frontend/pages/support-activity/components/TopRanking.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/frontend/pages/support-activity/composables/useContributionPolling.js b/frontend/pages/support-activity/composables/useContributionPolling.js index c2a0094..f0a9895 100644 --- a/frontend/pages/support-activity/composables/useContributionPolling.js +++ b/frontend/pages/support-activity/composables/useContributionPolling.js @@ -5,12 +5,15 @@ import { getActivityContributionsLatestApi } from '@/utils/api.js' * 贡献轮询逻辑 composable * @param {Ref} activityId - 活动ID * @param {boolean} isPageActive - 页面是否可见 + * @param {Object} [options] - 配置项 + * @param {boolean} [options.enableTTL=false] - 是否开启"每条记录 5 秒后自动消失"的开关(默认关闭,记录一直保留直到被新数据挤掉) + * @param {number} [options.ttlMs=5000] - 自动消失的延时(毫秒),仅在 enableTTL=true 时生效 * @returns {Object} records, visible, loading, error, start, stop, reset */ -export function useContributionPolling(activityId, isPageActive) { +export function useContributionPolling(activityId, isPageActive, options = {}) { + const { enableTTL = false, ttlMs = 5000 } = options const MAX_RECORDS = 5 const POLL_INTERVAL = 1000 // 每秒拉取 - const RECORD_TTL = 5000 // 每条记录 5 秒后消失 const records = ref([]) const visible = ref(true) @@ -25,6 +28,9 @@ export function useContributionPolling(activityId, isPageActive) { // 重置记录计时器 function resetRecordTimer(record) { + // TTL 功能未开启时:不做任何定时器与淡出处理,记录会一直保留 + if (!enableTTL) return + // 清除已有定时器 if (recordTimers.has(record.id)) { clearTimeout(recordTimers.get(record.id)) @@ -49,7 +55,7 @@ export function useContributionPolling(activityId, isPageActive) { } else { recordTimers.delete(record.id) } - }, RECORD_TTL) + }, ttlMs) recordTimers.set(record.id, timer) } @@ -58,7 +64,7 @@ export function useContributionPolling(activityId, isPageActive) { if (!activityId.value) return try { - const res = await getActivityContributionsLatestApi(activityId.value, latestTimestamp, latestId, 1) + const res = await getActivityContributionsLatestApi(activityId.value, latestTimestamp, latestId, 3) // 处理 code 不为 200 的情况(静默忽略) if (res.code !== 0) return @@ -66,22 +72,22 @@ export function useContributionPolling(activityId, isPageActive) { const newRecords = res.data?.records || [] if (newRecords.length === 0) return - const newRecord = newRecords[0] - - // 检测到新记录(时间戳更新,或时间戳相同但 ID 更新) - const isNew = newRecord.created_at > latestTimestamp || - (newRecord.created_at === latestTimestamp && newRecord.id > latestId) + // API 返回的 records 按 created_at DESC, id DESC 排序。 + // 只需比对第一条;后面的记录时间戳/ID 必然不更小。 + const firstRecord = newRecords[0] + const isNew = firstRecord.created_at > latestTimestamp || + (firstRecord.created_at === latestTimestamp && firstRecord.id > latestId) if (isNew) { // 重置所有现有记录的计时器(新数据到来,刷新列表) records.value.forEach(resetRecordTimer) - // 新记录插入到列表头部 - records.value = [newRecord, ...records.value].slice(0, MAX_RECORDS) - // 为新记录启动消失计时器 - resetRecordTimer(newRecord) - // 更新时间戳和 ID - latestTimestamp = newRecord.created_at - latestId = newRecord.id + // 一次性插入本轮拉取到的全部新记录(API 已按"新→旧"排序,新的在前) + records.value = [...newRecords, ...records.value].slice(0, MAX_RECORDS) + // 为每条新记录启动消失计时器 + newRecords.forEach(resetRecordTimer) + // 更新游标到最新一条 + latestTimestamp = firstRecord.created_at + latestId = firstRecord.id } } catch (e) { // 网络错误等,静默忽略,继续等待下一次轮询 diff --git a/frontend/pages/support-activity/index.vue b/frontend/pages/support-activity/index.vue index b667c77..9b16f46 100644 --- a/frontend/pages/support-activity/index.vue +++ b/frontend/pages/support-activity/index.vue @@ -6,10 +6,51 @@ backIconColor="#e6e6e6" :showGuideIcon="false" :showTaskIcon="false" :showStarActivityIcon="false" /> --> - + + + + + + + + + + + + + + + + + {{ exhibitionRevenue }} + + + + + + + + + + 排行榜 + + - + - - + /> --> + - + - + + + + @@ -75,6 +123,9 @@ + + + - + - - + + 加载中... - + {{ Math.round(loadingProgress) }}% - + @@ -120,180 +178,294 @@ \ No newline at end of file + +/* 留言板触发按钮(与 action-bar-trigger 对称,位于左侧) */ +.message-trigger { + width: 80rpx; + height: 80rpx; + border-radius: 80rpx; + position: fixed; + bottom: 32rpx; + left: 32rpx; + z-index: 100; + display: flex; + justify-content: center; + align-items: center; + background: rgba(217, 217, 217, 0.4); + box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.25); +} + +.message-trigger .trigger-icon-text { + font-size: 44rpx; +} + +/* 留言板弹出框样式 */ +.message-board-popup { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 998; +} + +.message-board-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); +} + +.message-board-content { + position: absolute; + bottom: 0; + left: 0; + right: 0; + max-height: 80vh; + background-image: url("@/static/rank/activity-support-icon/beijingkuang.png"); + background-size: 105% 130%; + background-position: center; + border-radius: 40rpx 40rpx 0 0; + padding: 32rpx 32rpx 40rpx; + display: flex; + flex-direction: column; + gap: 24rpx; + animation: slideUp 0.3s ease-out; +} + +.message-board-header { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 16rpx; + border-bottom: 1rpx solid rgba(255, 255, 255, 0.3); +} + +.message-board-title { + font-size: 32rpx; + font-weight: bold; + color: #fff; + text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3); +} + +.message-board-close { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 48rpx; + font-weight: bold; + background: rgba(255, 255, 255, 0.15); + border-radius: 50%; +} + +/* + * 留言输入框浮层:对齐底部确认赠送按钮 + * 与 ActionBar 的 .quantity-control 处于同一水平线 + */ +.message-input-floating-bottom { + width: 100%; + padding-top: 16rpx; + border-top: 1rpx solid rgba(255, 255, 255, 0.3); +} + +/* 右上角 TOP3 + 我的排名 悬浮组件 */ +.top-ranking-wrapper { + position: fixed; + top: 96rpx; + right: 24rpx; + z-index: 50; + max-width: 320rpx; +} + +.rank-ph { + position: fixed; + top: 288rpx; + right: 24rpx; + z-index: 50; + display: flex; + flex-direction: column; + align-items: center; +} + +.ph-image { + width: 96rpx; + height: 96rpx; +} + +.ph-text { + color: #fff; + text-shadow: -1px 1px 4px rgba(82, 8, 8, 0.45); + font-family: "Abhaya Libre ExtraBold"; + font-size: 22rpx; + font-style: normal; + font-weight: 600; + line-height: normal; +} + diff --git a/frontend/static/rank/activity-support-icon/message-row/avatar.png b/frontend/static/rank/activity-support-icon/message-row/avatar.png new file mode 100644 index 0000000..f3a425d Binary files /dev/null and b/frontend/static/rank/activity-support-icon/message-row/avatar.png differ diff --git a/frontend/static/rank/activity-support-icon/message-row/emoji.png b/frontend/static/rank/activity-support-icon/message-row/emoji.png new file mode 100644 index 0000000..b545b94 Binary files /dev/null and b/frontend/static/rank/activity-support-icon/message-row/emoji.png differ diff --git a/frontend/static/rank/activity-support-icon/message-row/send.png b/frontend/static/rank/activity-support-icon/message-row/send.png new file mode 100644 index 0000000..ffb9ed6 Binary files /dev/null and b/frontend/static/rank/activity-support-icon/message-row/send.png differ diff --git a/frontend/static/rank/phtb.png b/frontend/static/rank/phtb.png new file mode 100644 index 0000000..1769751 Binary files /dev/null and b/frontend/static/rank/phtb.png differ diff --git a/frontend/static/rank/sjbj.png b/frontend/static/rank/sjbj.png new file mode 100644 index 0000000..847760d Binary files /dev/null and b/frontend/static/rank/sjbj.png differ