Compare commits
No commits in common. "1ee151630dba706f8beada55cd649e990310bd8e" and "06c059ebd82c04e1b8da9ce4ec87148fc15fe240" have entirely different histories.
1ee151630d
...
06c059ebd8
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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' });
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 |
Loading…
Reference in New Issue
Block a user