185 lines
3.9 KiB
Vue
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>
|