Files
vpUni/.svn/pristine/16/160efaa5edfc9f00de1c2b32b7df9f7c6a4bdc40.svn-base
2026-03-09 16:39:03 +08:00

216 lines
7.6 KiB
Plaintext

<template>
<view class="points-mall-page">
<!-- 全新方案:纯 CSS 手动构建的骨架屏 -->
<view v-if="loading" class="skeleton-wrapper">
<view class="skeleton-item skeleton-rect" style="height: 300rpx; margin: 24rpx; border-radius: 16rpx;"></view>
<view class="skeleton-content">
<view class="skeleton-left-panel">
<view v-for="i in 6" :key="i" class="skeleton-item skeleton-text" style="width: 80%; height: 60rpx; margin-bottom: 50rpx;"></view>
</view>
<view class="skeleton-right-panel">
<view v-for="i in 4" :key="i" class="skeleton-card">
<view class="skeleton-item skeleton-rect" style="width: 160rpx; height: 160rpx;"></view>
<view style="flex:1; margin-left: 24rpx;">
<view class="skeleton-item skeleton-text" style="width: 90%; height: 30rpx;"></view>
<view class="skeleton-item skeleton-text" style="width: 60%; height: 32rpx; margin-top: 40rpx;"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 页面实际内容 -->
<view v-else class="page-content">
<!-- 1. 顶部轮播图 (原生 <swiper> 实现) -->
<view class="swiper-section">
<swiper class="swiper-container" circular autoplay :interval="3000" :duration="500" @change="e => swiperCurrent = e.detail.current">
<swiper-item v-for="(item, index) in swiperList" :key="index">
<view class="swiper-item">
<view class="image-placeholder swiper-image">
<image :src="item" style="width: 100%; height: 100%;" mode=""></image>
</view>
</view>
</swiper-item>
</swiper>
<!-- 自定义指示点 -->
<view class="swiper-dots">
<view class="dot" :class="{ active: index === swiperCurrent }" v-for="(item, index) in swiperList" :key="index"></view>
</view>
</view>
<!-- 2. 主体内容:左右联动 -->
<view class="main-content">
<!-- 左侧分类栏 -->
<scroll-view class="left-panel" scroll-y>
<view
class="category-item"
:class="{ active: currentCategory === index }"
v-for="(cat, index) in categories"
:key="cat.id"
@click="selectCategory(index)"
>
<text class="name">{{ cat.name }}</text>
</view>
</scroll-view>
<!-- 右侧商品列表 -->
<scroll-view class="right-panel" scroll-y>
<view class="product-list">
<view class="product-card" v-for="product in products" :key="product.id" @click="Service.GoPage('/pages/goods/integralGoods')">
<view class="image-placeholder product-image">
<image :src="product.img" style="width: 100%; height: 100%; border-radius: 20rpx;" mode=""></image>
</view>
<view class="product-info">
<text class="name">{{ product.name }}</text>
<view class="price-line">
<text class="points">{{ product.points }} 积分</text>
<text class="original-price" v-if="product.originalPrice">+ ¥{{ product.originalPrice }}</text>
</view>
</view>
</view>
</view>
<up-loadmore status="nomore" nomoreText="没有更多商品了"></up-loadmore>
</scroll-view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { Service } from "@/Service/Service";
const loading = ref<boolean>(true);
const currentCategory = ref(0);
const swiperCurrent = ref(0);
// 轮播图数据
const swiperList = reactive([
'/static/dele/dele1.jpg',
'/static/dele/dele2.jpg',
'/static/dele/dele3.png',
]);
// 分类数据
const categories = reactive([
{ id: 1, name: '热门兑换' }, { id: 2, name: '生活家居' }, { id: 3, name: '数码家电' },
{ id: 4, name: '美妆个护' }, { id: 5, name: '食品饮料' }, { id: 6, name: '虚拟卡券' },
]);
// 商品数据
const products = reactive([
{ id: 101, name: '品牌充电宝 10000mAh 金属外壳 超薄便携', img:'/static/dele/dele1.jpg', points: 2000, originalPrice: 19.9 },
{ id: 102, name: '声波震动电动牙刷 智能计时', img:'/static/dele/dele2.jpg', points: 5000 },
{ id: 103, name: '知名视频网站月度会员卡 直充', img:'/static/dele/dele3.png', points: 1500, originalPrice: 5 },
{ id: 104, name: '“天命打工人”限定版帆布袋', img:'/static/dele/dele4.jpg', points: 800 },
]);
const selectCategory = (index: number) => {
if (currentCategory.value === index) return;
currentCategory.value = index;
console.log(`切换到分类: ${categories[index].name}`);
};
onLoad(() => {
setTimeout(() => { loading.value = false; }, 1500);
});
onShow(() => {});
</script>
<style lang="scss" scoped>
/* 骨架屏样式 */
.skeleton-wrapper {
background-color: #f7f7f7;
.skeleton-content { display: flex; }
.skeleton-left-panel { width: 180rpx; padding: 30rpx 20rpx; background: #f7f7f7; }
.skeleton-right-panel { flex: 1; padding: 30rpx; background: #fff; }
.skeleton-card { display: flex; align-items: center; gap: 24rpx; padding-bottom: 30rpx; margin-bottom: 30rpx; border-bottom: 1rpx solid #f0f0f0;}
}
.points-mall-page {
background-color: #f7f7f7; height: 100vh;
display: flex; flex-direction: column;
}
.swiper-section {
padding: 24rpx;
position: relative;
.swiper-container {
height: 300rpx;
border-radius: 16rpx;
overflow: hidden;
}
.swiper-item {
width: 100%; height: 100%;
.swiper-image { width: 100%; height: 100%; }
}
.swiper-dots {
position: absolute;
bottom: 40rpx; left: 50%;
transform: translateX(-50%);
display: flex; gap: 12rpx;
.dot {
width: 12rpx; height: 12rpx;
border-radius: 50%; background-color: rgba(255, 255, 255, 0.5);
transition: all 0.3s;
&.active { width: 30rpx; background-color: #fff; }
}
}
}
.main-content {
flex: 1; display: flex; overflow: hidden;
}
.left-panel {
width: 180rpx; background-color: #f7f7f7; height: 100%;
.category-item {
padding: 30rpx 20rpx; font-size: 28rpx; color: #666;
text-align: center; position: relative;
&.active {
background-color: #fff; color: #333; font-weight: bold;
&::before {
content: ''; position: absolute; left: 0; top: 50%;
transform: translateY(-50%); width: 8rpx; height: 40rpx;
background-color: #fa6400;
}
}
}
}
.right-panel {
flex: 1; background-color: #fff; height: 100%; padding: 30rpx;
.product-list { display: flex; flex-direction: column; gap: 30rpx; }
.product-card {
display: flex; gap: 24rpx; align-items: center;
border-bottom: 1rpx solid #e2e2e2;
padding-bottom: 10rpx;
.product-image {
width: 160rpx; height: 160rpx;
border-radius: 12rpx; flex-shrink: 0;
}
.product-info {
flex: 1; min-width: 0;
.name {
font-size: 28rpx; color: #333; font-weight: 500;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden; text-overflow: ellipsis;
}
.price-line {
margin-top: 20rpx; display: flex; align-items: baseline;
.points { font-size: 32rpx; font-weight: bold; color: #fa6400; }
.original-price { font-size: 22rpx; color: #999; margin-left: 8rpx; }
}
}
.exchange-btn {
background-color: #fa6400; color: #fff;
border-radius: 30rpx; font-size: 24rpx;
padding: 10rpx 24rpx; margin: 0;
height: fit-content; line-height: 1.5;
&::after { border: none; }
}
}
}
</style>