- 添加了 itemDetail 组件,用于展示商品详细信息 - 在 home 页面中集成了瀑布流列表组件 - 实现了商品列表的加载更多和下拉刷新功能 - 添加了商品详情弹窗组件
		
			
				
	
	
		
			331 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <script setup>
 | ||
| import xPopup from '@/components/x-popup/index.vue'
 | ||
| import {goodStore} from "@/stores/goods/index.js";
 | ||
| import xImage from '@/components/x-image/index.vue'
 | ||
| import DetailPopup from '@/pages/home/components/DetailPopup/index.vue'
 | ||
| import {liveStore} from "~/stores/live/index.js";
 | ||
| import {ref, nextTick, watch, onMounted} from "vue";
 | ||
| 
 | ||
| const {pageRef, itemList, getArtworkList, loading: storeLoading} = goodStore();
 | ||
| const {auctionData} = liveStore()
 | ||
| const showDetail = ref(false)
 | ||
| const localState = ref({
 | ||
|   finished: false,
 | ||
|   refreshing: false,
 | ||
|   showDetail: false,
 | ||
|   showHeight: '',
 | ||
|   isSearchingCurrentItem: false,
 | ||
|   debugInfo: ''  // 添加调试信息字段
 | ||
| })
 | ||
| 
 | ||
| // DOM引用
 | ||
| const listContainerRef = ref(null)
 | ||
| 
 | ||
| const onRefresh = async () => {
 | ||
|   try {
 | ||
|     localState.value.refreshing = true
 | ||
|     localState.value.finished = false
 | ||
|     const { finished } = await getArtworkList(true)
 | ||
|     localState.value.finished = finished
 | ||
|   } finally {
 | ||
|     localState.value.refreshing = false
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| const props = defineProps({
 | ||
|   show: Boolean,
 | ||
|   title: {
 | ||
|     type: String,
 | ||
|     default: ''
 | ||
|   }
 | ||
| })
 | ||
| 
 | ||
| // 设置调试信息的辅助函数
 | ||
| const setDebugInfo = (info) => {
 | ||
|   console.log('调试信息:', info)
 | ||
|   localState.value.debugInfo = info
 | ||
| }
 | ||
| 
 | ||
| // 增强版的滚动到当前拍品函数
 | ||
| const scrollToCurrentItem = async () => {
 | ||
|   if (!auctionData.value?.artwork?.index) {
 | ||
|     setDebugInfo('当前拍品索引不存在')
 | ||
|     return
 | ||
|   }
 | ||
|   
 | ||
|   const targetIndex = auctionData.value.artwork.index
 | ||
|   setDebugInfo(`开始查找目标拍品: LOT${targetIndex}`)
 | ||
|   
 | ||
|   let currentIndex = itemList.value.findIndex(item => targetIndex === item?.index)
 | ||
|   
 | ||
|   if (currentIndex === -1) {
 | ||
|     try {
 | ||
|       localState.value.isSearchingCurrentItem = true
 | ||
|       setDebugInfo(`当前列表中未找到目标拍品,开始加载更多数据...`)
 | ||
|       
 | ||
|       // 尝试搜索特定拍品
 | ||
|       await searchSpecificItem(targetIndex)
 | ||
|       
 | ||
|       // 检查是否找到了
 | ||
|       currentIndex = itemList.value.findIndex(item => targetIndex === item?.index)
 | ||
|       setDebugInfo(`加载数据后,拍品索引位置: ${currentIndex}`)
 | ||
|       
 | ||
|       if (currentIndex > -1) {
 | ||
|         // 等待列表渲染完成
 | ||
|         await new Promise(resolve => setTimeout(resolve, 300))
 | ||
|         await nextTick()
 | ||
|         scrollToElement(currentIndex)
 | ||
|       } else {
 | ||
|         setDebugInfo(`加载后仍未找到目标拍品 LOT${targetIndex}`)
 | ||
|       }
 | ||
|     } finally {
 | ||
|       localState.value.isSearchingCurrentItem = false
 | ||
|     }
 | ||
|   } else {
 | ||
|     setDebugInfo(`在现有列表中找到拍品,位置: ${currentIndex}`)
 | ||
|     // 等待列表渲染完成
 | ||
|     await new Promise(resolve => setTimeout(resolve, 300))
 | ||
|     await nextTick()
 | ||
|     scrollToElement(currentIndex)
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // 搜索特定拍品的优化函数
 | ||
| const searchSpecificItem = async (targetIndex) => {
 | ||
|   try {
 | ||
|     // 保存原始设置
 | ||
|     const originalPage = pageRef.value.page
 | ||
|     const originalPageSize = pageRef.value.pageSize
 | ||
|     
 | ||
|     setDebugInfo(`尝试使用更大页面大小搜索拍品LOT${targetIndex}`)
 | ||
|     
 | ||
|     // 策略1: 直接设置大页面大小加载首页数据
 | ||
|     pageRef.value.page = 1
 | ||
|     pageRef.value.pageSize = 100 // 使用较大的页面大小
 | ||
|     await getArtworkList(true)
 | ||
|     
 | ||
|     // 检查是否找到
 | ||
|     let found = itemList.value.some(item => targetIndex === item?.index)
 | ||
|     setDebugInfo(`策略1结果: ${found ? '找到' : '未找到'}`)
 | ||
|     
 | ||
|     // 策略2: 如果没找到,尝试基于目标索引估算页码
 | ||
|     if (!found) {
 | ||
|       // 估算目标页面 (假设每页20个拍品)
 | ||
|       const estimatedPage = Math.ceil(targetIndex / 10)
 | ||
|       setDebugInfo(`尝试策略2: 估算页码 ${estimatedPage}`)
 | ||
|       
 | ||
|       pageRef.value.page = estimatedPage > 0 ? estimatedPage : 1
 | ||
|       await getArtworkList(true)
 | ||
|       
 | ||
|       found = itemList.value.some(item => targetIndex === item?.index)
 | ||
|       setDebugInfo(`策略2结果: ${found ? '找到' : '未找到'}`)
 | ||
|     }
 | ||
|     
 | ||
|     // 恢复原始设置
 | ||
|     pageRef.value.page = originalPage
 | ||
|     pageRef.value.pageSize = originalPageSize
 | ||
|     
 | ||
|     return found
 | ||
|   } catch (error) {
 | ||
|     console.error('搜索特定拍品时出错:', error)
 | ||
|     setDebugInfo(`搜索出错: ${error.message}`)
 | ||
|     return false
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // 改进滚动到元素的函数
 | ||
| const scrollToElement = (index) => {
 | ||
|   setDebugInfo(`尝试滚动到元素 index=${index}`)
 | ||
|   
 | ||
|   // 获取容器和目标元素
 | ||
|   const allItems = document.querySelectorAll('.item-wrapper')
 | ||
|   
 | ||
|   if (!listContainerRef.value || !allItems.length || index >= allItems.length) {
 | ||
|     setDebugInfo(`无法滚动: 容器或元素不存在 (找到${allItems.length}个元素)`)
 | ||
|     return
 | ||
|   }
 | ||
|   
 | ||
|   const targetElement = allItems[index]
 | ||
|   const container = listContainerRef.value
 | ||
|   
 | ||
|   if (targetElement && container) {
 | ||
|     try {
 | ||
|       setDebugInfo(`找到目标元素和容器,执行滚动`)
 | ||
|       
 | ||
|       // 使用 Element.scrollIntoView 更可靠的滚动方法
 | ||
|       targetElement.scrollIntoView({
 | ||
|         behavior: 'smooth',
 | ||
|         block: 'center'
 | ||
|       })
 | ||
|       
 | ||
|       // 添加高亮效果
 | ||
|       targetElement.classList.add('highlight-item')
 | ||
|       setTimeout(() => {
 | ||
|         targetElement.classList.remove('highlight-item')
 | ||
|       }, 2000)
 | ||
|       
 | ||
|       setDebugInfo(`滚动完成并应用高亮效果`)
 | ||
|     } catch (error) {
 | ||
|       setDebugInfo(`滚动过程出错: ${error.message}`)
 | ||
|       console.error('滚动过程出错:', error)
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| const emit = defineEmits(['update:show'])
 | ||
| const showDetailInfo = ref(null)
 | ||
| const close = () => emit('update:show', false);
 | ||
| const openShow = (item) => {
 | ||
|   showDetailInfo.value=item
 | ||
|   showDetail.value=true
 | ||
| }
 | ||
| 
 | ||
| const loadMore = async () => {
 | ||
|   pageRef.value.page++
 | ||
|   const { finished } = await getArtworkList()
 | ||
|   localState.value.finished = finished
 | ||
| }
 | ||
| 
 | ||
| // 监听拍品变化
 | ||
| watch(() => auctionData.value?.artwork?.index, (newValue, oldValue) => {
 | ||
|   if (newValue && newValue !== oldValue && props.show) {
 | ||
|     setDebugInfo(`检测到拍品索引变化: ${oldValue} -> ${newValue}`)
 | ||
|     nextTick(() => {
 | ||
|       scrollToCurrentItem()
 | ||
|     })
 | ||
|   }
 | ||
| })
 | ||
| 
 | ||
| // 监听弹窗显示状态
 | ||
| watch(() => props.show, (newValue) => {
 | ||
|   if (newValue) {
 | ||
|     setDebugInfo('弹窗显示,准备滚动到当前拍品')
 | ||
|     // 延迟执行,确保DOM已渲染
 | ||
|     setTimeout(() => {
 | ||
|       scrollToCurrentItem()
 | ||
|     }, 500)
 | ||
|   }
 | ||
| })
 | ||
| </script>
 | ||
| 
 | ||
| <template>
 | ||
|   <div>
 | ||
|     <x-popup :show="show" @update:show="close">
 | ||
|       <template #title>
 | ||
|         <div class="text-#000 text-16px">{{ $t('home.tab1')}}</div>
 | ||
|         <div class="text-#939393 text-16px ml-14px">{{ $t('live_room.total') }}{{ pageRef.itemCount }}{{ $t('live_room.lots_num') }}</div>
 | ||
|       </template>
 | ||
|       <div>
 | ||
|         <van-pull-refresh
 | ||
|             v-model="localState.refreshing"
 | ||
|             :success-text="$t('home.refresh_show')"
 | ||
|             :success-duration="700"
 | ||
|             @refresh="onRefresh"
 | ||
|         >
 | ||
|           <template #success>
 | ||
|             <van-icon name="success" /> <span>{{ $t('home.refresh_show') }}</span>
 | ||
|           </template>
 | ||
|           
 | ||
|           <!-- 搜索提示 -->
 | ||
|           <div v-if="localState.isSearchingCurrentItem" class="searching-item-tip">
 | ||
|             {{ $t('live_room.searching_current_lot') || '正在查找当前拍品...' }}
 | ||
|           </div>
 | ||
|           
 | ||
|           <!-- 调试信息区域,可在生产环境中移除 -->
 | ||
|           <div v-if="localState.debugInfo" class="debug-info">
 | ||
|             {{ localState.debugInfo }}
 | ||
|           </div>
 | ||
|           
 | ||
|           <van-list
 | ||
|               v-model:loading="storeLoading"
 | ||
|               :finished="localState.finished"
 | ||
|               :finished-text="$t('home.finished_text')"
 | ||
|               @load="loadMore"
 | ||
|               class="list-container"
 | ||
|               ref="listContainerRef"
 | ||
|           >
 | ||
|             <div
 | ||
|                 v-for="(item) of itemList"
 | ||
|                 :key="item.uuid"
 | ||
|                 class="flex mb-21px item-wrapper"
 | ||
|                 @click="openShow(item)"
 | ||
|                 :class="{'current-item': auctionData?.artwork?.index === item?.index}"
 | ||
|             >
 | ||
|               <div
 | ||
|                   class="mr-10px flex-shrink-0 rounded-4px overflow-hidden cursor-pointer relative"
 | ||
|               >
 | ||
|                 <xImage
 | ||
|                     :preview="false"
 | ||
|                     class="w-80px h-80px"
 | ||
|                     :src="item.artwork?.hdPic"
 | ||
|                     :alt="item?.artworkTitle"
 | ||
|                     loading="lazy"
 | ||
|                 />
 | ||
|                 <div class="w-45px h-17px bg-#2B53AC text-12px line-height-none flex justify-center items-center absolute top-2px left-2px text-#fff">LOT{{item.index}}</div>
 | ||
|                 <div v-show="auctionData?.artwork?.index===item?.index" class="w-80px h-20px bg-#B58047 flex line-height-none justify-center items-center text-#fff text-12px bottom-0 absolute blink">{{ $t('live_room.cast') }}</div>
 | ||
|               </div>
 | ||
|               <div>
 | ||
|                 <div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
 | ||
|                   {{ item.artworkTitle }}
 | ||
|                 </div>
 | ||
|                 <div class="text-14px text-#575757">{{ $t('home.start_price') }}:{{item?.startPriceCurrency}} {{item?.startPrice}}</div>
 | ||
|                 <div class="text-14px text-#B58047" v-if="item.soldPrice">{{ $t('home.close_price') }}:{{item.soldPrice}}</div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </van-list>
 | ||
|         </van-pull-refresh>
 | ||
|       </div>
 | ||
|     </x-popup>
 | ||
|     <DetailPopup v-model:show="showDetail" :detail-info="showDetailInfo"></DetailPopup>
 | ||
|   </div>
 | ||
| </template>
 | ||
| 
 | ||
| <style scoped>
 | ||
| .ellipsis {
 | ||
|   display: -webkit-box;
 | ||
|   -webkit-box-orient: vertical;
 | ||
|   -webkit-line-clamp: 2;
 | ||
|   overflow: hidden;
 | ||
|   text-overflow: ellipsis;
 | ||
| }
 | ||
| .blink {
 | ||
|   animation: fade 1s linear infinite;
 | ||
| }
 | ||
| 
 | ||
| @keyframes fade {
 | ||
|   0%, 100% { opacity: 1; }
 | ||
|   50% { opacity: 0.5; }
 | ||
| }
 | ||
| 
 | ||
| .searching-item-tip {
 | ||
|   text-align: center;
 | ||
|   padding: 10px 0;
 | ||
|   color: #B58047;
 | ||
|   font-size: 14px;
 | ||
| }
 | ||
| 
 | ||
| .highlight-item {
 | ||
|   animation: highlight-pulse 2s ease-in-out;
 | ||
| }
 | ||
| 
 | ||
| @keyframes highlight-pulse {
 | ||
|   0% { box-shadow: 0 0 0 0 rgba(181, 128, 71, 0.7); }
 | ||
|   50% { box-shadow: 0 0 0 10px rgba(181, 128, 71, 0); }
 | ||
|   100% { box-shadow: 0 0 0 0 rgba(181, 128, 71, 0); }
 | ||
| }
 | ||
| 
 | ||
| .current-item {
 | ||
|   background-color: rgba(181, 128, 71, 0.1);
 | ||
|   border-radius: 4px;
 | ||
| }
 | ||
| 
 | ||
| .debug-info {
 | ||
|   padding: 8px;
 | ||
|   background-color: #f5f5f5;
 | ||
|   border: 1px solid #ddd;
 | ||
|   color: #333;
 | ||
|   font-size: 12px;
 | ||
|   margin: 5px 0;
 | ||
|   word-break: break-all;
 | ||
| }
 | ||
| </style> |