364 lines
7.4 KiB
Plaintext
364 lines
7.4 KiB
Plaintext
<template>
|
|
<cl-page>
|
|
<view class="shop-page">
|
|
<!-- 自定义顶部导航栏 (Fixed Top) -->
|
|
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="navbar-content">
|
|
<text class="page-title">农场店</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 顶部占位 (Status + NavHeight 44) -->
|
|
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
|
|
|
<!-- 搜索栏与购物车 (Flows normally below navbar) -->
|
|
<view class="search-row">
|
|
<view class="search-bar" @click="goSearch">
|
|
<cl-icon name="search-line" :size="32" color="#999" />
|
|
<text class="search-placeholder">搜索农特产品</text>
|
|
</view>
|
|
|
|
<view class="cart-btn-wrap" @click="goCart">
|
|
<view class="cart-btn">
|
|
<cl-icon name="shopping-cart-line" :size="40" color="#333" />
|
|
</view>
|
|
<view class="badge" v-if="cartCount > 0">
|
|
<text class="badge-text">{{ cartCount }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 分类导航 -->
|
|
<scroll-view scroll-x class="category-scroll" :show-scrollbar="false">
|
|
<view class="category-list">
|
|
<view
|
|
v-for="cat in categories"
|
|
:key="cat.id"
|
|
class="category-item"
|
|
:class="{ active: currentCategory === cat.id }"
|
|
@click="selectCategory(cat.id)"
|
|
>
|
|
<text class="category-text" :class="{ 'active-text': currentCategory === cat.id }">{{ cat.name }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 商品列表 -->
|
|
<scroll-view scroll-y class="product-scroll" @scrolltolower="loadMore">
|
|
<view class="product-list">
|
|
<view class="product-row">
|
|
<view
|
|
v-for="(product, index) in productList"
|
|
:key="product.id"
|
|
class="product-card"
|
|
@click="goProductDetail(product.id)"
|
|
>
|
|
<image class="product-image" :src="product.mainImage || '/static/images/product.png'" mode="aspectFill" />
|
|
<view class="product-info">
|
|
<text class="product-name">{{ product.name }}</text>
|
|
<text class="product-origin">{{ product.origin }}</text>
|
|
<view class="product-footer">
|
|
<text class="product-price">¥{{ product.price }}</text>
|
|
<view class="add-btn" @click.stop="addToCart(product)">
|
|
<cl-icon name="add-line" :size="28" color="#fff" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<cl-empty v-if="productList.length === 0" text="暂无商品" />
|
|
</scroll-view>
|
|
</view>
|
|
|
|
<!-- 底部TabBar -->
|
|
<custom-tabbar :current="2" />
|
|
</cl-page>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, onMounted } from 'vue';
|
|
import { router, useCool } from '@/cool';
|
|
import CustomTabbar from '@/components/tabbar.uvue';
|
|
|
|
const { service } = useCool();
|
|
|
|
const statusBarHeight = ref(uni.getWindowInfo().statusBarHeight);
|
|
const categories = ref<any[]>([
|
|
{ id: 0, name: '全部' }
|
|
]);
|
|
|
|
const currentCategory = ref(0);
|
|
const productList = ref<any[]>([]);
|
|
const cartCount = ref(2);
|
|
|
|
// 加载分类
|
|
async function loadCategories() {
|
|
try {
|
|
const res = await service.nongchuang.category.list();
|
|
if (Array.isArray(res)) {
|
|
categories.value = [{ id: 0, name: '全部' }, ...res];
|
|
}
|
|
} catch (e) {
|
|
console.error('加载分类失败', e);
|
|
}
|
|
}
|
|
|
|
async function loadProducts() {
|
|
try {
|
|
const params: any = {};
|
|
if (currentCategory.value > 0) {
|
|
params.categoryId = currentCategory.value;
|
|
}
|
|
const res = await service.nongchuang.product.list(params);
|
|
if (res != null) {
|
|
// 后端直接返回数组或包含list的对象
|
|
if (Array.isArray(res)) {
|
|
productList.value = res;
|
|
} else if (res.list != null) {
|
|
productList.value = res.list as any[];
|
|
} else {
|
|
productList.value = [];
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('加载商品失败', e);
|
|
productList.value = [];
|
|
}
|
|
}
|
|
|
|
function selectCategory(id: number) {
|
|
currentCategory.value = id;
|
|
loadProducts();
|
|
}
|
|
|
|
function loadMore() {
|
|
// 加载更多
|
|
}
|
|
|
|
function goSearch() {
|
|
router.push({ path: '/pages/mall/search' });
|
|
}
|
|
|
|
function goCart() {
|
|
router.push({ path: '/pages/mall/cart' });
|
|
}
|
|
|
|
function goProductDetail(id: number) {
|
|
router.push({ path: '/pages/mall/detail', query: { id } });
|
|
}
|
|
|
|
async function addToCart(product: any) {
|
|
try {
|
|
await service.nongchuang.cart.add({ productId: product.id, quantity: 1 });
|
|
cartCount.value++;
|
|
uni.showToast({ title: '已加入购物车', icon: 'success' });
|
|
} catch (e: any) {
|
|
const msg = e.message;
|
|
uni.showToast({ title: msg != null ? msg : '添加失败', icon: 'none' });
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadCategories();
|
|
loadProducts();
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.shop-page {
|
|
flex: 1;
|
|
background-color: #f5f5f5;
|
|
padding-bottom: 120rpx;
|
|
}
|
|
|
|
.custom-navbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
z-index: 999;
|
|
background-color: #fff;
|
|
}
|
|
|
|
.navbar-content {
|
|
height: 44px; /* Standard Nav Height */
|
|
display: flex;
|
|
align-items: center;
|
|
padding-left: 30rpx;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 34rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.search-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
background-color: #fff;
|
|
/* 避免胶囊按钮遮挡,如果是放在顶部 */
|
|
}
|
|
|
|
.cart-btn-wrap {
|
|
position: relative;
|
|
margin-left: 20rpx;
|
|
}
|
|
|
|
.cart-btn {
|
|
width: 72rpx;
|
|
height: 72rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #fff;
|
|
border-radius: 50%;
|
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); /* 增加阴影提升质感 */
|
|
border: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.badge {
|
|
position: absolute;
|
|
top: -6rpx;
|
|
right: -6rpx;
|
|
min-width: 32rpx;
|
|
height: 32rpx;
|
|
background-color: #ff4d4f;
|
|
border-radius: 16rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0 8rpx;
|
|
z-index: 10;
|
|
border: 2rpx solid #fff; /* White border for separation */
|
|
}
|
|
|
|
.badge-text {
|
|
font-size: 20rpx;
|
|
color: #fff;
|
|
line-height: 1;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.search-bar {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
padding: 16rpx 30rpx;
|
|
background-color: #f5f5f5;
|
|
border-radius: 40rpx;
|
|
height: 72rpx;
|
|
}
|
|
|
|
.search-placeholder {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
margin-left: 16rpx;
|
|
}
|
|
|
|
.category-scroll {
|
|
background-color: #fff;
|
|
}
|
|
|
|
.category-list {
|
|
display: flex;
|
|
flex-direction: row;
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.category-item {
|
|
padding: 16rpx 30rpx;
|
|
margin-right: 16rpx;
|
|
background-color: #f5f5f5;
|
|
border-radius: 30rpx;
|
|
}
|
|
|
|
.category-item.active {
|
|
background-color: #52c41a;
|
|
}
|
|
|
|
.category-text {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.active-text {
|
|
color: #fff;
|
|
}
|
|
|
|
.product-scroll {
|
|
flex: 1;
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.product-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.product-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.product-card {
|
|
width: 48%;
|
|
background-color: #fff;
|
|
border-radius: 16rpx;
|
|
overflow: hidden;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.product-image {
|
|
width: 100%;
|
|
height: 280rpx;
|
|
}
|
|
|
|
.product-info {
|
|
padding: 16rpx;
|
|
}
|
|
|
|
.product-name {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.product-origin {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
margin-top: 6rpx;
|
|
}
|
|
|
|
.product-footer {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-top: 16rpx;
|
|
}
|
|
|
|
.product-price {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #ff4d4f;
|
|
}
|
|
|
|
.add-btn {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
background-color: #52c41a;
|
|
border-radius: 24rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
</style>
|