Files
shop-toy/src/pages/index/index.vue
FlowerWater 0eb8ac9181 页面提交
2025-11-29 17:20:17 +08:00

185 lines
3.9 KiB
Vue

<script lang="ts" setup>
import { getBannerList } from '@/api/banner'
import { getCategoryList } from '@/api/category'
import { getRecommendGoods } from '@/api/goods'
import type { Banner, Category, Goods } from '@/typings/mall'
import SearchBar from '@/components/common/SearchBar.vue'
import BannerComponent from '@/components/common/Banner.vue'
import CategoryGrid from '@/components/common/CategoryGrid.vue'
import GoodsCard from '@/components/goods/GoodsCard.vue'
import { useCartStore } from '@/store/cart'
definePage({
style: {
navigationBarTitleText: '首页',
},
})
const cartStore = useCartStore()
// 数据
const bannerList = ref<Banner[]>([])
const categoryList = ref<Category[]>([])
const goodsList = ref<Goods[]>([])
const searchKeyword = ref('')
// 加载状态
const loading = ref(false)
onMounted(() => {
loadData()
})
// 加载数据
async function loadData() {
loading.value = true
try {
// 并行加载数据
const [bannerRes, categoryRes, goodsRes] = await Promise.all([
getBannerList(),
getCategoryList(),
getRecommendGoods(10),
])
bannerList.value = (bannerRes as any).data || []
categoryList.value = (categoryRes as any).data || []
goodsList.value = (goodsRes as any).data || []
}
catch (error) {
console.error('加载数据失败', error)
uni.showToast({
title: '加载失败',
icon: 'none',
})
}
finally {
loading.value = false
}
}
// 搜索
function handleSearch(keyword: string) {
if (!keyword.trim()) {
uni.showToast({
title: '请输入搜索关键词',
icon: 'none',
})
return
}
uni.navigateTo({
url: `/pages/goods/list?keyword=${encodeURIComponent(keyword)}`,
})
}
// 下拉刷新
function onPullDownRefresh() {
loadData().finally(() => {
uni.stopPullDownRefresh()
})
}
defineExpose({
onPullDownRefresh,
})
</script>
<template>
<view class="index-page">
<!-- 搜索框 -->
<SearchBar
v-model="searchKeyword"
:show-cancel="false"
@search="handleSearch"
/>
<!-- 轮播图 -->
<BannerComponent v-if="bannerList.length" :list="bannerList" />
<!-- 分类 -->
<view class="section">
<view class="section-title">商品分类</view>
<CategoryGrid :list="categoryList" :columns="4" />
</view>
<!-- 推荐商品 -->
<view class="section">
<view class="section-title">
<text>为你推荐</text>
<text class="more" @click="() => uni.switchTab({ url: '/pages/sort/index' })">
更多
<text class="i-carbon-chevron-right"></text>
</text>
</view>
<view class="goods-list">
<GoodsCard
v-for="item in goodsList"
:key="item.id"
:goods="item"
/>
</view>
</view>
<!-- 购物车角标 -->
<view v-if="cartStore.totalCount > 0" class="cart-badge">
{{ cartStore.totalCount > 99 ? '99+' : cartStore.totalCount }}
</view>
</view>
</template>
<style lang="scss" scoped>
.index-page {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 120rpx;
}
.section {
margin-top: 24rpx;
background: #fff;
}
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
font-size: 32rpx;
font-weight: 600;
color: #333;
.more {
font-size: 24rpx;
font-weight: 400;
color: #999;
display: flex;
align-items: center;
gap: 4rpx;
}
}
.goods-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
padding: 0 16rpx 16rpx;
}
.cart-badge {
position: fixed;
bottom: 120rpx;
right: 40rpx;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #ff6b6b 0%, #ff4d4f 100%);
color: #fff;
font-size: 24rpx;
font-weight: 600;
border-radius: 50%;
box-shadow: 0 4rpx 12rpx rgba(255, 77, 79, 0.4);
z-index: 100;
}
</style>