bindbox-mini/components/activity/CabinetPreviewPopup.vue

229 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view v-if="visible" class="cabinet-overlay" @touchmove.stop.prevent>
<view class="cabinet-mask" @tap="close"></view>
<view class="cabinet-panel" @tap.stop>
<view class="cabinet-header">
<text class="cabinet-title">我的盒柜</text>
<view class="cabinet-actions">
<text class="view-all" @tap="goFullCabinet">查看全部</text>
<text class="cabinet-close" @tap="close">×</text>
</view>
</view>
<view v-if="loading" class="cabinet-loading">
<text class="loading-text">加载中...</text>
</view>
<view v-else-if="items.length === 0" class="cabinet-empty">
<text class="empty-text">暂无物品参与活动获取奖品</text>
</view>
<scroll-view v-else scroll-x class="cabinet-scroll">
<view class="thumb-list">
<view v-for="item in displayItems" :key="item.id" class="thumb-item">
<image class="thumb-img" :src="item.image" mode="aspectFill" />
<text class="thumb-count">x{{ item.count }}</text>
</view>
<view v-if="hasMore" class="thumb-more" @tap="goFullCabinet">
<text>+{{ items.length - maxDisplay }}</text>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { getInventory } from '@/api/appUser'
const props = defineProps({
visible: { type: Boolean, default: false },
activityId: { type: [String, Number], default: '' }
})
const emit = defineEmits(['update:visible'])
const loading = ref(false)
const items = ref([])
const total = ref(0)
const maxDisplay = 8
const displayItems = computed(() => items.value.slice(0, maxDisplay))
const hasMore = computed(() => items.value.length > maxDisplay)
function close() { emit('update:visible', false) }
function goFullCabinet() {
close()
uni.switchTab({ url: '/pages/cabinet/index' })
}
function cleanUrl(u) {
if (!u) return '/static/logo.png'
let s = String(u).trim()
if (s.startsWith('[') && s.endsWith(']')) {
try { const arr = JSON.parse(s); if (Array.isArray(arr) && arr.length > 0) s = arr[0] } catch (e) {}
}
s = s.replace(/[`'"]/g, '').trim()
const m = s.match(/https?:\/\/[^\s]+/)
if (m && m[0]) return m[0]
return s || '/static/logo.png'
}
async function loadItems() {
loading.value = true
try {
const userId = uni.getStorageSync('user_id')
if (!userId) { items.value = []; total.value = 0; return }
const res = await getInventory(userId, 1, 50, { status: 1 })
let list = []
let rawTotal = 0
if (res && Array.isArray(res.list)) { list = res.list; rawTotal = res.total || 0 }
else if (res && Array.isArray(res.data)) { list = res.data; rawTotal = res.total || 0 }
else if (Array.isArray(res)) { list = res; rawTotal = res.length }
// 后端已按 status=1 过滤,这里只需要排除前端正在处理的项
// 后端已经按 status=1 过滤并聚合,直接映射
const displayRes = list.map(item => ({
id: item.product_id,
name: (item.product_name || '未知商品').trim(),
image: cleanUrl(item.product_images || item.image),
count: item.count
}))
items.value = displayRes
total.value = rawTotal
} catch (e) {
console.error('[CabinetPreviewPopup] 加载失败', e)
items.value = []
total.value = 0
} finally {
loading.value = false
}
}
watch(() => props.visible, (v) => { if (v) loadItems() })
</script>
<style lang="scss" scoped>
.cabinet-overlay {
position: fixed;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 9000;
}
.cabinet-mask {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.cabinet-panel {
position: absolute;
left: 24rpx; right: 24rpx;
bottom: calc(env(safe-area-inset-bottom) + 24rpx);
background: rgba(255, 255, 255, 0.95);
border-radius: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
overflow: hidden;
animation: slideUp 0.2s ease-out;
}
.cabinet-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.06);
}
.cabinet-title {
font-size: 28rpx;
font-weight: 700;
color: #333;
}
.cabinet-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
.view-all {
font-size: 24rpx;
color: #FF6B35;
font-weight: 600;
}
.cabinet-close {
font-size: 40rpx;
line-height: 1;
color: #999;
padding: 0 8rpx;
}
.cabinet-loading, .cabinet-empty {
padding: 32rpx 24rpx;
text-align: center;
}
.loading-text, .empty-text {
font-size: 24rpx;
color: #999;
}
.cabinet-scroll {
white-space: nowrap;
padding: 20rpx 24rpx;
}
.thumb-list {
display: inline-flex;
gap: 16rpx;
}
.thumb-item {
position: relative;
flex-shrink: 0;
}
.thumb-img {
width: 100rpx;
height: 100rpx;
border-radius: 12rpx;
background: #f5f5f5;
}
.thumb-count {
position: absolute;
right: 4rpx;
bottom: 4rpx;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 20rpx;
padding: 2rpx 8rpx;
border-radius: 8rpx;
font-weight: 600;
}
.thumb-more {
width: 100rpx;
height: 100rpx;
border-radius: 12rpx;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #666;
font-weight: 600;
flex-shrink: 0;
}
@keyframes slideUp {
from { transform: translateY(20rpx); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
</style>