This commit is contained in:
solosw
2026-01-05 14:11:34 +08:00
parent 35ef825371
commit 99b11b04e4
658 changed files with 99266 additions and 0 deletions
+482
View File
@@ -0,0 +1,482 @@
<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>