Files
solosw 99b11b04e4 1.0
2026-01-05 14:11:34 +08:00

753 lines
16 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<!-- 筛选栏 -->
<view class="filter-bar">
<view class="filter-item" @click="showSortMenu('default')">
<text class="filter-text" :class="{ active: sortType === 'default' }">默认</text>
<uni-icons type="arrowdown" size="12" color="#999"></uni-icons>
</view>
<view class="filter-item" @click="showSortMenu('price')">
<text class="filter-text" :class="{ active: sortType === 'price' }">价格</text>
<uni-icons type="arrowdown" size="12" color="#999"></uni-icons>
</view>
<view class="filter-item" @click="showSortMenu('quantity')">
<text class="filter-text" :class="{ active: sortType === 'quantity' }">库存</text>
<uni-icons type="arrowdown" size="12" color="#999"></uni-icons>
</view>
<view class="filter-item" @click="showAddressFilter">
<text class="filter-text" :class="{ active: selectedRegion }">{{ selectedRegion || '地区' }}</text>
<uni-icons type="location" size="12" color="#999"></uni-icons>
</view>
<view class="filter-item" @click="resetAllFilters">
<text class="filter-text">重置</text>
<uni-icons type="reload" size="12" color="#999"></uni-icons>
</view>
<view class="filter-item" @click="showFilterPanel">
<uni-icons type="bars" size="16" color="#999"></uni-icons>
</view>
</view>
<!-- 商品列表 -->
<view class="product-list">
<view class="product-item"
:class="{ 'disabled': !item.can_purchase }"
v-for="(item, index) in productList"
:key="index"
@click="goToDetail(item)">
<view class="product-image-container">
<image v-if="item.product_images"
:src="getFullImageUrl(getFirstImage(item.product_images))"
mode="aspectFill"
:class="['product-image', { 'image-disabled': !item.can_purchase }]"></image>
<view v-else class="default-image">
<uni-icons type="image" size="40" color="#ccc"></uni-icons>
</view>
<!-- 购买限制提示遮罩 -->
<view class="purchase-limit-overlay" v-if="!item.can_purchase">
<!-- <text class="limit-text">{{ item.purchase_limit_message || '暂时无法购买' }}</text> -->
</view>
</view>
<view class="product-info">
<text class="product-title" :class="{ 'text-disabled': !item.can_purchase }">{{ item.product_name }}</text>
<view class="product-stats">
<text class="stats-text">销量 {{ item.sales_count || 0 }}</text>
<text class="stats-text">库存 {{ item.quantity || 0 }}</text>
</view>
<view class="product-price">
<text class="current-price" :class="{ 'price-disabled': !item.can_purchase }">¥{{ item.selling_price || item.current_price }}</text>
</view>
<view class="seller-info" v-if="item.seller">
<text class="seller-text" :class="{ 'text-disabled': !item.can_purchase }">卖家:{{ item.seller.real_name || item.seller.phone }}</text>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore">
<text>加载更多...</text>
</view>
<view class="no-more" v-else-if="productList.length > 0">
<text>没有更多了</text>
</view>
<!-- 地址筛选弹窗 -->
<view class="address-modal" v-if="showAddressModal" @tap="hideAddressFilter">
<view class="modal-content" @tap.stop>
<view class="modal-header">
<text class="modal-title">选择地区</text>
<text class="modal-close" @tap="hideAddressFilter">×</text>
</view>
<view class="modal-body">
<view class="region-options">
<!-- <view class="region-option" :class="{ active: !tempProvince }" @tap="selectAllRegion">
<text class="option-text">全部地区</text>
<view class="option-check" v-if="!tempProvince"></view>
</view> -->
<!-- 使用k-region组件 -->
<view class="k-region-container">
<Region
:width="300"
:height="92"
:showAllDistrict="true"
:isRevise="false"
@region="onRegionChange"
/>
</view>
</view>
<view class="filter-actions">
<button class="reset-btn" @tap="resetRegionFilter">重置</button>
<button class="confirm-btn" @tap="confirmRegionFilter">确定</button>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import request from '@/utils/request.js'
import Region from '@/components/k-region.vue'
export default {
components: {
Region
},
data() {
return {
productList: [],
sortType: 'default',
currentPage: 1,
pageSize: 10,
hasMore: true,
loading: false,
categoryId: '',
// 地址筛选相关
showAddressModal: false,
selectedProvince: '',
selectedCity: '',
selectedDistrict: '',
tempProvince: '',
tempCity: '',
tempDistrict: '',
showAllDistrict: false
}
},
onPullDownRefresh() {
var that=this
uni.getStorage({
key:"categoryId",
success(res) {
that.categoryId=res.data
that.currentPage=1
that.loadProducts()
uni.setStorage({
key:"categoryId",
data:0,
success() {
}
})
},
fail() {
that.loadProducts()
}
})
},
computed: {
selectedRegion() {
if (this.selectedProvince && this.selectedCity) {
if (this.selectedDistrict) {
return `${this.selectedProvince} ${this.selectedCity} ${this.selectedDistrict}`
} else {
return `${this.selectedProvince} ${this.selectedCity} 全部区`
}
}
return ''
},
regionText() {
if (this.tempProvince && this.tempCity) {
if (this.tempDistrict) {
return `${this.tempProvince} ${this.tempCity} ${this.tempDistrict}`
} else if (this.showAllDistrict) {
return `${this.tempProvince} ${this.tempCity} 全部区`
} else {
return `${this.tempProvince} ${this.tempCity}`
}
}
return ''
}
},
onShow() {
const token = uni.getStorageSync('token')
if (!token) {
uni.reLaunch({
url: '/pages/login/login'
})
return
}
var that=this
that.currentPage=1
uni.getStorage({
key:"categoryId",
success(res) {
that.categoryId=res.data
that.loadProducts()
uni.setStorage({
key:"categoryId",
data:0,
success() {
}
})
},
fail() {
that.loadProducts()
}
})
},
onLoad(options) {
// 检查登录状态
// // 保存分类参数
// if (options.category_id) {
// this.categoryId = options.category_id
// }
// this.loadProducts()
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.loadMore()
}
},
methods: {
// 获取完整的图片URL
getFullImageUrl(url) {
return request.getImageUrl(url)
},
// 获取商品的第一张图片
getFirstImage(images) {
if (!images) return ''
// 如果是逗号分隔的字符串
if (typeof images === 'string') {
return images.split(',')[0].trim()
}
// 如果是数组
if (Array.isArray(images) && images.length > 0) {
return images[0]
}
return ''
},
// 加载商品列表
async loadProducts() {
this.loading = true
try {
const params = {
page: this.currentPage,
page_size: this.pageSize,
sort: this.sortType
}
// 如果有分类ID,添加到参数中
if (this.categoryId) {
params.category_id = this.categoryId
}
// 如果有地址筛选,添加到参数中
if (this.selectedProvince) {
params.province = this.selectedProvince
}
if (this.selectedCity) {
params.city = this.selectedCity
}
if (this.selectedDistrict) {
params.district = this.selectedDistrict
}
// 这里显示二级商品(用户挂售的商品)
const res = await request.get('/back/user/products/secondary', params)
if (res.success) {
const data = res.data
if (this.currentPage === 1) {
this.productList = data.list || []
} else {
this.productList = [...this.productList, ...(data.list || [])]
}
this.hasMore = this.productList.length < data.total
}
} catch (error) {
console.error('加载商品失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
// 加载更多
loadMore() {
this.currentPage++
this.loadProducts()
},
// 显示排序菜单
showSortMenu(type) {
this.sortType = type
this.currentPage = 1
this.loadProducts()
},
// 显示筛选面板
showFilterPanel() {
uni.showActionSheet({
itemList: ['价格升序', '价格降序', '销量排序', '库存排序'],
success: (res) => {
const sortTypes = ['price_asc', 'price_desc', 'sales', 'quantity']
this.sortType = sortTypes[res.tapIndex]
this.currentPage = 1
this.loadProducts()
}
})
},
// 跳转到商品详情
goToDetail(item) {
// 如果不可购买,显示提示
if (!item.can_purchase) {
uni.showToast({
title: item.purchase_limit_message || '暂时无法购买',
icon: 'none',
duration: 2000
})
return
}
uni.navigateTo({
url: `/pages/product/detail?id=${item.id}&type=secondary`
})
},
// 显示地址筛选
showAddressFilter() {
this.showAddressModal = true
// 将当前选择的地址设为临时变量
this.tempProvince = this.selectedProvince
this.tempCity = this.selectedCity
this.tempDistrict = this.selectedDistrict
// 如果选择了省市但没有选择区,说明是"全部区"状态
this.showAllDistrict = this.selectedProvince && this.selectedCity && !this.selectedDistrict
},
// 隐藏地址筛选
hideAddressFilter() {
this.showAddressModal = false
},
// k-region组件回调
onRegionChange(regionArray) {
console.log('选择的地区:', regionArray)
this.tempProvince = regionArray[0] || ''
this.tempCity = regionArray[1] || ''
this.tempDistrict = regionArray[2] || ''
// 如果选择了省市但区为空,说明选择了"全部区"
this.showAllDistrict = this.tempProvince && this.tempCity && !this.tempDistrict
},
// 重置地区筛选
resetRegionFilter() {
this.tempProvince = ''
this.tempCity = ''
this.tempDistrict = ''
this.showAllDistrict = false
this.selectedProvince = ''
this.selectedCity = ''
this.selectedDistrict = ''
this.currentPage = 1
this.loadProducts()
this.hideAddressFilter()
},
// 确认地区筛选
confirmRegionFilter() {
this.selectedProvince = this.tempProvince
this.selectedCity = this.tempCity
// 如果选择了"全部区",则不设置具体区名
this.selectedDistrict = this.showAllDistrict ? '' : this.tempDistrict
this.currentPage = 1
this.loadProducts()
this.hideAddressFilter()
},
// 选择全部地区
selectAllRegion() {
this.tempProvince = ''
this.tempCity = ''
this.tempDistrict = ''
this.showAllDistrict = false
},
// 选择全部区
selectAllDistrict() {
this.tempDistrict = ''
this.showAllDistrict = true
},
// 重置所有筛选条件
resetAllFilters() {
uni.showModal({
title: '提示',
content: '确定要重置所有筛选条件吗?',
success: (res) => {
if (res.confirm) {
// 重置排序
this.sortType = 'default'
// 重置地区筛选
this.selectedProvince = ''
this.selectedCity = ''
this.selectedDistrict = ''
this.tempProvince = ''
this.tempCity = ''
this.tempDistrict = ''
this.showAllDistrict = false
// 重置分类
this.categoryId = ''
// 重置分页
this.currentPage = 1
// 重新加载数据
this.loadProducts()
uni.showToast({
title: '已重置',
icon: 'success'
})
}
}
})
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 筛选栏样式 */
.filter-bar {
background-color: #fff;
display: flex;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #eee;
}
.filter-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.filter-text {
font-size: 28rpx;
color: #333;
margin-right: 10rpx;
}
.filter-text.active {
color: #FF4444;
}
/* 商品列表样式 */
.product-list {
padding: 20rpx;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.product-item {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
width: 48%;
}
.product-item.disabled {
opacity: 0.6;
background-color: #f5f5f5;
}
.product-image-container {
width: 100%;
height: 200rpx;
position: relative;
}
.product-image {
width: 100%;
height: 100%;
}
.product-image.image-disabled {
filter: grayscale(100%);
opacity: 0.5;
}
.purchase-limit-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.limit-text {
color: #fff;
font-size: 24rpx;
text-align: center;
padding: 10rpx 20rpx;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 8rpx;
}
.default-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.product-info {
padding: 20rpx;
}
.product-title {
display: block;
font-size: 26rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-title.text-disabled {
color: #999;
}
.product-stats {
display: flex;
margin-bottom: 10rpx;
justify-content: space-between;
}
.stats-text {
font-size: 20rpx;
color: #999;
}
.product-price {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.current-price {
font-size: 28rpx;
font-weight: bold;
color: #FF4444;
}
.current-price.price-disabled {
color: #999;
}
.original-price {
font-size: 20rpx;
color: #999;
text-decoration: line-through;
margin-left: 10rpx;
}
.seller-info {
margin-top: 8rpx;
padding-top: 8rpx;
border-top: 1rpx solid #f0f0f0;
}
.seller-text {
font-size: 20rpx;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.seller-text.text-disabled {
color: #999;
}
/* 加载更多样式 */
.load-more, .no-more {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 24rpx;
}
/* 地址筛选弹窗样式 */
.address-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: #fff;
border-radius: 20rpx;
width: 600rpx;
max-height: 80vh;
overflow: hidden;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.modal-close {
font-size: 40rpx;
color: #999;
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
.modal-body {
padding: 30rpx;
}
.region-options {
margin-bottom: 30rpx;
}
.region-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.region-option.active {
background-color: #e3f2fd;
border: 2rpx solid #2196f3;
}
.option-text {
font-size: 28rpx;
color: #333;
}
.option-check {
font-size: 28rpx;
color: #2196f3;
font-weight: bold;
}
.region-picker {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
}
.k-region-container {
background-color: #f8f8f8;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.region-text {
font-size: 28rpx;
color: #333;
}
.region-placeholder {
font-size: 28rpx;
color: #999;
}
.filter-actions {
display: flex;
gap: 20rpx;
}
.reset-btn, .confirm-btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
.reset-btn {
background-color: #f5f5f5;
color: #666;
}
.confirm-btn {
background: linear-gradient(135deg, #FF4444 0%, #FF6666 100%);
color: #fff;
}
</style>