bindbox-mini/components/activity/CabinetPreviewPopup.vue

236 lines
5.6 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)
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 }
const filtered = list.filter(item => Number(item.status) === 1 && !item.has_shipment)
const aggregated = new Map()
filtered.forEach(item => {
const key = String(item.product_id || item.id)
if (aggregated.has(key)) {
aggregated.get(key).count += 1
} else {
aggregated.set(key, {
id: key,
name: (item.product_name || item.name || '').trim() || '未知物品',
image: cleanUrl(item.product_images || item.image),
count: 1
})
}
})
items.value = Array.from(aggregated.values())
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>