Compare commits

..

No commits in common. "1ee151630dba706f8beada55cd649e990310bd8e" and "06c059ebd82c04e1b8da9ce4ec87148fc15fe240" have entirely different histories.

8 changed files with 21 additions and 110 deletions

View File

@ -631,7 +631,7 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL", item.AssetID). if err := r.db.Where("asset_id = ? AND deleted_at IS NULL", item.AssetID).
First(&exhibition).Error; err == nil { First(&exhibition).Error; err == nil {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount) item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now, exhibition.ExpireAt) item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
} }
} }
@ -664,16 +664,9 @@ func calculateHourlyEarnings(likeCount int32) float64 {
// calculateRealtimeEarnings 实时计算展示收益 // calculateRealtimeEarnings 实时计算展示收益
// 公式R1 = R0 × T × [100% + Buff(n)] // 公式R1 = R0 × T × [100% + Buff(n)]
// R0 = 5 水晶/小时T = 上架时长小时Buff(n) 根据点赞数计算 // R0 = 5 水晶/小时T = 上架时长小时Buff(n) 根据点赞数计算
// 注意:使用 min(now, expireAt) 确保过期后收益不再增长 func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
func calculateRealtimeEarnings(likeCount int32, startTime, now, expireAt int64) int64 {
// 计算有效截止时间(展览结束时间 vs 当前时间,取较小值)
endTime := now
if expireAt > 0 && expireAt < now {
endTime = expireAt
}
// 计算上架时长(毫秒转小时) // 计算上架时长(毫秒转小时)
T := (endTime - startTime) / 3600000 T := (now - startTime) / 3600000
if T <= 0 { if T <= 0 {
T = 1 // 最少1小时 T = 1 // 最少1小时
} }
@ -728,7 +721,7 @@ func (r *socialRepositoryImpl) GetMyTodayLikedAssets(userID, starID int64, page,
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now.UnixMilli()). if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now.UnixMilli()).
First(&exhibition).Error; err == nil { First(&exhibition).Error; err == nil {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount) item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now.UnixMilli(), exhibition.ExpireAt) item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now.UnixMilli())
} }
} }
@ -789,7 +782,7 @@ func (r *socialRepositoryImpl) GetMyWeekLikedAssets(userID, starID int64, page,
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, nowMillis). if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, nowMillis).
First(&exhibition).Error; err == nil { First(&exhibition).Error; err == nil {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount) item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, nowMillis, exhibition.ExpireAt) item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, nowMillis)
} }
} }
@ -839,7 +832,7 @@ func (r *socialRepositoryImpl) GetUserLikedAssets(userID, starID int64, page, pa
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now). if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now).
First(&exhibition).Error; err == nil { First(&exhibition).Error; err == nil {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount) item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now, exhibition.ExpireAt) item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
} }
} }

View File

@ -289,7 +289,7 @@ onUnload(() => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 80rpx 40rpx 100rpx; padding: 200rpx 40rpx 100rpx;
box-sizing: border-box; box-sizing: border-box;
} }
@ -389,8 +389,6 @@ onUnload(() => {
display: flex; display: flex;
justify-content: center; justify-content: center;
animation: fadeIn 1s ease-out 0.6s both; animation: fadeIn 1s ease-out 0.6s both;
position: fixed;
bottom: 112rpx;
} }
@keyframes fadeIn { @keyframes fadeIn {

View File

@ -381,9 +381,7 @@ const handleExhibitionCardTap = (item, index) => {
// exhibitionWorks.value[index].earnings = data.earnings; // exhibitionWorks.value[index].earnings = data.earnings;
// } else { // } else {
// //
await loadExhibitedAssets();
await loadLikedAssets(); await loadLikedAssets();
// } // }
uni.showToast({ title: '点赞成功', icon: 'success' }); uni.showToast({ title: '点赞成功', icon: 'success' });

View File

@ -75,7 +75,7 @@ const props = defineProps({
isActive: { type: Boolean, default: true }, // isActive: { type: Boolean, default: true }, //
}) })
const emit = defineEmits(['cardClick', 'scroll']) const emit = defineEmits(['cardClick'])
// ========== ========== // ========== ==========
const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx) const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx)
@ -121,44 +121,18 @@ const iosScrollPaused = ref(true)
const startIOSAutoScroll = () => { const startIOSAutoScroll = () => {
if (!isComponentMounted || !isIOS) return if (!isComponentMounted || !isIOS) return
iosScrollPaused.value = false iosScrollPaused.value = false
// iOS scrollLeft
clearInterval(iosScrollEmitTimer)
iosScrollEmitTimer = setInterval(() => {
if (!isComponentMounted) return
// iOS CSS 0 totalWidth.value
const scrollDist = totalWidth.value
const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
const elapsed = (Date.now() % duration)
const pos = (elapsed / duration) * scrollDist
emit('scroll', pos)
}, 16)
} }
const stopIOSAutoScroll = () => { const stopIOSAutoScroll = () => {
iosScrollPaused.value = true iosScrollPaused.value = true
clearInterval(iosScrollEmitTimer)
iosScrollEmitTimer = null
} }
const pauseIOSAutoScroll = () => { const pauseIOSAutoScroll = () => {
iosScrollPaused.value = true iosScrollPaused.value = true
clearInterval(iosScrollEmitTimer)
iosScrollEmitTimer = null
} }
const resumeIOSAutoScroll = () => { const resumeIOSAutoScroll = () => {
iosScrollPaused.value = false iosScrollPaused.value = false
// iOS
clearInterval(iosScrollEmitTimer)
iosScrollEmitTimer = setInterval(() => {
if (!isComponentMounted) return
const scrollDist = totalWidth.value
const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
const elapsed = (Date.now() % duration)
const pos = (elapsed / duration) * scrollDist
emit('scroll', pos)
}, 16)
} }
let rafId = null let rafId = null
let userInteracting = false let userInteracting = false
@ -167,8 +141,6 @@ let appendTimer = null // 防抖定时器
let autoScrollPos = 0 // let autoScrollPos = 0 //
let momentumTimer = null // iOS/Android let momentumTimer = null // iOS/Android
let scrollUpdateTimer = null // scrollLeft Android let scrollUpdateTimer = null // scrollLeft Android
let iosScrollEmitTimer = null // iOS
let iosCurrentScrollPos = 0 // iOS CSS
const startAutoScroll = () => { const startAutoScroll = () => {
if (!isComponentMounted || isIOS) return if (!isComponentMounted || isIOS) return
@ -297,9 +269,6 @@ const onScroll = (e) => {
if (!isComponentMounted) return if (!isComponentMounted) return
currentScrollLeft = e.detail.scrollLeft currentScrollLeft = e.detail.scrollLeft
//
emit('scroll', currentScrollLeft)
// iOS // iOS
if (isIOS && isManualScrolling) { if (isIOS && isManualScrolling) {
return return

View File

@ -1,14 +1,7 @@
<template> <template>
<view class="square-container"> <view class="square-container">
<!-- 可横向滑动的背景 --> <!-- 固定背景 -->
<view class="bg-wrapper"> <image class="background-fixed" src="/static/square/squearbj.png" mode="aspectFill" />
<image
class="background-fixed"
:style="{ transform: `translateX(${-bgScrollLeft}px)` }"
src="/static/square/squearbj1.png"
mode="aspectFill"
/>
</view>
<!-- 横向瀑布流卡片层内部自带横向滚动 --> <!-- 横向瀑布流卡片层内部自带横向滚动 -->
<WaterfallGrid <WaterfallGrid
@ -20,7 +13,6 @@
:category="activeContentTab" :category="activeContentTab"
:isActive="isActive" :isActive="isActive"
@cardClick="handleCardClick" @cardClick="handleCardClick"
@scroll="handleWaterfallScroll"
class="fall-bg" class="fall-bg"
/> />
@ -113,19 +105,11 @@ const {
// banner(216+360rpx) + tab(16+80rpx) + (8rpx) 680rpx // banner(216+360rpx) + tab(16+80rpx) + (8rpx) 680rpx
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 715)) const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 715))
const bgScrollLeft = ref(0)
// ========== Handlers ========== // ========== Handlers ==========
const handleCardClick = (card) => { const handleCardClick = (card) => {
// WaterfallGrid // WaterfallGrid
} }
const handleWaterfallScroll = (scrollLeft) => {
// 30%
bgScrollLeft.value = scrollLeft * 0.3
}
const handleActivityClick = (item) => { const handleActivityClick = (item) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/support-activity/index?id=${item.id}`, url: `/pages/support-activity/index?id=${item.id}`,
@ -276,19 +260,13 @@ onUnmounted(() => {
margin-bottom: 8rpx; margin-bottom: 8rpx;
} */ } */
.bg-wrapper { .background-fixed {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 110%; height: 110%;
z-index: 0; z-index: 0;
overflow: hidden;
}
.background-fixed {
width: 300%;
height: 100%;
} }
.nav-mask { .nav-mask {

View File

@ -334,36 +334,13 @@ async function handleConfirmContribute() {
} }
// API // API
let successCount = 0
let lastRemainingBalance = null
let lastErrorMessage = ''
for (let i = 0; i < quantity.value; i++) { for (let i = 0; i < quantity.value; i++) {
const res = await contributeItem(item, true, true) await contributeItem(item, false, true)
console.log(`${i+1}次调用返回值:`, res)
if (res && res.success !== false) {
successCount++
lastRemainingBalance = res.remainingBalance
} else {
lastErrorMessage = res?.message || '活动不在进行中,无法购买'
}
}
// 使
if (lastRemainingBalance !== null) {
await updateLocalBalanceFromResult(lastRemainingBalance)
}
//
if (successCount === quantity.value) {
showResultToast('✅', `贡献成功\n+${item.cost * successCount} 贡献值已到账`)
} else if (successCount > 0) {
showResultToast('⚠️', `部分成功 ${successCount}/${quantity.value}`)
} else {
showResultToast('❌', lastErrorMessage || '贡献失败')
} }
} finally { } finally {
processingItems.delete(item.type) processingItems.delete(item.type)
isContributing.value = false isContributing.value = false
// selectedItem
} }
} }
@ -389,13 +366,11 @@ async function contributeItem(item, isRetry = false, silent = false) {
try { try {
// 使 activity-config.js purchaseItem // 使 activity-config.js purchaseItem
const result = await purchaseItem(props.activityId, item.type, 1) const result = await purchaseItem(props.activityId, item.type, 1)
console.log(`[contributeItem] result:`, result, 'isRetry:', isRetry, 'silent:', silent)
// //
if (!result.success) { if (!result.success) {
const msg = result.message || '活动不在进行中,无法购买' if (!silent) showResultToast('', result.message || '活动不在进行中,无法购买')
if (!silent) showResultToast('', msg) return false
return { success: false, message: msg }
} }
// //
@ -418,7 +393,7 @@ async function contributeItem(item, isRetry = false, silent = false) {
} }
// syncPendingActions true // syncPendingActions true
return isRetry ? { contribution: result.totalContribution, remainingBalance: result.remainingBalance, success: true } : { success: true } return isRetry ? { contribution: result.totalContribution, remainingBalance: result.remainingBalance } : true
} catch (error) { } catch (error) {
console.error('贡献失败:', error) console.error('贡献失败:', error)
@ -429,12 +404,12 @@ async function contributeItem(item, isRetry = false, silent = false) {
if (!queued) { if (!queued) {
// 退 // 退
refundLocalBalance(item.cost) refundLocalBalance(item.cost)
return { success: false, message: '网络异常,已加入队列' } return false
} }
return { success: false, message: '网络异常,已加入队列' } showResultToast('', '网络异常,已加入队列')
} }
return { success: false, message: error.message || '贡献失败' } return false
} }
} }

View File

@ -22,11 +22,11 @@
/> />
<!-- 实时贡献列表 --> <!-- 实时贡献列表 -->
<!-- <ContributionList <ContributionList
v-if="activityId && !isLoading" v-if="activityId && !isLoading"
:activity-id="activityId" :activity-id="activityId"
class="contribution-list-wrapper" class="contribution-list-wrapper"
/> --> />
<!-- 舞台区域 --> <!-- 舞台区域 -->
<StageArea <StageArea

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB