|
@@ -1,425 +0,0 @@
|
|
|
-<template>
|
|
|
|
|
- <view class="container">
|
|
|
|
|
- <!-- 购物车入口 -->
|
|
|
|
|
- <view class="cart-entry" @click="goCart">
|
|
|
|
|
- <text class="cart-icon">🛒</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <!-- Level 1 分类:2列横排网格 -->
|
|
|
|
|
- <view class="cat-level1">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="cat in categories"
|
|
|
|
|
- :key="cat.id"
|
|
|
|
|
- :class="['cat1-item', currentCat1 && currentCat1.id === cat.id ? 'active' : '']"
|
|
|
|
|
- @click="onCat1Select(cat)"
|
|
|
|
|
- >
|
|
|
|
|
- <image v-if="cat.image" class="cat1-icon" :src="cat.image" mode="aspectFit" />
|
|
|
|
|
- <view v-else class="cat1-icon-placeholder">
|
|
|
|
|
- <text class="cat1-icon-text">{{ cat.name.substr(0, 1) }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="cat1-name">{{ cat.name }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- Level 2 子分类:横向滚动chips -->
|
|
|
|
|
- <scroll-view
|
|
|
|
|
- v-if="currentCat1 && currentCat1.children && currentCat1.children.length"
|
|
|
|
|
- scroll-x
|
|
|
|
|
- enable-flex
|
|
|
|
|
- show-scrollbar="false"
|
|
|
|
|
- class="cat-level2-scroll"
|
|
|
|
|
- >
|
|
|
|
|
- <view class="cat2-chips">
|
|
|
|
|
- <view
|
|
|
|
|
- :class="['cat2-chip', !currentCat2 ? 'active' : '']"
|
|
|
|
|
- @click="onCat2Select(null)"
|
|
|
|
|
- >全部</view>
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="child in currentCat1.children"
|
|
|
|
|
- :key="child.id"
|
|
|
|
|
- :class="['cat2-chip', currentCat2 && currentCat2.id === child.id ? 'active' : '']"
|
|
|
|
|
- @click="onCat2Select(child)"
|
|
|
|
|
- >{{ child.name }}</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </scroll-view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 商品列表 -->
|
|
|
|
|
- <scroll-view
|
|
|
|
|
- scroll-y
|
|
|
|
|
- class="product-list"
|
|
|
|
|
- @scrolltolower="onLoadMore"
|
|
|
|
|
- refresher-enabled
|
|
|
|
|
- :refresher-triggered="refreshing"
|
|
|
|
|
- @refresherrefresh="onRefresh"
|
|
|
|
|
- >
|
|
|
|
|
- <view v-if="loading && products.length === 0" class="loading-wrap">
|
|
|
|
|
- <text class="loading-text">加载中...</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-else-if="products.length === 0" class="empty-wrap">
|
|
|
|
|
- <text class="empty-icon">📦</text>
|
|
|
|
|
- <text class="empty-text">暂无商品</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-else class="grid">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="item in products"
|
|
|
|
|
- :key="item.id"
|
|
|
|
|
- class="product-card"
|
|
|
|
|
- @click="goDetail(item.id)"
|
|
|
|
|
- >
|
|
|
|
|
- <image
|
|
|
|
|
- class="cover"
|
|
|
|
|
- :src="item.image || item.coverImage || '/static/default-product.png'"
|
|
|
|
|
- mode="aspectFill"
|
|
|
|
|
- />
|
|
|
|
|
- <view class="card-body">
|
|
|
|
|
- <text class="product-name">{{ item.name }}</text>
|
|
|
|
|
- <view class="price-row">
|
|
|
|
|
- <text class="price">{{ formatPriceWithSymbol(item.price) }}</text>
|
|
|
|
|
- <text v-if="item.memberPrice" class="member-price">会员{{ formatPriceWithSymbol(item.memberPrice) }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="vendor-name">{{ item.brandName || '平台官方' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-if="loadingMore" class="loading-more">
|
|
|
|
|
- <text class="loading-text">加载中...</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-if="noMore && products.length > 0" class="no-more">
|
|
|
|
|
- <text class="no-more-text">— 没有更多了 —</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-if="!isLoggedIn" class="login-hint-bar">
|
|
|
|
|
- <text class="login-hint-text">🔒 登录查看全部商品和会员价</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="bottom-spacer"></view>
|
|
|
|
|
- </scroll-view>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- </view>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script>
|
|
|
|
|
-import { goodsCategory, productList } from '@/utils/api.js'
|
|
|
|
|
-export default {
|
|
|
|
|
- data() {
|
|
|
|
|
- return {
|
|
|
|
|
- categories: [],
|
|
|
|
|
- currentCat1: null,
|
|
|
|
|
- currentCat2: null,
|
|
|
|
|
- products: [],
|
|
|
|
|
- page: 1,
|
|
|
|
|
- size: 20,
|
|
|
|
|
- loading: false,
|
|
|
|
|
- loadingMore: false,
|
|
|
|
|
- refreshing: false,
|
|
|
|
|
- noMore: false,
|
|
|
|
|
- isLoggedIn: false
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- onLoad() {
|
|
|
|
|
- this.isLoggedIn = !!uni.getStorageSync('token')
|
|
|
|
|
- this.loadCategories()
|
|
|
|
|
- this.loadProducts()
|
|
|
|
|
- },
|
|
|
|
|
- onShow() {
|
|
|
|
|
- this.isLoggedIn = !!uni.getStorageSync('token')
|
|
|
|
|
- },
|
|
|
|
|
- methods: {
|
|
|
|
|
- loadCategories() {
|
|
|
|
|
- goodsCategory('0').then(res => {
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- this.categories = res.data || []
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(() => {})
|
|
|
|
|
- },
|
|
|
|
|
- onCat1Select(cat) {
|
|
|
|
|
- if (this.currentCat1 && this.currentCat1.id === cat.id) return
|
|
|
|
|
- this.currentCat1 = cat
|
|
|
|
|
- this.currentCat2 = null
|
|
|
|
|
- this.page = 1
|
|
|
|
|
- this.products = []
|
|
|
|
|
- this.noMore = false
|
|
|
|
|
- this.loadProducts()
|
|
|
|
|
- },
|
|
|
|
|
- onCat2Select(cat) {
|
|
|
|
|
- this.currentCat2 = cat
|
|
|
|
|
- this.page = 1
|
|
|
|
|
- this.products = []
|
|
|
|
|
- this.noMore = false
|
|
|
|
|
- this.loadProducts()
|
|
|
|
|
- },
|
|
|
|
|
- loadProducts() {
|
|
|
|
|
- if (this.loading) return
|
|
|
|
|
- this.loading = true
|
|
|
|
|
- const params = { page: this.page, size: this.size }
|
|
|
|
|
- if (this.currentCat2) {
|
|
|
|
|
- params.categoryId = this.currentCat2.id
|
|
|
|
|
- } else if (this.currentCat1) {
|
|
|
|
|
- params.categoryId = this.currentCat1.id
|
|
|
|
|
- }
|
|
|
|
|
- productList(params).then(res => {
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- this.refreshing = false
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- const records = res.data.records || []
|
|
|
|
|
- if (this.page === 1) {
|
|
|
|
|
- this.products = records
|
|
|
|
|
- } else {
|
|
|
|
|
- this.products = this.products.concat(records)
|
|
|
|
|
- }
|
|
|
|
|
- const total = res.data.total || 0
|
|
|
|
|
- this.noMore = this.products.length >= total
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(() => {
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- this.refreshing = false
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- onRefresh() {
|
|
|
|
|
- this.refreshing = true
|
|
|
|
|
- this.page = 1
|
|
|
|
|
- this.noMore = false
|
|
|
|
|
- this.loadProducts()
|
|
|
|
|
- },
|
|
|
|
|
- onLoadMore() {
|
|
|
|
|
- if (this.noMore || this.loadingMore) return
|
|
|
|
|
- this.page++
|
|
|
|
|
- this.loadingMore = true
|
|
|
|
|
- const params = { page: this.page, size: this.size }
|
|
|
|
|
- if (this.currentCat2) {
|
|
|
|
|
- params.categoryId = this.currentCat2.id
|
|
|
|
|
- } else if (this.currentCat1) {
|
|
|
|
|
- params.categoryId = this.currentCat1.id
|
|
|
|
|
- }
|
|
|
|
|
- productList(params).then(res => {
|
|
|
|
|
- this.loadingMore = false
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- const records = res.data.records || []
|
|
|
|
|
- this.products = this.products.concat(records)
|
|
|
|
|
- const total = res.data.total || 0
|
|
|
|
|
- this.noMore = this.products.length >= total
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(() => {
|
|
|
|
|
- this.loadingMore = false
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- goDetail(id) {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/detail/detail?id=' + id })
|
|
|
|
|
- },
|
|
|
|
|
- goCart() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/cart/cart' })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style scoped>
|
|
|
|
|
-.container {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- height: 100vh;
|
|
|
|
|
- background: #f5f5f5;
|
|
|
|
|
-}
|
|
|
|
|
-/* ==================== 分类布局 ==================== */
|
|
|
|
|
-/* Level 1 分类:2列横排网格 */
|
|
|
|
|
-.cat-level1 {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- padding: 16rpx 16rpx 8rpx;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-bottom: 1rpx solid #eee;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-item {
|
|
|
|
|
- width: 50%;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- padding: 8rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-item.active {
|
|
|
|
|
- background: rgba(249, 115, 22, 0.08);
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-icon {
|
|
|
|
|
- width: 72rpx;
|
|
|
|
|
- height: 72rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-bottom: 6rpx;
|
|
|
|
|
- background: #f5f5f5;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-icon-placeholder {
|
|
|
|
|
- width: 72rpx;
|
|
|
|
|
- height: 72rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- margin-bottom: 6rpx;
|
|
|
|
|
- background: linear-gradient(135deg, #667eea, #764ba2);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-icon-text {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-name {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- max-width: 140rpx;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
-}
|
|
|
|
|
-.cat1-item.active .cat1-name {
|
|
|
|
|
- color: #F97316;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* Level 2 子分类:横向滚动chips */
|
|
|
|
|
-.cat-level2-scroll {
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- padding: 12rpx 16rpx;
|
|
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
-}
|
|
|
|
|
-.cat2-chips {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- gap: 12rpx;
|
|
|
|
|
- padding: 0 4rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.cat2-chip {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- padding: 8rpx 24rpx;
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #666;
|
|
|
|
|
- background: #f0f0f0;
|
|
|
|
|
- border-radius: 30rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.cat2-chip.active {
|
|
|
|
|
- background: #F97316;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* ==================== 商品列表 ==================== */
|
|
|
|
|
-.product-list {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- height: calc(100vh - 220rpx);
|
|
|
|
|
-}
|
|
|
|
|
-.login-hint-bar {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 20rpx;
|
|
|
|
|
- background: #fef9e7;
|
|
|
|
|
- border-top: 1rpx solid #f0e6c0;
|
|
|
|
|
-}
|
|
|
|
|
-.login-hint-text {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #b8860b;
|
|
|
|
|
-}
|
|
|
|
|
-.bottom-spacer {
|
|
|
|
|
- height: 120rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.grid {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- padding: 20rpx;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
-}
|
|
|
|
|
-.product-card {
|
|
|
|
|
- width: 49%;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
|
|
|
|
|
-}
|
|
|
|
|
-.cover {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 320rpx;
|
|
|
|
|
- background: #f0f0f0;
|
|
|
|
|
-}
|
|
|
|
|
-.card-body {
|
|
|
|
|
- padding: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.product-name {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- lines: 2;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- display: -webkit-box;
|
|
|
|
|
- -webkit-line-clamp: 2;
|
|
|
|
|
- -webkit-box-orient: vertical;
|
|
|
|
|
- margin-bottom: 12rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.price-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: baseline;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.price {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- color: #F97316;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- margin-right: 12rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.member-price {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.vendor-name {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.loading-wrap,
|
|
|
|
|
-.empty-wrap {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding-top: 200rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.loading-text {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.empty-icon {
|
|
|
|
|
- font-size: 100rpx;
|
|
|
|
|
- margin-bottom: 30rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.empty-text {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.loading-more,
|
|
|
|
|
-.no-more {
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 30rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.loading-text {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
-}
|
|
|
|
|
-.no-more-text {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: #ccc;
|
|
|
|
|
-}
|
|
|
|
|
-.cart-entry {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- top: 20rpx;
|
|
|
|
|
- right: 20rpx;
|
|
|
|
|
- z-index: 99;
|
|
|
|
|
- width: 80rpx;
|
|
|
|
|
- height: 80rpx;
|
|
|
|
|
- background: rgba(255,255,255,0.95);
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.1);
|
|
|
|
|
-}
|
|
|
|
|
-.cart-icon {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|