优化在售商品列表(紧凑CSS)+筛选弹窗(出版社/作者去重)+市场竞争统计

This commit is contained in:
97694732@qq.com 2026-06-03 16:15:32 +08:00
parent a5bb2e3b9b
commit 96d575a25c

View File

@ -169,16 +169,16 @@
<text class="stat-value">{{ marketData.onSale }}</text> <text class="stat-value">{{ marketData.onSale }}</text>
</view> </view>
<view class="stat-item"> <view class="stat-item">
<text class="stat-label">旧书</text> <text class="stat-label">最低价</text>
<text class="stat-value">{{ marketData.old }}</text> <text class="stat-value min-price">{{ marketData.minPrice || '-' }}</text>
</view> </view>
<view class="stat-item"> <view class="stat-item">
<text class="stat-label">新书</text> <text class="stat-label">均价</text>
<text class="stat-value">{{ marketData.new }}</text> <text class="stat-value avg-price">{{ marketData.avgPrice || '-' }}</text>
</view> </view>
<view class="stat-item"> <view class="stat-item">
<text class="stat-label">已售</text> <text class="stat-label">不同店铺</text>
<text class="stat-value">{{ marketData.sold }}</text> <text class="stat-value">{{ marketData.shops || 0 }}</text>
</view> </view>
</view> </view>
</view> </view>
@ -615,7 +615,7 @@
<view class="filter-popup" v-if="showFilterPopup" @click="showFilterPopup = false"> <view class="filter-popup" v-if="showFilterPopup" @click="showFilterPopup = false">
<view class="popup-content" @click.stop> <view class="popup-content" @click.stop>
<view class="popup-header"> <view class="popup-header">
<text class="popup-title">筛选条件</text> <text class="popup-title">筛选在售商品</text>
<text class="popup-close" @click="showFilterPopup = false"></text> <text class="popup-close" @click="showFilterPopup = false"></text>
</view> </view>
<view class="popup-body"> <view class="popup-body">
@ -625,9 +625,37 @@
<view <view
class="tag-item" class="tag-item"
v-for="(item, index) in conditionList" v-for="(item, index) in conditionList"
:key="index" :key="'c'+index"
:class="{ active: filterCondition === item }" :class="{ active: filterCondition === item }"
@click="filterCondition = item" @click="filterCondition = filterCondition === item ? '' : item"
>
<text class="tag-text">{{ item }}</text>
</view>
</view>
</view>
<view class="filter-group" v-if="filterPublishers.length > 0">
<text class="group-title">出版社</text>
<view class="tag-list">
<view
class="tag-item"
v-for="(item, index) in filterPublishers"
:key="'p'+index"
:class="{ active: filterPress === item }"
@click="filterPress = filterPress === item ? '' : item"
>
<text class="tag-text">{{ item }}</text>
</view>
</view>
</view>
<view class="filter-group" v-if="filterAuthors.length > 0">
<text class="group-title">作者</text>
<view class="tag-list">
<view
class="tag-item"
v-for="(item, index) in filterAuthors"
:key="'a'+index"
:class="{ active: filterAuthor === item }"
@click="filterAuthor = filterAuthor === item ? '' : item"
> >
<text class="tag-text">{{ item }}</text> <text class="tag-text">{{ item }}</text>
</view> </view>
@ -746,7 +774,7 @@ export default {
photoList: [], photoList: [],
isbnSelectedArea: '', isbnSelectedArea: '',
isbnWarehouseData: null, isbnWarehouseData: null,
marketData: { onSale: 0, old: 0, new: 0, sold: 0 }, marketData: { onSale: 0, minPrice: '-', avgPrice: '-', shops: 0 },
productList: [], productList: [],
compareType: 'isbn', compareType: 'isbn',
sortBy: 'total', sortBy: 'total',
@ -778,6 +806,8 @@ export default {
// //
showFilterPopup: false, showFilterPopup: false,
filterCondition: '', filterCondition: '',
filterPress: '',
filterAuthor: '',
// //
showWarehousePicker: false, showWarehousePicker: false,
@ -847,14 +877,39 @@ export default {
computed: { computed: {
sortedProductList() { sortedProductList() {
const list = [...this.productList] let list = [...this.productList]
//
if (this.filterCondition) {
list = list.filter(item => item.condition === this.filterCondition)
}
if (this.filterPress) {
list = list.filter(item => item.shopName === this.filterPress)
}
if (this.filterAuthor) {
list = list.filter(item => item.author === this.filterAuthor)
}
//
if (this.sortBy === 'total') { if (this.sortBy === 'total') {
list.sort((a, b) => parseFloat(a.totalPrice || 0) - parseFloat(b.totalPrice || 0)) list.sort((a, b) => parseFloat(a.totalPrice) - parseFloat(b.totalPrice))
} else if (this.sortBy === 'book') { } else if (this.sortBy === 'book') {
list.sort((a, b) => parseFloat(a.bookPrice || 0) - parseFloat(b.bookPrice || 0)) list.sort((a, b) => parseFloat(a.bookPrice || 0) - parseFloat(b.bookPrice || 0))
} }
return list return list
}, },
filterPublishers() {
const set = new Set()
this.productList.slice(0, 12).forEach(item => {
if (item.shopName) set.add(item.shopName)
})
return Array.from(set)
},
filterAuthors() {
const set = new Set()
this.productList.slice(0, 12).forEach(item => {
if (item.author) set.add(item.author)
})
return Array.from(set)
},
lowestOptions() { lowestOptions() {
const arr = [] const arr = []
for (let i = 1; i <= 12; i++) arr.push(i) for (let i = 1; i <= 12; i++) arr.push(i)
@ -941,20 +996,13 @@ export default {
searchProducts(this.isbn, { phpsessid }).then(data => { searchProducts(this.isbn, { phpsessid }).then(data => {
this.isLoading = false this.isLoading = false
if (data && data.total > 0) { if (data && data.total > 0) {
//
this.marketData = {
onSale: data.total || 0,
old: 0,
new: 0,
sold: 0
}
// 12 // 12
const list = (data.list || []).slice(0, 12) const list = (data.list || []).slice(0, 12)
this.productList = list.map(item => ({ this.productList = list.map(item => ({
image: item.imgBigUrl || '', image: item.imgBigUrl || '',
totalPrice: item.priceText || '', totalPrice: parseFloat((item.priceText || '0').replace(/[^\d.]/g, '')),
bookPrice: (item.priceText || '0').replace(/[^\d.]/g, ''), bookPrice: parseFloat((item.priceText || '0').replace(/[^\d.]/g, '')),
shippingFee: item.postage && item.postage.shippingList && item.postage.shippingList.length > 0 ? item.postage.shippingList[0].shippingFee || '0' : '0', shippingFee: item.postage && item.postage.shippingList && item.postage.shippingList.length > 0 ? parseFloat(item.postage.shippingList[0].shippingFee || 0) : 0,
condition: item.qualityText || '', condition: item.qualityText || '',
shopName: item.shopName || '', shopName: item.shopName || '',
bookName: item.title || '', bookName: item.title || '',
@ -962,8 +1010,19 @@ export default {
pubDate: item.pubDateText || '', pubDate: item.pubDateText || '',
bookId: item.id || '' bookId: item.id || ''
})) }))
//
const prices = this.productList.map(p => p.totalPrice).filter(p => p && p > 0)
const avg = prices.length > 0 ? (prices.reduce((a, b) => a + b, 0) / prices.length) : 0
const min = prices.length > 0 ? Math.min(...prices) : 0
const shopSet = new Set(list.map(item => item.shopName).filter(Boolean))
this.marketData = {
onSale: data.total || 0,
minPrice: min > 0 ? '¥' + min.toFixed(2) : '-',
avgPrice: avg > 0 ? '¥' + avg.toFixed(2) : '-',
shops: shopSet.size || 0
}
} else { } else {
this.marketData = { onSale: 0, old: 0, new: 0, sold: 0 } this.marketData = { onSale: 0, minPrice: '-', avgPrice: '-', shops: 0 }
} }
}).catch(() => { }).catch(() => {
this.isLoading = false this.isLoading = false
@ -1178,6 +1237,8 @@ export default {
resetFilter() { resetFilter() {
this.filterCondition = '' this.filterCondition = ''
this.filterPress = ''
this.filterAuthor = ''
}, },
applyFilter() { applyFilter() {
@ -1735,7 +1796,7 @@ export default {
background-color: #fafafa; background-color: #fafafa;
border: 2rpx solid #ebeef5; border: 2rpx solid #ebeef5;
border-radius: 8rpx; border-radius: 8rpx;
padding: 18rpx 8rpx; padding: 14rpx 6rpx;
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
} }
@ -1744,20 +1805,28 @@ export default {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 6rpx; gap: 4rpx;
} }
.stat-label { .stat-label {
font-size: 24rpx; font-size: 22rpx;
color: #909399; color: #909399;
} }
.stat-value { .stat-value {
font-size: 32rpx; font-size: 28rpx;
color: #303133; color: #303133;
font-weight: 600; font-weight: 600;
} }
.stat-value.min-price {
color: #f56c6c;
}
.stat-value.avg-price {
color: #e6a23c;
}
/* ========== 在售商品 ========== */ /* ========== 在售商品 ========== */
.section-header-row { .section-header-row {
display: flex; display: flex;
@ -1847,42 +1916,44 @@ export default {
.product-grid { .product-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 10rpx; gap: 8rpx;
margin-top: 14rpx; margin-top: 10rpx;
} }
.grid-item { .grid-item {
background-color: #fafafa; background-color: #fafafa;
border: 2rpx solid #ebeef5; border: 2rpx solid #ebeef5;
border-radius: 8rpx; border-radius: 8rpx;
padding: 10rpx; padding: 6rpx;
overflow: hidden;
} }
.grid-image { .grid-image {
width: 100%; width: 100%;
height: 150rpx; height: 120rpx;
border-radius: 6rpx; border-radius: 4rpx;
} }
.grid-total-price { .grid-total-price {
font-size: 22rpx; font-size: 20rpx;
color: #f56c6c; color: #f56c6c;
font-weight: 600; font-weight: 600;
display: block; display: block;
margin-top: 4rpx;
text-align: center; text-align: center;
width: 100%; margin-top: 2rpx;
line-height: 1.4;
} }
.grid-price-detail { .grid-price-detail {
font-size: 18rpx; font-size: 16rpx;
color: #909399; color: #909399;
display: block; display: block;
text-align: center; text-align: center;
line-height: 1.3;
} }
.grid-book-name { .grid-book-name {
font-size: 24rpx; font-size: 22rpx;
color: #303133; color: #303133;
font-weight: 500; font-weight: 500;
display: block; display: block;
@ -1891,10 +1962,11 @@ export default {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
line-height: 1.3;
} }
.grid-author { .grid-author {
font-size: 20rpx; font-size: 18rpx;
color: #909399; color: #909399;
display: block; display: block;
text-align: center; text-align: center;
@ -1902,24 +1974,26 @@ export default {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
margin-top: 2rpx; line-height: 1.3;
} }
.grid-condition { .grid-condition {
font-size: 22rpx; font-size: 20rpx;
color: #606266; color: #606266;
display: block; display: block;
margin-top: 4rpx;
text-align: center; text-align: center;
line-height: 1.3;
} }
.grid-shop { .grid-shop {
font-size: 20rpx; font-size: 18rpx;
color: #909399; color: #909399;
display: block; display: block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
text-align: center;
line-height: 1.3;
} }
.no-data { .no-data {