爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2412 lines
71 KiB

<template>
<view class="container">
<!-- 顶部banner -->
<banner-swiper :banner-list="bannerList" height="320rpx" :fallback-image="recycle_banner"
video-id-prefix="recycle-video"></banner-swiper>
<!-- 商品列表 -->
<view class="goods-list">
<!-- 左侧分类导航 -->
<view class="category-nav">
<view v-for="(category, index) in categories" :key="category.id || index" class="category-item"
:class="{ active: currentCategory === index }" @click="switchCategory(index)">
<view class="category-dot" v-if="getCategoryItemCount(index) > 0">{{ getCategoryItemCount(index) }}</view>
{{ category.title }}
</view>
</view>
<!-- 右侧商品列表 -->
<scroll-view class="goods-content" scroll-y @scrolltolower="loadMoreGoods">
<view class="goods-section">
<view class="goods-item" v-for="(item, index) in recycleList" :key="index">
<view class="goods-img-container">
<image v-if="item.image" :src="item.image" class="goods-item-img" mode="aspectFit" />
<!-- 品牌标签 -->
<view class="brand-tag" v-if="item.isPin === 'Y'">品牌</view>
</view>
<view class="goods-info-wrap">
<view class="goods-header">
<text class="goods-name">{{ item.name }}</text>
</view>
<text class="goods-desc">{{ item.service }}</text>
<view class="rules-brand-row">
<view class="rules-link" @click="showRules(item)" v-if="item.isRecycleRules == '1'">
<view class="rules">
<text>回收规则</text>
<uni-icons type="right" size="14" color="#999"></uni-icons>
</view>
</view>
<!-- <view class="brand-check-placeholder" v-if="item.isPin === 'Y'">
<view class="brand-check" @click="checkBrand(index)">
<text>查看品牌</text>
<uni-icons type="right" size="12" color="#ff7a0e"></uni-icons>
</view>
</view> -->
</view>
<view class="goods-info">
<view class="price-info">
<text class="price-symbol">¥</text>
<text class="price-value" v-if="!item.maxPrice || item.maxPrice == item.price">{{ item.price }}</text>
<text class="price-value" v-else>{{ item.price }}-{{ item.maxPrice }}</text>
<text class="price-unit">/{{ item.unit || '件' }}</text>
</view>
<view class="quantity-control">
<button class="btn-minus" @click="updateQuantity(index, -1)">-</button>
<text class="quantity">{{ getItemTotalQuantity(item) }}</text>
<button class="btn-plus" @click="updateQuantity(index, 1)">+</button>
</view>
</view>
</view>
</view>
</view>
<!-- 不可回收商品 -->
<view class="other-unrecycle-card">
<!-- 使用商品分类数据中的icon -->
<image class="other-unrecycle-img" :src="categories[currentCategory]?.icon" mode="aspectFit" />
<view class="other-unrecycle-info">
<!-- 主标题和副标题用接口返回的otherTitle和otherSubTitle -->
<view class="other-unrecycle-title">{{ categories[currentCategory]?.otherTitle }}</view>
<view class="other-unrecycle-desc">{{ categories[currentCategory]?.otherSubTitle }}</view>
<view class="other-unrecycle-price-row">
<text class="other-unrecycle-price">¥ — /{{ categories[currentCategory]?.unit || '件' }}</text>
</view>
</view>
<button class="other-unrecycle-btn" open-type="contact">+</button>
</view>
<view v-if="loadingMore" class="loading-more">加载中...</view>
<view v-else-if="finished" class="loading-more">没有更多了</view>
</scroll-view>
</view>
<!-- 固定底部区域 -->
<view class="fixed-bottom-wrap" v-if="!showDetailPanel">
<view class="green-tip-bar">
回收范围仅支持回收以上品类,按件回收预计比称重回收多
<text class="tip-highlight"> {{ extraRecycleAmount.display }}</text>
</view>
<view class="bottom-bar">
<view class="bottom-left">
<view class="summary-row">
<text class="summary-label">已选 <text class="summary-count">{{ totalCount }}</text> {{ totalUnitText }}
预计回收可得</text>
<uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" @tap="showPriceInfoPopups" />
</view>
<view class="amount-row" @click="toggleDetailPanel">
<uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e"
style="margin-right: 8rpx;vertical-align: middle;" />
<text class="amount" v-if="priceRange.min === priceRange.max">¥{{ priceRange.min }}</text>
<text class="amount" v-else>¥{{ priceRange.min }}-{{ priceRange.max }}</text>
</view>
</view>
<button class="submit-btn" @click="submitOrder">预约上门取件</button>
</view>
<view class="bottom-bar-divider"></view>
</view>
<!-- 明细弹窗遮罩和弹窗 -->
<view v-if="showDetailPanel" class="detail-popup-mask" @click.self="toggleDetailPanel">
<view class="detail-popup" @click.stop>
<view class="detail-popup-close" @click="toggleDetailPanel">×</view>
<view class="green-tip-bar popup-green-tip">
回收范围仅支持回收以上品类,按件回收预计比称重回收多
<text class="tip-highlight"> {{ extraRecycleAmount.display }}</text>
</view>
<view class="panel-header">
<text class="panel-title">已选商品明细</text>
</view>
<scroll-view class="panel-list popup-panel-list" scroll-y>
<!-- @click="openProductDetail(item)" -->
<view v-for="(item, idx) in selectedProducts" :key="item.uniqueKey || idx"
class="panel-item">
<view class="panel-img-container">
<image v-if="item.styleImage || item.image" :src="item.styleImage || item.image" class="panel-item-img" mode="aspectFit" />
<!-- 品牌标签 -->
<view class="panel-brand-tag" v-if="item.brandId">品牌</view>
</view>
<view class="panel-item-info">
<!-- <text class="panel-item-name">{{ item.name }}</text> -->
<!-- <text class="panel-item-desc" v-if="item.brandName && item.styleName">品牌:{{ item.brandName }} | 款式:{{ item.styleName }}</text> -->
<text class="panel-item-name">{{ item.brandName || item.name }}</text>
<text class="panel-item-desc" v-if="item.styleName">{{ item.styleName }}</text>
<text class="panel-item-desc" v-else>{{ item.service }}</text>
<text class="panel-item-price" v-if="!item.maxPrice || item.maxPrice == item.price">¥{{ item.price }}/{{
item.unit || '件' }}</text>
<text class="panel-item-price" v-else>¥{{ item.price }}-{{ item.maxPrice }}/{{ item.unit || '件' }}</text>
</view>
<view class="panel-quantity-control">
<button class="btn-minus" @click.stop="updateQuantityByProduct(item, -1)">-</button>
<!-- <button class="btn-minus">-</button> -->
<text class="quantity">{{ item.quantity }}</text>
<button class="btn-plus" @click.stop="updateQuantityByProduct(item, 1)">+</button>
<!-- <button class="btn-plus">+</button> -->
</view>
</view>
</scroll-view>
<view class="popup-bottom-bar">
<view class="bottom-left">
<view class="summary-row">
<text class="summary-label">已选 <text class="summary-count">{{ totalCount }}</text> {{ totalUnitText }}
预计回收可得</text>
<uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" @tap="showPriceInfoPopups" />
</view>
<view class="amount-row" @click="toggleDetailPanel">
<uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e"
style="margin-right: 8rpx;vertical-align: middle;" />
<text class="amount" v-if="priceRange.min === priceRange.max">¥{{ priceRange.min }}</text>
<text class="amount" v-else>¥{{ priceRange.min }}-{{ priceRange.max }}</text>
</view>
</view>
<button class="submit-btn" @click="submitOrder">预约上门取件</button>
</view>
<!-- 添加衣物浮窗按钮 -->
<view class="floating-add-btn" @click="addMoreItems">
<text class="floating-btn-text">添加衣物</text>
</view>
</view>
</view>
<!-- 价格说明弹窗 -->
<view v-if="showPriceInfoPopup" class="price-info-popup-mask" @click.self="closePriceInfoPopup">
<view class="price-info-popup">
<view class="price-info-popup-title">回收规则</view>
<scroll-view class="price-info-popup-content" scroll-y>
<!-- <view class="price-info-section">
<view class="price-info-heading">关于旧衣质检</view>
<view class="price-info-text">请确认本次回收旧衣是可以进行二次穿着的程度,如回收旧衣有破损磨损、开线变形、起球发黄、染色污渍、配饰脱落或款式老旧等问题,无法通过质检。</view>
</view>
<view class="price-info-section">
<view class="price-info-heading">质检报告</view>
<view class="price-info-text">回收商收到衣后1-3个工作日内完成衣质检报告。</view>
</view>
<view class="price-info-section">
<view class="price-info-heading">质检结果与回收价格</view>
<view class="price-info-text">若回收旧衣质检通过,质检价格与用户提交订单时的预估价格一致,回收商将按照预估价打款至您的小程序账户余额。</view>
</view> -->
<uv-parse :content="huodong_text"></uv-parse>
</scroll-view>
<button class="price-info-popup-btn" @click="closePriceInfoPopup">我知道了</button>
</view>
</view>
<!-- 根据角色显示不同的导航栏 -->
<tabbar v-if="ishow" select="recycle"></tabbar>
<!-- 品牌选择组件 -->
<brand-selector ref="brandSelector" @brand-confirm="onBrandConfirm" @reduce-select="onReduceSelect"
@close="onBrandSelectorClose" @get-existing-quantities="getExistingQuantities"></brand-selector>
<!-- 规则弹窗组件 -->
<rule-popup ref="rulePopup" :confirm-content="recycle_toast" @rule-confirm="onRuleConfirm"
@pickup-cancel="handlePickupCancel" @pickup-confirm="handlePickupAgree"></rule-popup>
<!-- 商品明细弹窗组件 -->
<product-detail-popup ref="productDetailPopup" @confirm-changes="onDetailConfirmChanges" @close="onDetailPopupClose"></product-detail-popup>
</view>
</template>
<script>
import tabBarMixin from '../mixins/tabBarMixin.js'
import { pinyin } from '../../utils/pinyin.js'
import tabbar from '../../compoent/base/tabbar.vue'
import brandSelector from '../../compoent/recycle/brand-selector.vue'
import bannerSwiper from '../../compoent/base/banner-swiper.vue'
import rulePopup from '../../compoent/base/rule-popup.vue'
import productDetailPopup from '../../compoent/recycle/product-detail-popup.vue'
export default {
mixins: [tabBarMixin],
components: {
tabbar,
brandSelector,
bannerSwiper,
rulePopup,
productDetailPopup
},
data() {
return {
value: 1,
ishow: true,
// 动态数据
allProducts: {}, // { [categoryId]: [商品数组] }
allProductsPage: {}, // { [categoryId]: 当前已加载页码 }
allProductsTotal: {}, // { [categoryId]: 总数 }
pageSize: 10,
currentCategory: 0,
tabbarHeight: 0,
showDetailPanel: false,
ruleImgUrl: '/static/回收/回收规则.png',
brandCache: {}, // 为每个商品缓存品牌信息 { productId: [brandList] }
loadingMore: false,
finished: false,
pendingBrandIndex: null, // 记录待加一的品牌商品index
showPriceInfoPopup: false,
isWaitingForBrandSelection: false, // 等待品牌选择的标志
reduceItem: null, // 待减少数量的商品
viewedRuleItems: new Set(), // 已查看过规则的商品ID集合
loadOptions: null, // 保存options参数
userInfo: null, // 用户信息
isUserBlacklisted: false, // 用户是否被拉黑
currentProductId: null, // 当前查看品牌的商品ID
searchTimer: null, // 搜索防抖定时器
isFromPickupFlow: false // 标记是否来自预约流程
}
},
computed: {
recycle_banner() {
const item = getApp().globalData.configData.find(i => i.keyName === 'recycle_banner')
return item ? item.keyContent : ''
},
re_key_numer() {
const item = getApp().globalData.configData.find(i => i.keyName === 're_key_numer')
return item ? parseFloat(item.keyContent) : 0.066
},
recycle_toast() {
const item = getApp().globalData.configData.find(i => i.keyName === 'recycle_toast')
return item ? item.keyContent : ''
},
huodong_text() {
const item = getApp().globalData.configData.find(i => i.keyName === 'huodong_text')
return item ? item.keyContent : ''
},
// 当前分类的商品列表
recycleList() {
const currentCategoryId = this.categories[this.currentCategory]?.id
return this.allProducts[currentCategoryId] || []
},
// 计算总数量
totalCount() {
return Object.values(this.allProducts).reduce((total, categoryItems) => {
return total + categoryItems.reduce((sum, item) => {
// 如果商品有品牌款式数量,汇总所有品牌款式的数量
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
return sum + Object.values(item.brandStyleQuantities).reduce((styleSum, qty) => styleSum + qty, 0)
}
// 如果商品有品牌数量,汇总所有品牌的数量
if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
return sum + Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
}
// 否则使用原来的quantity字段
return sum + (item.quantity || 0)
}, 0)
}, 0)
},
// 计算总单位文本
totalUnitText() {
// 获取所有已选商品的单位
const units = new Set()
Object.values(this.allProducts).forEach(categoryItems => {
categoryItems.forEach(item => {
let hasQuantity = false
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
hasQuantity = Object.values(item.brandStyleQuantities).some(qty => qty > 0)
} else if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
hasQuantity = Object.values(item.brandQuantities).some(qty => qty > 0)
} else {
hasQuantity = (item.quantity || 0) > 0
}
if (hasQuantity) {
units.add(item.unit || '件')
}
})
})
// 如果只有一种单位,显示单位;如果混合多种单位,显示"项"
if (units.size === 0) return '件'
if (units.size === 1) return Array.from(units)[0]
return '项'
},
// 计算总价格范围
totalPriceRange() {
const result = Object.values(this.allProducts).reduce((categoryTotal, categoryItems) => {
return categoryItems.reduce((sum, item) => {
// 如果商品有品牌款式数量,使用款式价格计算
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
Object.entries(item.brandStyleQuantities).forEach(([uniqueKey, quantity]) => {
if (quantity > 0 && item.styleCache && item.styleCache[uniqueKey]) {
const styleInfo = item.styleCache[uniqueKey].styleInfo
const minPrice = Number(styleInfo.minPrice) || 0
const maxPrice = Number(styleInfo.maxPrice) || Number(styleInfo.minPrice) || 0
sum.min += quantity * minPrice
sum.max += quantity * maxPrice
}
})
} else {
let itemQuantity = 0
// 如果商品有品牌数量,汇总所有品牌的数量
if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
itemQuantity = Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
} else {
itemQuantity = item.quantity || 0
}
if (itemQuantity > 0) {
const minPrice = Number(item.price) || 0
const maxPrice = Number(item.maxPrice) || Number(item.price) || 0
sum.min += itemQuantity * minPrice
sum.max += itemQuantity * maxPrice
}
}
return sum
}, categoryTotal)
}, { min: 0, max: 0 })
return {
min: result.min.toFixed(1),
max: result.max.toFixed(1)
}
},
// 计算总价格 (保持兼容性,使用最低价格)
totalPrice() {
return this.totalPriceRange.min
},
// 计算价格范围
priceRange() {
if (this.totalCount === 0) {
return {
min: '0.0',
max: '0.0'
}
}
return this.totalPriceRange
},
selectedProducts() {
// 返回所有分类下所有已选商品,按品牌款式分组
const products = []
Object.values(this.allProducts).flat().forEach(item => {
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
// 按品牌款式分别添加
Object.entries(item.brandStyleQuantities).forEach(([uniqueKey, quantity]) => {
if (quantity > 0 && item.styleCache && item.styleCache[uniqueKey]) {
const { brandInfo, styleInfo } = item.styleCache[uniqueKey]
products.push({
...item,
quantity: quantity,
brandId: brandInfo.id,
brandName: brandInfo.name,
brandImage: brandInfo.logo,
styleId: styleInfo.id,
styleName: styleInfo.name,
styleImage: styleInfo.image,
price: styleInfo.minPrice, // 使用款式价格
maxPrice: styleInfo.maxPrice,
name: item.name, // 保持原商品名称
uniqueKey: `${item.id}_${uniqueKey}` // 用于区分同商品不同品牌款式
})
}
})
} else if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
// 按品牌分别添加(兼容旧数据)
Object.entries(item.brandQuantities).forEach(([brandId, quantity]) => {
if (quantity > 0) {
const brandInfo = this.getBrandInfo(brandId)
products.push({
...item,
quantity: quantity,
brandId: brandId,
brandName: brandInfo ? brandInfo.name : '未知品牌',
brandImage: brandInfo ? brandInfo.logo : '',
uniqueKey: `${item.id}_${brandId}` // 用于区分同商品不同品牌
})
}
})
} else if (item.quantity > 0) {
// 没有品牌的商品
products.push({
...item,
uniqueKey: item.id
})
}
})
return products
},
bannerList() {
return getApp().globalData.bannerList || []
},
categories() {
const list = getApp().globalData.pricePreviewList || []
return list.sort((a, b) => a.sort - b.sort)
// return list.filter(item => item.pid === '0').sort((a, b) => a.sort - b.sort)
},
minMoney() {
const config = getApp().globalData.configData || [];
const item = config.find(i => i.keyName === 'min_money');
return item ? parseFloat(item.keyContent) : 0;
},
minMumber() {
const config = getApp().globalData.configData || [];
const item = config.find(i => i.keyName === 'min_number');
return item ? parseFloat(item.keyContent) : 0;
},
// 计算比预计回收多的金额(商品回收价格 - (商品回收价格 * 0.066))
extraRecycleAmount() {
const minPrice = parseFloat(this.priceRange.min) || 0;
const maxPrice = parseFloat(this.priceRange.max) || 0;
if (minPrice === 0 && maxPrice === 0) {
return {
min: '0.00',
max: '0.00',
display: '0.00元'
};
}
let re_key_numer = this.re_key_numer || 0.066
// 计算减去6.6%后的金额
const minExtra = minPrice - (minPrice * re_key_numer);
const maxExtra = maxPrice - (maxPrice * re_key_numer);
// 如果最小值和最大值相等,显示单个值
if (minPrice === maxPrice) {
return {
min: minExtra.toFixed(2),
max: maxExtra.toFixed(2),
display: `${minExtra.toFixed(2)}`
};
}
// 显示区间
return {
min: minExtra.toFixed(2),
max: maxExtra.toFixed(2),
display: `${minExtra.toFixed(2)}-${maxExtra.toFixed(2)}`
};
},
},
methods: {
showPriceInfoPopups() {
console.log('showPriceInfoPopup called');
this.isFromPickupFlow = false // 标记是从帮助按钮触发
this.showPriceInfoPopup = true
},
getSelectKey() {
const keys = ['home', 'recycle', 'my']
return keys[this.value] || 'recycle'
},
fetchGoodsList(categoryId, page = 1, callback) {
this.$api('getClassGoodsList', {
classId: categoryId,
pageNo: page,
pageSize: this.pageSize
}, res => {
if (res.code === 200 && res.result && Array.isArray(res.result.records)) {
const oldList = this.allProducts[categoryId] || []
const newList = page === 1 ? res.result.records : oldList.concat(res.result.records)
this.$set(this.allProducts, categoryId, newList)
this.$set(this.allProductsPage, categoryId, page)
this.$set(this.allProductsTotal, categoryId, res.result.total)
}
if (callback) callback()
})
},
// 获取分类商品总数
getCategoryItemCount(index) {
const categoryId = this.categories[index]?.id
const categoryItems = this.allProducts[categoryId] || []
return categoryItems.reduce((sum, item) => {
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
return sum + Object.values(item.brandStyleQuantities).reduce((styleSum, qty) => styleSum + qty, 0)
}
if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
return sum + Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
}
return sum + (item.quantity || 0)
}, 0)
},
// 切换分类
switchCategory(index) {
this.currentCategory = index
this.loadingMore = false
this.finished = false
const categoryId = this.categories[index]?.id
// console.log(categoryId,'switchCategory')
if (!this.allProducts[categoryId]) {
this.fetchGoodsList(categoryId, 1)
}
},
// 更新商品数量
updateQuantity(index, delta) {
const categoryId = this.categories[this.currentCategory]?.id
const item = this.allProducts[categoryId]?.[index]
if (!item) return
// 如果是减少数量且delta为负数
if (delta < 0) {
// 检查是否有多个品牌款式
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 1) {
// 有多个品牌款式,显示选择弹窗
this.reduceItem = { item, index, delta }
const reduceStyleList = Object.entries(item.brandStyleQuantities)
.filter(([uniqueKey, quantity]) => quantity > 0)
.map(([uniqueKey, quantity]) => {
const cacheInfo = item.styleCache[uniqueKey]
return {
uniqueKey,
quantity,
name: cacheInfo ? `${cacheInfo.brandInfo.name} - ${cacheInfo.styleInfo.name}` : '未知款式',
logo: cacheInfo ? cacheInfo.brandInfo.logo : ''
}
})
this.$refs.brandSelector.openReducePopup(reduceStyleList)
return
} else if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length === 1) {
// 只有一个品牌款式,直接减少
const uniqueKey = Object.keys(item.brandStyleQuantities)[0]
const currentQty = item.brandStyleQuantities[uniqueKey] || 0
const newQty = Math.max(0, currentQty + delta)
this.$set(item.brandStyleQuantities, uniqueKey, newQty)
// 如果数量为0,删除该品牌款式
if (newQty === 0) {
delete item.brandStyleQuantities[uniqueKey]
if (item.styleCache && item.styleCache[uniqueKey]) {
delete item.styleCache[uniqueKey]
}
}
return
} else if (item.brandQuantities && Object.keys(item.brandQuantities).length > 1) {
// 有多个品牌,显示选择弹窗(兼容旧数据)
this.reduceItem = { item, index, delta }
const reduceBrandList = Object.entries(item.brandQuantities)
.filter(([brandId, quantity]) => quantity > 0)
.map(([brandId, quantity]) => {
const brandInfo = this.getBrandInfo(brandId)
return {
brandId,
quantity,
name: brandInfo ? brandInfo.name : '未知品牌',
logo: brandInfo ? brandInfo.logo : ''
}
})
this.$refs.brandSelector.openReducePopup(reduceBrandList)
return
} else if (item.brandQuantities && Object.keys(item.brandQuantities).length === 1) {
// 只有一个品牌,直接减少(兼容旧数据)
const brandId = Object.keys(item.brandQuantities)[0]
const currentQty = item.brandQuantities[brandId] || 0
const newQty = Math.max(0, currentQty + delta)
this.$set(item.brandQuantities, brandId, newQty)
// 如果数量为0,删除该品牌
if (newQty === 0) {
delete item.brandQuantities[brandId]
}
return
} else {
// 没有品牌数量,使用原来的逻辑
let newQuantity = (item.quantity || 0) + delta
if (newQuantity < 0) newQuantity = 0
this.$set(item, 'quantity', newQuantity)
return
}
}
// 品牌商品且数量为0且加一时
if (item.isPin === 'Y' && (item.quantity || 0) === 0 && delta > 0) {
this.pendingBrandIndex = index
// 检查是否需要显示回收规则
if (item.isRecycleRules === 'Y') {
this.isWaitingForBrandSelection = true;
this.showRules(item); // 先显示回收规则
} else {
// 不需要显示规则,直接打开品牌选择
this.$refs.brandSelector.open(item.id)
}
return
}
// 无品牌商品,数量为0且加一时
if (item.isPin !== 'Y' && (item.quantity || 0) === 0 && delta > 0) {
this.pendingBrandIndex = index
// 检查是否需要显示回收规则
if (item.isRecycleRules === 'Y') {
this.isWaitingForBrandSelection = false; // 标记为无品牌
this.showRules(item)
} else {
// 不需要显示规则,直接加数量
let newQuantity = (item.quantity || 0) + delta
if (newQuantity < 0) newQuantity = 0
this.$set(item, 'quantity', newQuantity)
this.pendingBrandIndex = null
}
return
}
// 其它情况直接加数量
let newQuantity = (item.quantity || 0) + delta
if (newQuantity < 0) newQuantity = 0
this.$set(item, 'quantity', newQuantity)
},
// 处理品牌确认事件(新的款式选择流程)
onBrandConfirm(data) {
if (this.pendingBrandIndex !== null) {
const categoryId = this.categories[this.currentCategory]?.id
const item = this.allProducts[categoryId]?.[this.pendingBrandIndex]
if (item && data.selectedStyles && data.selectedStyles.length > 0) {
// 初始化品牌款式数量对象
if (!item.brandStyleQuantities) {
this.$set(item, 'brandStyleQuantities', {})
}
// 清理当前品牌的所有款式数据
const brandPrefix = `${data.brandInfo.id}_`
Object.keys(item.brandStyleQuantities).forEach(key => {
if (key.startsWith(brandPrefix)) {
delete item.brandStyleQuantities[key]
if (item.styleCache && item.styleCache[key]) {
delete item.styleCache[key]
}
}
})
// 为每个选中的款式设置数量(只保存有数量的款式)
data.selectedStyles.forEach(style => {
if (style.quantity > 0) {
const uniqueKey = `${data.brandInfo.id}_${style.id}`
this.$set(item.brandStyleQuantities, uniqueKey, style.quantity)
// 缓存款式信息用于后续显示
if (!item.styleCache) {
this.$set(item, 'styleCache', {})
}
this.$set(item.styleCache, uniqueKey, {
brandInfo: data.brandInfo,
styleInfo: style
})
}
})
// 清除原来的quantity和brandQuantities(如果存在)
if (item.quantity) {
this.$set(item, 'quantity', 0)
}
if (item.brandQuantities) {
this.$set(item, 'brandQuantities', {})
}
// 品牌选择完成后自动打开商品明细弹窗
this.$nextTick(() => {
this.showDetailPanel = true
})
}
this.pendingBrandIndex = null
}
},
// 处理减少品牌选择事件
onReduceSelect(selectInfo) {
const { item, index, delta } = this.reduceItem
if (selectInfo.uniqueKey) {
// 新的品牌款式减少逻辑
const currentQty = item.brandStyleQuantities[selectInfo.uniqueKey] || 0
const newQty = Math.max(0, currentQty + delta)
this.$set(item.brandStyleQuantities, selectInfo.uniqueKey, newQty)
// 如果数量为0,删除该品牌款式
if (newQty === 0) {
delete item.brandStyleQuantities[selectInfo.uniqueKey]
if (item.styleCache && item.styleCache[selectInfo.uniqueKey]) {
delete item.styleCache[selectInfo.uniqueKey]
}
}
} else if (selectInfo.brandId) {
// 兼容旧的品牌减少逻辑
const currentQty = item.brandQuantities[selectInfo.brandId] || 0
const newQty = Math.max(0, currentQty + delta)
this.$set(item.brandQuantities, selectInfo.brandId, newQty)
// 如果数量为0,删除该品牌
if (newQty === 0) {
delete item.brandQuantities[selectInfo.brandId]
}
}
this.reduceItem = null
},
// 处理品牌选择器关闭事件
onBrandSelectorClose() {
// 如果用户取消品牌选择,重置状态
this.pendingBrandIndex = null
this.isWaitingForBrandSelection = false
},
// 获取已有的款式数量
getExistingQuantities(brandId, callback) {
if (this.pendingBrandIndex !== null) {
const categoryId = this.categories[this.currentCategory]?.id
const item = this.allProducts[categoryId]?.[this.pendingBrandIndex]
if (item && item.brandStyleQuantities) {
// 过滤出当前品牌的款式数量
const existingQuantities = {}
Object.entries(item.brandStyleQuantities).forEach(([uniqueKey, quantity]) => {
if (uniqueKey.startsWith(`${brandId}_`)) {
existingQuantities[uniqueKey] = quantity
}
})
callback(existingQuantities)
} else {
callback({})
}
} else {
callback({})
}
},
// 打开商品明细弹窗
openProductDetail(selectedItem) {
if (!selectedItem.brandId || !selectedItem.styleId) {
uni.showToast({
title: '该商品无法查看明细',
icon: 'none'
})
return
}
// 查找原始商品信息
const originalItem = this.findOriginalItem(selectedItem.id)
if (!originalItem) {
uni.showToast({
title: '商品信息不存在',
icon: 'none'
})
return
}
// 构建商品信息
const productInfo = {
id: originalItem.id,
name: originalItem.name,
image: originalItem.image
}
// 构建品牌信息
const brandInfo = {
id: selectedItem.brandId,
name: selectedItem.brandName,
logo: selectedItem.brandImage
}
// 获取该品牌下的所有款式数量
const existingQuantities = {}
if (originalItem.brandStyleQuantities) {
Object.entries(originalItem.brandStyleQuantities).forEach(([uniqueKey, quantity]) => {
if (uniqueKey.startsWith(`${selectedItem.brandId}_`)) {
existingQuantities[uniqueKey] = quantity
}
})
}
// 打开商品明细弹窗
this.$refs.productDetailPopup.open(productInfo, brandInfo, existingQuantities, originalItem.styleCache || {})
},
// 处理商品明细确认修改事件
onDetailConfirmChanges(data) {
const { productInfo, brandInfo, updatedStyles } = data
// 查找原始商品
const originalItem = this.findOriginalItem(productInfo.id)
if (!originalItem) return
// 清理当前品牌的所有款式数据
const brandPrefix = `${brandInfo.id}_`
if (originalItem.brandStyleQuantities) {
Object.keys(originalItem.brandStyleQuantities).forEach(key => {
if (key.startsWith(brandPrefix)) {
delete originalItem.brandStyleQuantities[key]
if (originalItem.styleCache && originalItem.styleCache[key]) {
delete originalItem.styleCache[key]
}
}
})
}
// 重新设置款式数据
if (!originalItem.brandStyleQuantities) {
this.$set(originalItem, 'brandStyleQuantities', {})
}
if (!originalItem.styleCache) {
this.$set(originalItem, 'styleCache', {})
}
updatedStyles.forEach(style => {
if (style.quantity > 0) {
const uniqueKey = `${brandInfo.id}_${style.id}`
this.$set(originalItem.brandStyleQuantities, uniqueKey, style.quantity)
this.$set(originalItem.styleCache, uniqueKey, {
brandInfo: brandInfo,
styleInfo: style
})
}
})
// uni.showToast({
// title: '修改成功',
// icon: 'success'
// })
},
// 处理商品明细弹窗关闭事件
onDetailPopupClose() {
// 弹窗关闭时的处理逻辑
},
// 处理规则确认事件
onRuleConfirm() {
// 如果是在等待品牌选择的状态下关闭规则弹窗,则接着打开品牌选择
if (this.isWaitingForBrandSelection) {
this.isWaitingForBrandSelection = false; // 清除等待状态
const categoryId = this.categories[this.currentCategory]?.id;
const item = this.allProducts[categoryId]?.[this.pendingBrandIndex];
// 记录该商品的规则已被查看
this.viewedRuleItems.add(item.id);
this.$refs.brandSelector.open(item.id); // 打开品牌索引弹窗
} else if (this.pendingBrandIndex !== null) {
// 无品牌商品,规则弹窗关闭后加数量
const categoryId = this.categories[this.currentCategory]?.id;
const item = this.allProducts[categoryId]?.[this.pendingBrandIndex];
if (item) {
let newQuantity = (item.quantity || 0) + 1
this.$set(item, 'quantity', newQuantity)
}
this.pendingBrandIndex = null
}
},
// 获取品牌信息
getBrandInfo(brandId) {
// 从所有商品的品牌缓存中查找品牌信息
for (const productId in this.brandCache) {
const brandInfo = this.brandCache[productId].find(brand => brand.id === brandId)
if (brandInfo) {
return brandInfo
}
}
// 如果缓存中没有找到,也从当前brandList中查找(兼容性)
return this.brandList.find(brand => brand.id === brandId)
},
// 获取商品的总数量(所有品牌款式)
getItemTotalQuantity(item) {
if (item.brandStyleQuantities && Object.keys(item.brandStyleQuantities).length > 0) {
return Object.values(item.brandStyleQuantities).reduce((sum, qty) => sum + qty, 0)
}
if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
return Object.values(item.brandQuantities).reduce((sum, qty) => sum + qty, 0)
}
return item.quantity || 0
},
// 显示回收规则
showRules(item) {
// 检查是否需要显示回收规则
if (item.isRecycleRules !== 'Y') {
// 不需要显示规则,直接添加数量
if (item.isPin === 'Y') {
// 品牌商品,直接打开品牌选择
this.pendingBrandIndex = this.recycleList.findIndex(i => i.id === item.id)
this.$refs.brandSelector.open(item.id)
} else {
// 非品牌商品,直接加数量
let newQuantity = (item.quantity || 0) + 1
this.$set(item, 'quantity', newQuantity)
}
return
}
// isPin=Y: 弹规则,读到底部后自动弹品牌;isPin=N: 只弹规则
if (item.isPin === 'Y') {
// 检查该商品是否已经查看过规则
if (this.viewedRuleItems.has(item.id)) {
// 如果已经查看过,直接跳过规则弹窗,进入品牌选择
this.isWaitingForBrandSelection = false;
this.$refs.brandSelector.open(item.id);
return;
}
// 获取回收规则富文本
this.$api('getGoodsRecycleRule', { goodsId: item.id }, res => {
const ruleContent = (res.code === 200 && res.result) ? res.result : '<p>暂无回收规则</p>'
this.$refs.rulePopup.openRulePopup(ruleContent)
// 规则弹窗关闭后自动弹品牌弹窗逻辑在onRuleConfirm已实现
})
} else {
// isPin=N 只弹规则
this.$api('getGoodsRecycleRule', { goodsId: item.id }, res => {
const ruleContent = (res.code === 200 && res.result) ? res.result : '<p>暂无回收规则</p>'
this.$refs.rulePopup.openRulePopup(ruleContent)
// 不弹品牌弹窗
this.isWaitingForBrandSelection = false;
})
}
},
showMore() {
uni.showToast({
title: '更多规则请咨询客服',
icon: 'none'
})
},
submitOrder() {
// 检查用户是否被拉入黑名单
if (this.isUserBlacklisted) {
uni.showModal({
title: '提示',
content: '您的账户已被限制使用回收服务,如有疑问请联系客服。',
showCancel: false,
confirmText: '我知道了'
})
return
}
if (this.totalCount < this.minMumber || Number(this.totalPrice) < this.minMoney) {
uni.showToast({
title: `各品类混合需要满${this.minMumber}${this.totalUnitText}并且各品类混合需要满${this.minMoney}元才能回收哦`,
icon: 'none'
})
return
}
this.$refs.rulePopup.openConfirmPopup();
},
handlePickupCancel() {
// 取消回收的处理逻辑
},
handlePickupAgree() {
// 显示总的回收规则弹窗
this.showGeneralRulesPopup()
},
// 显示总的回收规则弹窗
showGeneralRulesPopup() {
this.isFromPickupFlow = true // 标记是从预约流程触发
this.showPriceInfoPopup = true
},
// 关闭价格说明弹窗的方法
closePriceInfoPopup() {
console.log('closePriceInfoPopup called');
this.showPriceInfoPopup = false
// 如果是从预约流程触发的,关闭弹窗后执行跳转逻辑
if (this.isFromPickupFlow) {
this.isFromPickupFlow = false // 重置标志
this.executePickupFlow()
}
},
// 执行预约流程
executePickupFlow() {
uni.showLoading({
title: '提交中...'
})
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '预约成功',
icon: 'success'
})
this.goToPickup()
}, 1500)
},
goToPickup() {
// 获取所有选中的衣物(所有分类)
const selectedItems = this.selectedProducts.map(item => {
let desc = '允许脏破烂,160码以上'
if (item.brandName && item.styleName) {
desc = `品牌:${item.brandName} | 款式:${item.styleName}`
} else if (item.brandName) {
desc = `品牌:${item.brandName}`
} else if (item.styleName) {
desc = `款式:${item.styleName}`
}
const baseItem = {
id: item.id,
name: item.name,
icon: item.image,
quantity: item.quantity,
unitPrice: item.price,
maxPrice: item.maxPrice,
desc: desc
}
// 如果有品牌信息,添加品牌相关字段
if (item.brandId) {
baseItem.brandId = item.brandId
baseItem.brandName = item.brandName
baseItem.brandImage = item.brandImage
}
// 如果有款式信息,添加款式相关字段
if (item.styleId) {
baseItem.styleId = item.styleId
baseItem.styleName = item.styleName
baseItem.styleImage = item.styleImage
}
return baseItem
})
const itemsStr = encodeURIComponent(JSON.stringify(selectedItems))
uni.navigateTo({
url: `/pages/subcomponent/pickup?fromRecycle=true&items=${itemsStr}`
})
},
checkBrand(index) {
const categoryId = this.categories[this.currentCategory]?.id
const item = this.allProducts[categoryId]?.[index]
if (item?.id) {
this.pendingBrandIndex = index
this.$refs.brandSelector.open(item.id)
}
},
// 添加下拉刷新方法
async refreshData() {
try {
// 这里可以添加刷新数据的逻辑,比如重新获取商品列表等
// 示例:重新初始化数据
this.currentCategory = 0
Object.values(this.allProducts).forEach(categoryItems => {
categoryItems.forEach(item => {
item.quantity = 0
if (item.brandQuantities) {
item.brandQuantities = {}
}
if (item.brandStyleQuantities) {
item.brandStyleQuantities = {}
}
if (item.styleCache) {
item.styleCache = {}
}
})
})
// 清空已查看规则的记录
this.viewedRuleItems.clear()
// 清空品牌缓存
this.brandCache = {}
// 模拟网络请求延迟
await new Promise(resolve => setTimeout(resolve, 1000))
uni.showToast({
title: '刷新成功',
icon: 'success'
})
} catch (error) {
uni.showToast({
title: '刷新失败',
icon: 'none'
})
} finally {
// 停止下拉刷新动画
uni.stopPullDownRefresh()
}
},
toggleDetailPanel() {
this.showDetailPanel = !this.showDetailPanel
},
// 添加更多衣物
addMoreItems() {
this.showDetailPanel = false
},
fetchUserInfo() {
if (uni.getStorageSync('token')) {
this.login_status = getApp().globalData.login_status;
this.$api("getUserByToken", {}, (res) => {
if (res.code == 200) {
this.userInfo = res.result
// 检查用户是否被拉入黑名单
this.isUserBlacklisted = res.result.isBlack === 'Y'
// isTuiType 为0用户,1推广达人,2推广大使
}
})
} else {
this.login_status = false;
}
},
updateQuantityByProduct(item, delta) {
// 在明细弹窗中更新数量
if (item.brandId && item.styleId) {
// 有品牌和款式ID的商品(新的品牌款式逻辑)
const originalItem = this.findOriginalItem(item.id)
if (originalItem && originalItem.brandStyleQuantities) {
const uniqueKey = `${item.brandId}_${item.styleId}`
const currentQty = originalItem.brandStyleQuantities[uniqueKey] || 0
const newQty = Math.max(0, currentQty + delta)
if (newQty > 0) {
this.$set(originalItem.brandStyleQuantities, uniqueKey, newQty)
} else {
// 如果数量为0,删除该品牌款式
delete originalItem.brandStyleQuantities[uniqueKey]
if (originalItem.styleCache && originalItem.styleCache[uniqueKey]) {
delete originalItem.styleCache[uniqueKey]
}
}
// 同步更新显示的数量
item.quantity = newQty
}
} else if (item.brandId) {
// 有品牌ID但没有款式ID的商品(兼容旧逻辑)
const originalItem = this.findOriginalItem(item.id)
if (originalItem && originalItem.brandQuantities) {
const currentQty = originalItem.brandQuantities[item.brandId] || 0
const newQty = Math.max(0, currentQty + delta)
if (newQty > 0) {
this.$set(originalItem.brandQuantities, item.brandId, newQty)
} else {
// 如果数量为0,删除该品牌
delete originalItem.brandQuantities[item.brandId]
}
// 同步更新显示的数量
item.quantity = newQty
}
} else {
// 没有品牌的商品
if (!item.quantity) item.quantity = 0
item.quantity = Math.max(0, item.quantity + delta)
// 同步到原商品
const originalItem = this.findOriginalItem(item.id)
if (originalItem) {
this.$set(originalItem, 'quantity', item.quantity)
}
}
},
// 查找原始商品对象
findOriginalItem(itemId) {
for (const categoryItems of Object.values(this.allProducts)) {
const item = categoryItems.find(i => i.id === itemId)
if (item) return item
}
return null
},
loadMoreGoods() {
const categoryId = this.categories[this.currentCategory]?.id
const page = (this.allProductsPage[categoryId] || 1) + 1
const total = this.allProductsTotal[categoryId] || 0
const loaded = (this.allProducts[categoryId] || []).length
if (this.loadingMore || this.finished) return
if (loaded < total) {
this.loadingMore = true
this.fetchGoodsList(categoryId, page, () => {
this.loadingMore = false
// 判断是否加载完
const newLoaded = (this.allProducts[categoryId] || []).length
this.finished = newLoaded >= (this.allProductsTotal[categoryId] || 0)
})
} else {
this.finished = true
}
},
// 初始化页面数据的方法
initializePageData() {
const options = this.loadOptions || {}
if (options && options.categoryId) {
const idx = this.categories.findIndex(c => c.id == options.categoryId)
if (idx !== -1) this.currentCategory = idx
}
if (this.categories.length > 0) {
this.fetchGoodsList(this.categories[this.currentCategory].id, 1)
}
uni.$on('bannerListUpdated', () => {
this.$forceUpdate && this.$forceUpdate()
})
if (getApp().globalData.bannerList && getApp().globalData.bannerList.length > 0) {
this.$forceUpdate && this.$forceUpdate()
}
// 检查全局清空标志(兼容 reLaunch)
if (getApp().globalData.shouldClearRecycle) {
Object.values(this.allProducts).forEach(categoryItems => {
categoryItems.forEach(item => {
this.$set(item, 'quantity', 0)
if (item.brandQuantities) {
this.$set(item, 'brandQuantities', {})
}
if (item.brandStyleQuantities) {
this.$set(item, 'brandStyleQuantities', {})
}
if (item.styleCache) {
this.$set(item, 'styleCache', {})
}
})
})
// 清空已查看规则的记录
this.viewedRuleItems.clear()
this.showDetailPanel = false
this.brandCache = {} // 清空品牌缓存
this.$forceUpdate()
getApp().globalData.shouldClearRecycle = false
}
},
},
created() {
this.currentCategory = 0
this.$nextTick(() => {
if (this.categories.length > 0) {
const firstCategoryId = this.categories[0]?.id
if (firstCategoryId) {
this.fetchGoodsList(firstCategoryId, 1)
}
}
})
},
mounted() {
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this)
query.select('.uv-tabbar').boundingClientRect(rect => {
if (rect && rect.height) {
this.tabbarHeight = rect.height
} else {
this.tabbarHeight = uni.upx2px ? uni.upx2px(95) : 45
}
// console.log(this.tabbarHeight,'tabbarHeight')
}).exec()
})
},
onLoad(options) {
// 保存options参数
this.loadOptions = options
// 检查App数据是否已经准备好
if (!getApp().globalData.isAppDataReady) {
// 显示加载状态
uni.showLoading({
title: '加载中...',
mask: true
})
// 监听App数据准备完成事件
uni.$on('appDataReady', () => {
uni.hideLoading()
this.initializePageData()
})
} else {
// App数据已经准备好,直接初始化页面数据
this.initializePageData()
}
},
onUnload() {
uni.$off('bannerListUpdated')
// 移除事件监听
uni.$off('clearRecycleOrderData')
},
onShow() {
// 获取用户信息,检查黑名单状态
this.fetchUserInfo()
const id = getApp().globalData.targetRecycleCategoryId
if (id) {
const trySwitch = () => {
if (this.categories.length > 0) {
const idx = this.categories.findIndex(c => String(c.id) === String(id))
if (idx !== -1) {
this.currentCategory = idx
const categoryId = this.categories[idx]?.id
if (categoryId && !this.allProducts[categoryId]) {
this.loadingMore = false
this.finished = false
this.fetchGoodsList(categoryId, 1)
}
}
getApp().globalData.targetRecycleCategoryId = null
} else {
setTimeout(trySwitch, 100)
}
}
trySwitch()
}
// 检查全局清空标志
if (getApp().globalData.shouldClearRecycle) {
Object.values(this.allProducts).forEach(categoryItems => {
categoryItems.forEach(item => {
this.$set(item, 'quantity', 0)
if (item.brandQuantities) {
this.$set(item, 'brandQuantities', {})
}
if (item.brandStyleQuantities) {
this.$set(item, 'brandStyleQuantities', {})
}
if (item.styleCache) {
this.$set(item, 'styleCache', {})
}
})
})
// 清空已查看规则的记录
this.viewedRuleItems.clear()
this.showDetailPanel = false
this.brandCache = {} // 清空品牌缓存
this.$forceUpdate()
getApp().globalData.shouldClearRecycle = false
}
// 监听清除订单数据的事件
uni.$on('clearRecycleOrderData', () => {
// 清除所有商品的选中数量,保证响应式
Object.values(this.allProducts).forEach(categoryItems => {
categoryItems.forEach(item => {
this.$set(item, 'quantity', 0)
if (item.brandQuantities) {
this.$set(item, 'brandQuantities', {})
}
if (item.brandStyleQuantities) {
this.$set(item, 'brandStyleQuantities', {})
}
if (item.styleCache) {
this.$set(item, 'styleCache', {})
}
})
})
// 清空已查看规则的记录
this.viewedRuleItems.clear()
// 清空品牌缓存
this.brandCache = {}
// 重置其他相关数据
this.showDetailPanel = false
this.$forceUpdate()
})
},
watch: {
categories(newVal) {
const id = getApp().globalData.targetRecycleCategoryId
const idx = newVal.findIndex(c => String(c.id) === String(id))
if (id && newVal.length > 0 && idx !== -1) {
this.currentCategory = idx
getApp().globalData.targetRecycleCategoryId = null
// 自动加载右侧商品
const categoryId = newVal[idx]?.id
if (categoryId && !this.allProducts[categoryId]) {
this.loadingMore = false
this.finished = false
this.fetchGoodsList(categoryId, 1)
}
}
}
},
}
</script>
<style lang="scss" scoped>
.container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
overflow: hidden;
}
.goods-list {
// flex: 1;
display: flex;
position: relative;
height: calc(110vh - 320rpx - 160rpx - env(safe-area-inset-bottom));
/* 减去banner、底部栏和绿色提示条的高度 */
margin-top: -10rpx;
z-index: 2;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
padding: 30rpx;
box-shadow: 0 -4rpx 8rpx rgba(0, 0, 0, 0.05);
background: linear-gradient(to bottom, #fff7e8, 20%, #ffffff);
.category-nav {
width: 20%;
background: #ffffff;
height: 100%;
border-right: 1rpx solid rgba(255, 126, 14, 0.1);
margin: 1rpx;
border-radius: 20rpx 0 0 0;
margin-right: 20rpx;
overflow-y: auto;
scrollbar-width: none;
/* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
&::-webkit-scrollbar {
width: 0 !important;
display: none;
/* Chrome, Safari, Opera */
}
.category-item {
position: relative;
padding: 28rpx 20rpx;
text-align: center;
font-family: PingFang SC;
font-weight: 600;
font-size: 15px;
line-height: 100%;
letter-spacing: 0px;
color: #666;
.category-dot {
position: absolute;
top: 15rpx;
right: 15rpx;
min-width: 32rpx;
height: 32rpx;
padding: 0 6rpx;
background: #ff7a0e;
border-radius: 16rpx;
color: #fff;
font-size: 20rpx;
text-align: center;
line-height: 32rpx;
box-sizing: border-box;
}
&.active {
color: #ff7a0e;
font-weight: bold;
background: #fff7e8;
position: relative;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 6rpx;
height: 36rpx;
background: #ff7a0e;
border-radius: 3rpx;
}
}
}
}
.goods-content {
flex: 1;
height: 100%;
padding: 0 0 180rpx 0;
/* 添加底部padding,为固定底部栏预留空间 */
background: #ffffff;
width: 70%;
margin: 1rpx;
margin-left: 0;
border-radius: 0 20rpx 0 0;
overflow-y: auto;
box-sizing: border-box;
scrollbar-width: none;
/* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
&::-webkit-scrollbar {
width: 0 !important;
display: none;
/* Chrome, Safari, Opera */
}
}
}
.goods-item {
display: flex;
align-items: flex-start;
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
.goods-img-container {
position: relative;
width: 180rpx;
height: 180rpx;
margin-right: 28rpx;
flex-shrink: 0;
}
.goods-item-img {
width: 100%;
height: 100%;
border-radius: 24rpx;
background: #f8f8f8;
object-fit: contain;
}
.brand-tag {
position: absolute;
top: 0rpx;
left: 0rpx;
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 20rpx;
padding: 4rpx 8rpx;
border-radius: 8rpx;
z-index: 2;
}
.goods-info-wrap {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
.goods-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.goods-name {
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
line-height: 140%;
letter-spacing: 0%;
vertical-align: middle;
color: #333;
font-weight: bold;
flex-shrink: 1;
flex-grow: 1;
}
.brand-check-placeholder {
flex-shrink: 0;
margin-left: 10rpx;
}
.brand-check {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border: 1px solid #f8a01d;
border-radius: 8rpx;
color: #ff7a0e;
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
padding: 4rpx 10rpx;
line-height: 1;
white-space: nowrap;
text {
margin-right: 4rpx;
}
}
.goods-desc {
font-size: 20rpx;
color: #999;
display: block;
// margin-bottom: 20rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.goods-info {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
gap: 5rpx;
margin-top: 10rpx;
}
.price-info {
display: flex;
align-items: baseline;
white-space: nowrap;
flex-shrink: 0;
.price-symbol {
font-size: 24rpx;
color: #ff7a0e;
}
.price-value {
font-size: 36rpx;
color: #ff7a0e;
font-weight: bold;
margin: 0 0rpx;
white-space: nowrap;
}
.price-unit {
font-size: 24rpx;
color: #999;
white-space: nowrap;
}
}
.quantity-control {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
button {
width: 60rpx;
height: 60rpx;
padding: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #666;
background: #ffffff;
border: none;
border-radius: 50%;
&::after {
border: none;
}
&:active {
opacity: 0.8;
}
}
.quantity {
width: 50rpx;
text-align: center;
font-size: 32rpx;
color: #333;
}
}
.rules-brand-row {
display: flex;
align-items: center;
margin-top: 20rpx;
gap: 16rpx;
.rules-link {
margin-top: 0;
.rules {
display: inline-flex;
align-items: center;
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #666;
white-space: nowrap;
}
}
.brand-check-placeholder {
margin-left: 12rpx;
}
}
}
.other-unrecycle-card {
display: flex;
align-items: center;
background: #fff;
border-radius: 24rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
padding: 30rpx 30rpx 30rpx 30rpx;
margin: 30rpx 0 0 0;
}
.other-unrecycle-img {
width: 120rpx;
height: 120rpx;
border-radius: 24rpx;
background: #f8f8f8;
margin-right: 28rpx;
object-fit: contain;
flex-shrink: 0;
}
.other-unrecycle-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
overflow: hidden;
}
.other-unrecycle-title {
font-size: 30rpx;
color: #222;
font-weight: bold;
margin-bottom: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.other-unrecycle-desc {
font-size: 24rpx;
color: #999;
margin-bottom: 12rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.other-unrecycle-price-row {
display: flex;
align-items: center;
white-space: nowrap;
}
.other-unrecycle-price {
font-size: 28rpx;
color: #ff9c00;
font-weight: bold;
white-space: nowrap;
}
.other-unrecycle-btn {
width: 60rpx;
height: 60rpx;
margin-left: 24rpx;
border-radius: 50%;
background: #fff;
color: #666;
font-size: 36rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
.fixed-bottom-wrap {
position: fixed;
padding-bottom: 120rpx;
left: 0;
bottom: calc(env(safe-area-inset-bottom));
// bottom: calc(v-bind('tabbarHeight + "rpx"') + env(safe-area-inset-bottom));
width: 100vw;
z-index: 100;
background: transparent;
box-sizing: border-box;
pointer-events: auto;
}
.bottom-bar-divider {
width: 100%;
height: 1px;
background: #f0f0f0;
position: absolute;
left: 0;
bottom: 0;
z-index: 1;
}
.green-tip-bar {
width: 100%;
background: #eaffea;
color: #13ac47;
font-size: 20rpx;
padding: 8rpx 30rpx;
box-sizing: border-box;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
line-height: 1.4;
min-height: 40rpx;
.tip-highlight {
color: #ff9c00;
font-weight: bold;
font-size: 20rpx;
}
}
.bottom-bar {
width: 100%;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
height: 120rpx;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: env(safe-area-inset-bottom);
border-bottom-right-radius: env(safe-area-inset-bottom);
.bottom-left {
// flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.summary-row {
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.summary-label {
color: #333;
}
.summary-count {
color: #ff9c00;
font-weight: bold;
font-size: 28rpx;
}
}
.amount-row {
display: flex;
align-items: center;
margin-top: 4rpx;
.amount {
color: #ff9c00;
font-size: 44rpx;
font-weight: bold;
vertical-align: middle;
}
}
}
.submit-btn {
width: 300rpx;
height: 88rpx;
background: linear-gradient(to right, #ffd01e, #ff8917);
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
border: none;
// margin-left: 0rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
&::after {
border: none;
}
&:active {
opacity: 0.9;
}
}
}
.detail-popup-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
// padding-bottom: calc(env(safe-area-inset-bottom));
// bottom: calc(120rpx + env(safe-area-inset-bottom));
// bottom: calc(90rpx + env(safe-area-inset-bottom));
/* tabbar高度+安全区 */
background: rgba(0, 0, 0, 0.35);
z-index: 9999;
display: flex;
align-items: flex-end;
justify-content: center;
}
.detail-popup {
width: 100vw;
max-width: none;
background: #fff;
border-radius: 48rpx 48rpx 0 0;
box-shadow: 0 8rpx 48rpx rgba(0, 0, 0, 0.18);
display: flex;
flex-direction: column;
align-items: stretch;
position: relative;
padding: 0;
padding-bottom: calc(env(safe-area-inset-bottom));
overflow: hidden;
min-height: 80vh;
bottom: 0;
}
.detail-popup-close {
position: absolute;
right: 36rpx;
top: 36rpx;
font-size: 36rpx;
color: #bbb;
z-index: 2;
}
.popup-green-tip {
border-radius: 48rpx 48rpx 0 0;
font-size: 20rpx;
padding: 24rpx 30rpx 0 30rpx;
background: #eaffea;
color: #13ac47;
text-align: left;
}
.panel-header {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: bold;
padding: 40rpx 36rpx 0 36rpx;
background: #fff;
position: relative;
}
.panel-title {
font-size: 32rpx;
color: #222;
font-weight: bold;
text-align: center;
flex: 1;
}
.popup-panel-list {
// flex: 1;
width: 710rpx;
overflow-y: auto;
height: 70vh !important;
padding: 0 24rpx;
scrollbar-width: none;
/* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
&::-webkit-scrollbar {
width: 0 !important;
display: none;
/* Chrome, Safari, Opera */
}
}
.panel-item {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 24rpx 0;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
&:active {
// background: #f8f8f8;
}
}
.panel-img-container {
position: relative;
width: 100rpx;
height: 100rpx;
margin-right: 20rpx;
flex-shrink: 0;
}
.panel-item-img {
width: 100%;
height: 100%;
border-radius: 16rpx;
background: #f8f8f8;
}
.panel-brand-tag {
position: absolute;
top: 0rpx;
left: 0rpx;
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 18rpx;
padding: 2rpx 6rpx;
border-radius: 6rpx;
z-index: 2;
}
.panel-item-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
.panel-item-name {
font-size: 28rpx;
color: #222;
font-weight: bold;
margin-bottom: 4rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.panel-item-desc {
font-size: 24rpx;
color: #999;
margin-bottom: 4rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.panel-item-price {
font-size: 26rpx;
color: #ff9c00;
margin-top: 2rpx;
}
.panel-quantity-control {
display: flex;
align-items: center;
margin-left: 20rpx;
// margin-right: 30rpx;
flex-shrink: 0;
}
.panel-quantity-control button {
width: 48rpx;
height: 48rpx;
padding: 0;
margin: 0 8rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #666;
background: #ffffff;
border: none;
border-radius: 50%;
&::after {
border: none;
}
&:active {
opacity: 0.8;
}
}
.panel-quantity-control .quantity {
width: 30rpx;
text-align: center;
font-size: 28rpx;
color: #333;
}
.popup-bottom-bar {
width: 100%;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-sizing: border-box;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
height: 120rpx;
border-top: 1px solid #f0f0f0;
border-bottom-left-radius: 48rpx;
border-bottom-right-radius: 48rpx;
// padding-bottom: env(safe-area-inset-bottom);
.bottom-left {
display: flex;
flex-direction: column;
justify-content: center;
.summary-row {
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.summary-label {
color: #333;
}
.summary-count {
color: #ff9c00;
font-weight: bold;
font-size: 28rpx;
}
}
.amount-row {
display: flex;
align-items: center;
margin-top: 4rpx;
.amount {
color: #ff9c00;
font-size: 44rpx;
font-weight: bold;
vertical-align: middle;
}
}
}
.submit-btn {
width: 300rpx;
height: 88rpx;
background: linear-gradient(to right, #ffd01e, #ff8917);
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
border: none;
box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
&::after {
border: none;
}
&:active {
opacity: 0.9;
}
}
}
/* 添加衣物浮窗按钮 */
.floating-add-btn {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 210rpx;
width: 160rpx;
height: 80rpx;
background: linear-gradient(to right, #ffd01e, #ff8917);
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(255, 156, 0, 0.3);
z-index: 10;
&:active {
opacity: 0.9;
transform: translateX(-50%) scale(0.95);
}
.floating-btn-text {
color: #fff;
font-size: 28rpx;
font-weight: bold;
line-height: 1;
text-align: center;
}
}
.uv-tabbar {
z-index: 1000;
}
.loading-more {
text-align: center;
color: #999;
padding: 20rpx 0;
font-size: 26rpx;
}
.price-info-popup-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 996000;
display: flex;
align-items: center;
justify-content: center;
}
.price-info-popup {
width: 85vw;
max-width: 600rpx;
background: #fff;
border-radius: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
align-items: center;
position: relative;
padding: 40rpx 40rpx;
.price-info-popup-title {
font-size: 34rpx;
color: #333;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
}
.price-info-popup-content {
width: 100%;
max-height: 50vh;
.price-info-section {
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
.price-info-heading {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 15rpx;
}
.price-info-text {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
}
}
.price-info-popup-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(to right, #ffd01e, #ff8917);
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
border: none;
margin-top: 40rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
&::after {
border: none;
}
&:active {
opacity: 0.9;
}
}
}
.rules-link {
min-width: 90rpx;
padding: 0 12rpx;
.rules {
font-size: 15px;
white-space: normal;
overflow: visible;
}
}
// ... existing code ...
.rules-brand-row {
display: flex;
align-items: center;
margin-top: 20rpx;
gap: 24rpx; // 增大间距
.rules-link {
margin-top: 0;
}
.brand-check-placeholder {
margin-left: 16rpx;
}
}
// ... existing code ...
.price-info {
display: flex;
align-items: baseline;
white-space: nowrap;
flex-shrink: 0;
gap: 12rpx; // 增大间距
.price-symbol {
font-size: 32rpx; // 增大符号
color: #ff7a0e;
}
.price-value {
font-size: 44rpx; // 增大数字
color: #ff7a0e;
font-weight: bold;
margin: 0 6rpx;
white-space: nowrap;
}
.price-unit {
font-size: 28rpx;
color: #999;
white-space: nowrap;
}
}
.quantity-control {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
gap: 0rpx; // 增大间距
button {
width: 30rpx; // 增大按钮
height: 72rpx;
padding: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx; // 增大符号
color: #666;
background: #ffffff;
border: none;
border-radius: 50%;
&::after {
border: none;
}
&:active {
opacity: 0.8;
}
}
.quantity {
width: 60rpx;
text-align: center;
font-size: 36rpx; // 增大数字
color: #333;
}
}
</style>