Initial commit: frontend code

This commit is contained in:
Agent
2026-03-20 04:59:03 +00:00
commit e0e38d6ecd
14 changed files with 2092 additions and 0 deletions

274
pages/product/list.vue Normal file
View File

@@ -0,0 +1,274 @@
<template>
<view class="page">
<!-- 搜索栏 -->
<view class="search-bar">
<input
class="search-input"
v-model="keyword"
placeholder="搜索商品名称"
@confirm="search"
/>
<button class="search-btn" @click="search">搜索</button>
</view>
<!-- 分类筛选 -->
<scroll-view class="category-scroll" scroll-x>
<view
class="category-item"
:class="{ active: !categoryId }"
@click="selectCategory('')"
>
全部
</view>
<view
v-for="cat in categories"
:key="cat.categoryId"
class="category-item"
:class="{ active: categoryId === cat.categoryId }"
@click="selectCategory(cat.categoryId)"
>
{{ cat.name }}
</view>
</scroll-view>
<!-- 商品列表 -->
<view class="product-list">
<view
v-for="item in products"
:key="item.productId"
class="product-item"
@click="viewDetail(item)"
>
<view class="product-info">
<text class="product-name">{{ item.name }}</text>
<text class="product-spec">{{ item.spec || '-' }}</text>
<view class="product-price">
<text class="price">¥{{ item.price }}</text>
<text class="unit">/{{ item.unit }}</text>
</view>
</view>
<view class="product-stock">
<text class="stock-label">库存</text>
<text class="stock-value">{{ getStock(item.productId) }}</text>
</view>
</view>
<!-- 空状态 -->
<view v-if="products.length === 0" class="empty">
<text>暂无商品</text>
</view>
</view>
<!-- 加载更多 -->
<view v-if="products.length > 0" class="load-more">
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
</view>
</view>
</template>
<script>
import productApi from '@/api/product'
export default {
data() {
return {
keyword: '',
categoryId: '',
categories: [],
products: [],
stocks: {},
page: 1,
pageSize: 20,
hasMore: true,
loading: false
}
},
onLoad() {
this.loadCategories()
this.loadProducts()
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.page++
this.loadProducts()
}
},
methods: {
async loadCategories() {
try {
const categories = await productApi.getCategories()
this.categories = categories || []
} catch (e) {
console.error(e)
}
},
async loadProducts() {
if (this.loading) return
this.loading = true
try {
const res = await productApi.getProducts({
categoryId: this.categoryId,
keyword: this.keyword,
page: this.page,
pageSize: this.pageSize
})
const list = res.records || []
if (this.page === 1) {
this.products = list
} else {
this.products = [...this.products, ...list]
}
this.hasMore = list.length >= this.pageSize
} catch (e) {
console.error(e)
} finally {
this.loading = false
}
},
selectCategory(id) {
this.categoryId = id
this.page = 1
this.loadProducts()
},
search() {
this.page = 1
this.loadProducts()
},
viewDetail(item) {
uni.showToast({ title: '商品详情开发中', icon: 'none' })
},
getStock(productId) {
return this.stocks[productId] || 0
}
}
}
</script>
<style>
.page {
padding: 20rpx;
}
.search-bar {
display: flex;
margin-bottom: 20rpx;
}
.search-input {
flex: 1;
height: 70rpx;
padding: 0 20rpx;
background: #fff;
border-radius: 8rpx;
font-size: 28rpx;
}
.search-btn {
width: 120rpx;
height: 70rpx;
line-height: 70rpx;
background: #3cc51f;
color: #fff;
border: none;
border-radius: 8rpx;
margin-left: 20rpx;
font-size: 28rpx;
}
.category-scroll {
white-space: nowrap;
margin-bottom: 20rpx;
}
.category-item {
display: inline-block;
padding: 12rpx 24rpx;
margin-right: 16rpx;
background: #fff;
border-radius: 8rpx;
font-size: 26rpx;
color: #666;
}
.category-item.active {
background: #3cc51f;
color: #fff;
}
.product-list {
background: #fff;
border-radius: 16rpx;
}
.product-item {
display: flex;
justify-content: space-between;
padding: 24rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.product-info {
flex: 1;
}
.product-name {
font-size: 28rpx;
font-weight: bold;
display: block;
}
.product-spec {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
.product-price {
margin-top: 12rpx;
}
.price {
color: #ff4d4f;
font-size: 28rpx;
font-weight: bold;
}
.unit {
color: #999;
font-size: 24rpx;
}
.product-stock {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.stock-label {
font-size: 24rpx;
color: #999;
}
.stock-value {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.empty {
padding: 100rpx;
text-align: center;
color: #999;
}
.load-more {
padding: 20rpx;
text-align: center;
color: #999;
font-size: 24rpx;
}
</style>