552 lines
15 KiB
Vue
552 lines
15 KiB
Vue
<template>
|
||
<view>
|
||
<!-- 筛选按钮 -->
|
||
<view class="button-group">
|
||
<u-popup :show="popupShow" @close="close" @open="open" mode="bottom" round="10">
|
||
<view class="filter-popup">
|
||
<view class="filter-section">
|
||
<view class="section-title">出版社</view>
|
||
<view class="tag-group">
|
||
<u-tag v-for="(item, index) in publisherOptions" :key="index" v-if="item.visible"
|
||
:text="item.publisher" :plain="!item.checked" size="large" :name="index"
|
||
@click="togglePublisherFilter(index)" />
|
||
</view>
|
||
</view>
|
||
<view class="filter-section">
|
||
<view class="section-title">作者</view>
|
||
<view class="tag-group">
|
||
<u-tag v-for="(item, index) in authorOptions" :key="index" v-if="item.visible"
|
||
:text="item.author" :plain="!item.checked" size="large" :name="index"
|
||
@click="toggleAuthorFilter(index)" />
|
||
</view>
|
||
</view>
|
||
<view class="filter-buttons">
|
||
<u-button text="重置" type="info" plain @click="resetFilters" />
|
||
<u-button text="确定" type="primary" @click="applyFilters" />
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
|
||
<view class="book-list">
|
||
<view class="label">
|
||
<view style="display: inline-block;width: 380rpx;text-align: left;">
|
||
在售商品(总价/书价/运费)
|
||
</view>
|
||
|
||
<view class="btn" @click="onSwitchCompareType">
|
||
{{ compareType === 'isbn' ? '书名比价' : 'ISBN比价' }}
|
||
</view>
|
||
<view class="btn" @click="popupShow = true">更多筛序</view>
|
||
</view>
|
||
<!-- 在售商品展示区域 -->
|
||
<view class="container" v-if="(isFiltered ? filteredOnSaleProducts : displayOnSaleProducts).length > 0">
|
||
<view class="product-grid">
|
||
<view class="product-card"
|
||
v-for="(product, index) in isFiltered ? filteredOnSaleProducts : displayOnSaleProducts"
|
||
:key="index">
|
||
<view class="product-image-container">
|
||
<image class="product-image" :src="product.imageUrl" mode="aspectFit"
|
||
@click="previewImage(product.imageUrl, isFiltered ? filteredOnSaleProducts : displayOnSaleProducts)">
|
||
</image>
|
||
<view class="price-details">
|
||
<text>{{product.totalPrice}}/{{product.bookPrice}}/{{product.shippingFee}}</text>
|
||
</view>
|
||
</view>
|
||
<view class="product-info">
|
||
<text class="product-price"
|
||
style="font-size: 26rpx;">{{product.totalPrice}}/{{product.qualityText}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'book-filter-display',
|
||
props: {
|
||
// 接收父组件传递的数据
|
||
onSaleProducts: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
compareType: {
|
||
type: String,
|
||
default: 'isbn'
|
||
},
|
||
publisherAuthorMap: {
|
||
type: Map,
|
||
default: () => new Map()
|
||
},
|
||
authorPublisherMap: {
|
||
type: Map,
|
||
default: () => new Map()
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
popupShow: false,
|
||
isFiltered: false,
|
||
publisherOptions: [],
|
||
authorOptions: [],
|
||
displayOnSaleProducts: [],
|
||
filteredOnSaleProducts: []
|
||
};
|
||
},
|
||
watch: {
|
||
onSaleProducts: {
|
||
handler(newProducts) {
|
||
if (newProducts && newProducts.length > 0) {
|
||
// 对商品数据按照总价从低到高排序
|
||
const sortedData = [...newProducts].sort((a, b) => {
|
||
const priceA = parseFloat(a.totalPrice) || 0;
|
||
const priceB = parseFloat(b.totalPrice) || 0;
|
||
return priceA - priceB; // 从低到高排序
|
||
});
|
||
|
||
// 只保留前12条数据用于展示
|
||
this.displayOnSaleProducts = sortedData.slice(0, 12);
|
||
|
||
// 提取出版社和作者信息
|
||
this.extractPublishersAndAuthors();
|
||
} else {
|
||
this.displayOnSaleProducts = [];
|
||
}
|
||
},
|
||
immediate: true
|
||
}
|
||
},
|
||
methods: {
|
||
open() {
|
||
this.popupShow = true;
|
||
this.$emit('popup-open');
|
||
},
|
||
close() {
|
||
this.popupShow = false;
|
||
this.$emit('popup-close');
|
||
},
|
||
onSwitchCompareType() {
|
||
const newType = this.compareType === 'isbn' ? 'title' : 'isbn';
|
||
this.$emit('switch-compare-type', newType);
|
||
},
|
||
previewImage(currentUrl, productList) {
|
||
// 检查productList是否存在且是数组
|
||
let urls = [];
|
||
|
||
if (productList && Array.isArray(productList)) {
|
||
// 提取所有图片 URL
|
||
urls = productList.map(item => item.imageUrl);
|
||
} else {
|
||
// 如果productList不存在或不是数组,则只使用当前URL
|
||
urls = [currentUrl];
|
||
}
|
||
|
||
// 确保urls中至少有一个有效的URL
|
||
if (!urls.length || !urls[0]) {
|
||
console.error('没有有效的图片URL可预览');
|
||
return;
|
||
}
|
||
|
||
// 调用 UniApp 预览接口,添加长按操作
|
||
uni.previewImage({
|
||
current: currentUrl, // 当前显示图片
|
||
urls: urls, // 所有图片列表
|
||
longPressActions: {
|
||
itemList: ['保存图片', '分享图片'],
|
||
success: function(data) {
|
||
console.log('选择了第' + (data.tapIndex + 1) + '个按钮');
|
||
},
|
||
fail: function(err) {
|
||
console.log(err.errMsg);
|
||
}
|
||
},
|
||
success: () => {},
|
||
fail: (err) => {
|
||
console.error('预览失败:', err);
|
||
}
|
||
});
|
||
},
|
||
// 从商品数据中提取出版社和作者信息
|
||
extractPublishersAndAuthors() {
|
||
// 提取出版社
|
||
const publishers = new Set();
|
||
this.onSaleProducts.forEach(product => {
|
||
if (product.publisher) {
|
||
publishers.add(product.publisher);
|
||
}
|
||
});
|
||
|
||
// 提取作者
|
||
const authors = new Set();
|
||
this.onSaleProducts.forEach(product => {
|
||
if (product.author) {
|
||
authors.add(product.author);
|
||
}
|
||
});
|
||
|
||
// 转换为选项数组
|
||
this.publisherOptions = Array.from(publishers).map(publisher => ({
|
||
publisher,
|
||
checked: false,
|
||
visible: true // 添加可见性属性
|
||
}));
|
||
|
||
this.authorOptions = Array.from(authors).map(author => ({
|
||
author,
|
||
checked: false,
|
||
visible: true // 添加可见性属性
|
||
}));
|
||
},
|
||
// 切换出版社筛选
|
||
togglePublisherFilter(index) {
|
||
// 切换选中状态
|
||
this.publisherOptions[index].checked = !this.publisherOptions[index].checked;
|
||
|
||
// 获取所有选中的出版社
|
||
const selectedPublishers = this.publisherOptions
|
||
.filter(item => item.checked)
|
||
.map(item => item.publisher);
|
||
|
||
// 如果没有选中任何出版社,显示所有作者
|
||
if (selectedPublishers.length === 0) {
|
||
this.authorOptions.forEach(item => {
|
||
item.visible = true;
|
||
});
|
||
} else {
|
||
// 获取当前选中的出版社
|
||
const currentPublisher = this.publisherOptions[index].publisher;
|
||
|
||
// 如果取消选中,需要重新计算可见的作者
|
||
if (!this.publisherOptions[index].checked) {
|
||
// 重置所有作者的可见性
|
||
this.authorOptions.forEach(item => {
|
||
item.visible = false;
|
||
});
|
||
|
||
// 根据剩余选中的出版社更新作者可见性
|
||
selectedPublishers.forEach(publisher => {
|
||
if (this.publisherAuthorMap.has(publisher)) {
|
||
const relatedAuthors = this.publisherAuthorMap.get(publisher);
|
||
this.authorOptions.forEach(item => {
|
||
if (relatedAuthors.has(item.author)) {
|
||
item.visible = true;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
} else {
|
||
// 如果是新选中,只显示与该出版社相关的作者
|
||
this.authorOptions.forEach(item => {
|
||
// 检查该作者是否与当前选中的出版社相关
|
||
if (this.publisherAuthorMap.has(currentPublisher)) {
|
||
const relatedAuthors = this.publisherAuthorMap.get(currentPublisher);
|
||
item.visible = relatedAuthors.has(item.author);
|
||
} else {
|
||
item.visible = false;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 取消选中不可见的作者
|
||
this.authorOptions.forEach(item => {
|
||
if (!item.visible) {
|
||
item.checked = false;
|
||
}
|
||
});
|
||
},
|
||
// 切换作者筛选
|
||
toggleAuthorFilter(index) {
|
||
// 切换选中状态
|
||
this.authorOptions[index].checked = !this.authorOptions[index].checked;
|
||
|
||
// 获取所有选中的作者
|
||
const selectedAuthors = this.authorOptions
|
||
.filter(item => item.checked)
|
||
.map(item => item.author);
|
||
|
||
// 如果没有选中任何作者,显示所有出版社
|
||
if (selectedAuthors.length === 0) {
|
||
this.publisherOptions.forEach(item => {
|
||
item.visible = true;
|
||
});
|
||
} else {
|
||
// 获取当前选中的作者
|
||
const currentAuthor = this.authorOptions[index].author;
|
||
|
||
// 如果取消选中,需要重新计算可见的出版社
|
||
if (!this.authorOptions[index].checked) {
|
||
// 重置所有出版社的可见性
|
||
this.publisherOptions.forEach(item => {
|
||
item.visible = false;
|
||
});
|
||
|
||
// 根据剩余选中的作者更新出版社可见性
|
||
selectedAuthors.forEach(author => {
|
||
if (this.authorPublisherMap.has(author)) {
|
||
const relatedPublishers = this.authorPublisherMap.get(author);
|
||
this.publisherOptions.forEach(item => {
|
||
if (relatedPublishers.has(item.publisher)) {
|
||
item.visible = true;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
} else {
|
||
// 如果是新选中,只显示与该作者相关的出版社
|
||
this.publisherOptions.forEach(item => {
|
||
// 检查该出版社是否与当前选中的作者相关
|
||
if (this.authorPublisherMap.has(currentAuthor)) {
|
||
const relatedPublishers = this.authorPublisherMap.get(currentAuthor);
|
||
item.visible = relatedPublishers.has(item.publisher);
|
||
} else {
|
||
item.visible = false;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 取消选中不可见的出版社
|
||
this.publisherOptions.forEach(item => {
|
||
if (!item.visible) {
|
||
item.checked = false;
|
||
}
|
||
});
|
||
},
|
||
// 重置筛选
|
||
resetFilters() {
|
||
// 重置出版社选项
|
||
this.publisherOptions.forEach(item => {
|
||
item.checked = false;
|
||
item.visible = true; // 确保所有出版社选项可见
|
||
});
|
||
|
||
// 重置作者选项
|
||
this.authorOptions.forEach(item => {
|
||
item.checked = false;
|
||
item.visible = true; // 确保所有作者选项可见
|
||
});
|
||
|
||
// 重置筛选状态
|
||
this.isFiltered = false;
|
||
|
||
// 恢复排序后的前十条数据
|
||
this.filteredOnSaleProducts = [];
|
||
|
||
// 关闭弹窗
|
||
this.popupShow = false;
|
||
|
||
// 触发重置事件
|
||
this.$emit('reset-filters');
|
||
},
|
||
// 应用筛选
|
||
applyFilters() {
|
||
// 获取选中的出版社
|
||
const selectedPublishers = this.publisherOptions
|
||
.filter(item => item.checked)
|
||
.map(item => item.publisher);
|
||
|
||
// 获取选中的作者
|
||
const selectedAuthors = this.authorOptions
|
||
.filter(item => item.checked)
|
||
.map(item => item.author);
|
||
|
||
// 如果没有选择任何筛选条件,则显示排序后的前十条数据
|
||
if (selectedPublishers.length === 0 && selectedAuthors.length === 0) {
|
||
this.filteredOnSaleProducts = [];
|
||
this.isFiltered = false;
|
||
} else {
|
||
// 筛选在售商品
|
||
let filteredProducts = this.onSaleProducts.filter(product => {
|
||
const matchPublisher = selectedPublishers.length === 0 ||
|
||
(product.publisher && selectedPublishers.includes(product.publisher));
|
||
const matchAuthor = selectedAuthors.length === 0 ||
|
||
(product.author && selectedAuthors.includes(product.author));
|
||
return matchPublisher && matchAuthor;
|
||
});
|
||
|
||
// 对筛选后的结果按总价从低到高排序
|
||
filteredProducts = filteredProducts.sort((a, b) => {
|
||
const priceA = parseFloat(a.totalPrice) || 0;
|
||
const priceB = parseFloat(b.totalPrice) || 0;
|
||
return priceA - priceB; // 从低到高排序
|
||
});
|
||
|
||
// 只保留前10条数据
|
||
this.filteredOnSaleProducts = filteredProducts.slice(0, 10);
|
||
this.isFiltered = true;
|
||
}
|
||
|
||
// 关闭弹窗
|
||
this.popupShow = false;
|
||
|
||
// 触发应用筛选事件
|
||
this.$emit('apply-filters', {
|
||
filteredProducts: this.filteredOnSaleProducts,
|
||
selectedPublishers,
|
||
selectedAuthors
|
||
});
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.book-list {
|
||
margin-top: 20rpx;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.book-list>.label {
|
||
padding: 10rpx;
|
||
display: block;
|
||
width: calc(100% - 20rpx);
|
||
height: 40rpx;
|
||
color: #333;
|
||
border-bottom: 2rpx solid #ccc;
|
||
}
|
||
|
||
.book-list>.label>.btn {
|
||
display: inline-block;
|
||
padding: 10rpx;
|
||
text-align: center;
|
||
width: 120rpx;
|
||
height: 30rpx;
|
||
line-height: 30rpx;
|
||
color: #fff;
|
||
background-color: #3c9cff;
|
||
border-radius: 10%;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.book-list>.container {
|
||
position: relative;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.book-list>.container>.product-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
@media screen and (max-width: 768rpx) {
|
||
.book-list>.container>.product-grid>.product-card {
|
||
width: calc(25% - 7rpx);
|
||
}
|
||
}
|
||
|
||
@media screen and (max-width: 480rpx) {
|
||
.book-list>.container>.product-grid>.product-card {
|
||
width: calc(50% - 5rpx);
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image {
|
||
height: 200rpx;
|
||
}
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card {
|
||
width: calc(25% - 8rpx);
|
||
background-color: #ffffff;
|
||
border-radius: 12rpx;
|
||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||
overflow: hidden;
|
||
flex-shrink: 0;
|
||
height: 180rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 140rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image-container>.product-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
flex: 0 0 auto;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image-container>.product-info {
|
||
padding: 8rpx;
|
||
font-size: 24rpx;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-align: center;
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image-container>.product-info>.product-price {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #ff6b00;
|
||
display: block;
|
||
margin-bottom: 4rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.book-list>.container>.product-grid>.product-card>.product-image-container>.price-details {
|
||
font-size: 22rpx;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
color: #ffffff;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
padding: 4rpx 0;
|
||
border-radius: 0 0 8rpx 8rpx;
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.button-group {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
margin: 10rpx 0;
|
||
}
|
||
|
||
.filter-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.tag-group {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.filter-popup {
|
||
padding: 30rpx;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.filter-buttons {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 40rpx;
|
||
}
|
||
</style> |