Files
shangbangbang/uniapp/pages/index/index.vue
T
solosw 99b11b04e4 1.0
2026-01-05 14:11:34 +08:00

482 lines
11 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 class="container">
<!-- 顶部搜索框 -->
<view class="search-container">
<view class="search-box">
<uni-icons type="search" size="16" color="#999"></uni-icons>
<input class="search-input" placeholder="请输入关键词搜索" />
</view>
</view>
<!-- 轮播图 -->
<view class="banner-container" v-if="banners.length > 0">
<swiper class="banner-swiper" indicator-dots="true" autoplay="true" interval="3000" duration="500">
<swiper-item v-for="banner in banners" :key="banner.id" @click="onBannerClick(banner)">
<view class="banner-item">
<image class="banner-image" :src="getFullImageUrl(banner.image_url)" mode="aspectFill"></image>
<!-- <view class="banner-content" v-if="banner.title || banner.description">
<text class="banner-title" v-if="banner.title">{{ banner.title }}</text>
<text class="banner-description" v-if="banner.description">{{ banner.description }}</text>
</view> -->
</view>
</swiper-item>
</swiper>
</view>
<!-- 分类导航 -->
<view class="category-container" v-if="categories.length > 0">
<swiper class="category-swiper" indicator-dots="true" v-if="categoryPages.length > 1">
<swiper-item v-for="(page, pageIndex) in categoryPages" :key="pageIndex">
<view class="category-grid">
<view class="category-row" v-for="(row, rowIndex) in page" :key="rowIndex">
<view class="category-item" v-for="category in row" :key="category.id" @click="navigateToCategory(category.id)">
<image v-if="category.category_icon" class="category-icon" :src="getFullImageUrl(category.category_icon)" mode="aspectFit"></image>
<uni-icons v-else type="apps" size="40" color="#FF4444"></uni-icons>
<text class="category-text">{{ category.category_name }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 如果只有一页分类不显示swiper -->
<view class="category-grid" v-else-if="categoryPages.length === 1">
<view class="category-row" v-for="(row, rowIndex) in categoryPages[0]" :key="rowIndex">
<view class="category-item" v-for="category in row" :key="category.id" @click="navigateToCategory(category.id)">
<image v-if="category.category_icon" class="category-icon" :src="getFullImageUrl(category.category_icon)" mode="aspectFit"></image>
<uni-icons v-else type="apps" size="40" color="#FF4444"></uni-icons>
<text class="category-text">{{ category.category_name }}</text>
</view>
</view>
</view>
</view>
<!-- 一级商品列表 -->
<view class="product-section" v-if="primaryProducts.length > 0">
<view class="section-header">
<text class="section-title">热门商品</text>
<text class="view-all" @click="viewAllProducts">查看全部></text>
</view>
<view class="product-grid">
<view class="product-item" v-for="product in displayProducts" :key="product.id" @click="navigateToProduct(product.id)">
<view class="product-image-container">
<image v-if="product.product_images"
:src="getFullImageUrl(getFirstImage(product.product_images))"
mode="aspectFill"
class="product-image"></image>
<view v-else class="default-image">
<uni-icons type="image" size="60" color="#ccc"></uni-icons>
</view>
</view>
<view class="product-info">
<text class="product-name">{{ product.product_name }}</text>
<text class="product-description">{{ product.product_description }}</text>
<view class="product-price">
<text class="current-price">¥{{ product.current_price }}</text>
<text class="original-price" v-if="product.original_price > product.current_price">¥{{ product.original_price }}</text>
</view>
<view class="product-stats">
<text class="stats-text">销量 {{ product.sales_count }}</text>
<text class="stats-text">库存 {{ product.stock_quantity }}</text>
<!-- <text class="stats-text">浏览 {{ product.view_count }}</text> -->
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import request from '@/utils/request.js'
export default {
data() {
return {
banners: [],
categories: [],
primaryProducts: [],
token: ''
}
},
onLoad() {
this.checkLogin()
this.loadData()
},
computed: {
// 将分类按每页8个分组,用于滑动分页
categoryPages() {
const pages = []
for (let i = 0; i < this.categories.length; i += 8) {
const pageCategories = this.categories.slice(i, i + 8)
// 每页内部按每行4个分组
const rows = []
for (let j = 0; j < pageCategories.length; j += 4) {
rows.push(pageCategories.slice(j, j + 4))
}
pages.push(rows)
}
return pages
},
// 首页只显示前8个商品,每行2个
displayProducts() {
return this.primaryProducts
}
},
methods: {
// 检查登录状态
checkLogin() {
this. token = uni.getStorageSync('token')
if (!this. token) {
uni.redirectTo({
url: '/pages/login/login'
})
}
},
// 加载数据
async loadData() {
try {
// 加载首页数据
const res = await request.get('/back/user/home/data')
if (res.success) {
this.banners = res.data.banners || []
this.categories = res.data.categories || []
this.primaryProducts = res.data.primary_products || []
this.primaryProducts=this.primaryProducts.slice(0,12)
}
} catch (error) {
console.error('加载首页数据失败:', error)
// uni.showToast({
// title: '加载数据失败',
// icon: 'none'
// })
}
},
// 获取完整的图片URL
getFullImageUrl(url) {
return request.getImageUrl(url)
},
// 获取商品的第一张图片
getFirstImage(images) {
if (!images) return ''
// 如果是逗号分隔的字符串
if (typeof images === 'string') {
return images.split(',')[0].trim()
}
// 如果是数组
if (Array.isArray(images) && images.length > 0) {
return images[0]
}
return ''
},
// 导航到分类页面(商城页面)
navigateToCategory(categoryId) {
uni.setStorage({
data:categoryId,
key:"categoryId",
success() {
setTimeout(()=>{
uni.switchTab({
url: `/pages/mall/mall`
})
},500)
}
})
},
// 导航到商品详情
navigateToProduct(productId) {
uni.navigateTo({
url: `/pages/product/detail?id=${productId}&type=primary`
})
},
// 轮播图点击事件
onBannerClick(banner) {
if (banner.link_url) {
// 如果有链接,跳转到对应页面
if (banner.link_url.startsWith('http')) {
// 外部链接,打开浏览器
// #ifdef APP-PLUS
plus.runtime.openURL(banner.link_url)
// #endif
// #ifdef H5
window.open(banner.link_url, '_blank')
// #endif
} else {
// 内部链接,使用uni.navigateTo
uni.navigateTo({
url: banner.link_url
})
}
}
},
// 查看全部商品
async viewAllProducts() {
try {
// 加载首页数据
const res = await request.get('/back/user/home/data')
if (res.success) {
this.banners = res.data.banners || []
this.categories = res.data.categories || []
this.primaryProducts = res.data.primary_products || []
}
} catch (error) {
console.error('加载首页数据失败:', error)
// uni.showToast({
// title: '加载数据失败',
// icon: 'none'
// })
}
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 搜索框样式 */
.search-container {
background: linear-gradient(135deg, #FF4444 0%, #FF6666 100%);
padding: 20rpx;
}
.search-box {
background-color: #fff;
border-radius: 50rpx;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
}
.search-box uni-icons {
margin-right: 20rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
color: #999;
}
/* 轮播图样式 */
.banner-container {
margin: 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
}
.banner-swiper {
height: 300rpx;
}
.banner-item {
position: relative;
height: 100%;
}
.banner-image {
width: 100%;
height: 100%;
}
.banner-content {
position: absolute;
right: 30rpx;
top: 30rpx;
color: #333;
}
.banner-title {
display: block;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
color: #fff;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
.banner-description {
display: block;
font-size: 24rpx;
color: #fff;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
/* 分类容器样式 */
.category-container {
margin: 20rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.category-swiper {
height: 400rpx;
}
/* 分类网格样式 */
.category-grid {
padding: 40rpx 20rpx;
}
.category-row {
display: flex;
justify-content: space-between;
margin-bottom: 40rpx;
}
.category-row:last-child {
margin-bottom: 0;
}
.category-item {
display: flex;
flex-direction: column;
align-items: center;
width: 140rpx;
}
.category-item uni-icons {
margin-bottom: 20rpx;
}
.category-icon {
width: 80rpx;
height: 80rpx;
margin-bottom: 20rpx;
}
.category-text {
font-size: 24rpx;
color: #333;
}
/* 商品区域样式 */
.product-section {
margin: 20rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.view-all {
font-size: 24rpx;
color: #999;
}
/* 商品网格样式 */
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.product-item {
background-color: #f8f8f8;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.product-image-container {
width: 100%;
height: 200rpx;
position: relative;
}
.product-image {
width: 100%;
height: 100%;
}
.default-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.product-info {
padding: 20rpx;
}
.product-name {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-description {
display: block;
font-size: 24rpx;
color: #666;
margin-bottom: 15rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-price {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.current-price {
font-size: 32rpx;
font-weight: bold;
color: #FF4444;
margin-right: 15rpx;
}
.original-price {
font-size: 24rpx;
color: #999;
text-decoration: line-through;
}
.product-stats {
display: flex;
justify-content: space-between;
}
.stats-text {
font-size: 22rpx;
color: #999;
}
</style>