爱简收旧衣按件回收前端代码仓库
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.

2372 lines
66 KiB

2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
  1. <template>
  2. <view class="container">
  3. <!-- 顶部banner -->
  4. <view class="banner">
  5. <!-- <swiper
  6. :indicator-dots="false"
  7. :autoplay="true"
  8. :interval="3000"
  9. :duration="500"
  10. circular
  11. style="width: 100%; height: 320rpx;"
  12. > -->
  13. <!-- <swiper-item v-for="(item, index) in bannerList" :key="item.id || index">
  14. <video
  15. v-if="item.type == 1"
  16. :src="item.voUrl"
  17. autoplay
  18. muted
  19. loop
  20. :controls="false"
  21. :show-play-btn="false"
  22. :show-center-play-btn="false"
  23. object-fit="cover"
  24. style="width: 100%; height: 100%;"
  25. ></video>
  26. <image v-else :src="item.image" mode="aspectFill" style="width: 100%; height: 100%;" />
  27. </swiper-item>
  28. </swiper> -->
  29. <image v-if="recycle_banner" :src="recycle_banner" mode="aspectFill" style="width: 100%; height: 100%;" />
  30. </view>
  31. <!-- 商品列表 -->
  32. <view class="goods-list">
  33. <!-- 左侧分类导航 -->
  34. <view class="category-nav">
  35. <view
  36. v-for="(category, index) in categories"
  37. :key="category.id || index"
  38. class="category-item"
  39. :class="{ active: currentCategory === index }"
  40. @click="switchCategory(index)"
  41. >
  42. <view class="category-dot" v-if="getCategoryItemCount(index) > 0">{{ getCategoryItemCount(index) }}</view>
  43. {{ category.title }}
  44. </view>
  45. </view>
  46. <!-- 右侧商品列表 -->
  47. <scroll-view class="goods-content" scroll-y @scrolltolower="loadMoreGoods">
  48. <view class="goods-section">
  49. <view class="goods-item" v-for="(item, index) in recycleList" :key="index">
  50. <image v-if="item.image" :src="item.image" class="goods-item-img" mode="aspectFit" />
  51. <view class="goods-info-wrap">
  52. <view class="goods-header">
  53. <text class="goods-name">{{item.name}}</text>
  54. <view class="brand-check-placeholder" >
  55. <view class="brand-check" @click="checkBrand(index)">
  56. <text>查看品牌</text>
  57. <uni-icons type="right" size="12" color="#ff7a0e"></uni-icons>
  58. </view>
  59. </view>
  60. </view>
  61. <text class="goods-desc">{{item.service}}</text>
  62. <view class="rules-link" @click="showRules(item)">
  63. <view class="rules">
  64. <text>回收规则</text>
  65. <uni-icons type="right" size="14" color="#999"></uni-icons>
  66. </view>
  67. </view>
  68. <view class="goods-info">
  69. <view class="price-info">
  70. <text class="price-symbol">¥</text>
  71. <text class="price-value">{{item.price}}</text>
  72. <text class="price-unit">/{{ item.unit || '件' }}</text>
  73. </view>
  74. <view class="quantity-control">
  75. <button class="btn-minus" @click="updateQuantity(index, -1)">-</button>
  76. <text class="quantity">{{getItemTotalQuantity(item)}}</text>
  77. <button class="btn-plus" @click="updateQuantity(index, 1)">+</button>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </view>
  83. <!-- 不可回收商品 -->
  84. <view class="other-unrecycle-card">
  85. <image class="other-unrecycle-img" src="/static/回收/衣物.png" mode="aspectFit" />
  86. <view class="other-unrecycle-info">
  87. <view class="other-unrecycle-title">其他上衣需咨询顾问暂不回收</view>
  88. <view class="other-unrecycle-desc">需连线回收顾问筛选</view>
  89. <view class="other-unrecycle-price-row">
  90. <text class="other-unrecycle-price">¥ /</text>
  91. </view>
  92. </view>
  93. <button class="other-unrecycle-btn" open-type="contact">+</button>
  94. </view>
  95. <view v-if="loadingMore" class="loading-more">加载中...</view>
  96. <view v-else-if="finished" class="loading-more">没有更多了</view>
  97. </scroll-view>
  98. </view>
  99. <!-- 固定底部区域 -->
  100. <view class="fixed-bottom-wrap" v-if="!showDetailPanel" >
  101. <view class="green-tip-bar">
  102. 回收范围仅支持回收以上品类按件回收预计比称重回收多
  103. <text class="tip-highlight"> 3.76</text>
  104. </view>
  105. <view class="bottom-bar">
  106. <view class="bottom-left">
  107. <view class="summary-row">
  108. <text class="summary-label">已选 <text class="summary-count">{{totalCount}}</text> {{ totalUnitText }} 预计回收可得</text>
  109. <uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" @tap="showPriceInfoPopups" />
  110. </view>
  111. <view class="amount-row">
  112. <uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @click="toggleDetailPanel" />
  113. <text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text>
  114. </view>
  115. </view>
  116. <button class="submit-btn" @click="submitOrder">预约上门取件</button>
  117. </view>
  118. <view class="bottom-bar-divider"></view>
  119. </view>
  120. <!-- 明细弹窗遮罩和弹窗 -->
  121. <view v-if="showDetailPanel" class="detail-popup-mask" @click.self="toggleDetailPanel">
  122. <view class="detail-popup" @click.stop>
  123. <view class="detail-popup-close" @click="toggleDetailPanel">×</view>
  124. <view class="green-tip-bar popup-green-tip">
  125. 回收范围仅支持回收以上品类按件回收预计比称重回收多
  126. <text class="tip-highlight"> 3.76</text>
  127. </view>
  128. <view class="panel-header">
  129. <text class="panel-title">已选商品明细</text>
  130. </view>
  131. <scroll-view class="panel-list popup-panel-list" scroll-y>
  132. <view v-for="(item, idx) in selectedProducts" :key="item.uniqueKey || idx" class="panel-item">
  133. <image v-if="item.image" :src="item.image" class="panel-item-img" mode="aspectFit" />
  134. <view class="panel-item-info">
  135. <text class="panel-item-name">{{item.name}}</text>
  136. <text class="panel-item-desc" v-if="item.brandName">品牌{{item.brandName}}</text>
  137. <text class="panel-item-desc" v-else>{{item.service}}</text>
  138. <text class="panel-item-price">¥{{item.price}}/{{ item.unit || '件' }}</text>
  139. </view>
  140. <view class="panel-quantity-control">
  141. <button class="btn-minus" @click="updateQuantityByProduct(item, -1)">-</button>
  142. <text class="quantity">{{item.quantity}}</text>
  143. <button class="btn-plus" @click="updateQuantityByProduct(item, 1)">+</button>
  144. </view>
  145. </view>
  146. </scroll-view>
  147. <view class="popup-bottom-bar">
  148. <view class="bottom-left">
  149. <view class="summary-row">
  150. <text class="summary-label">已选 <text class="summary-count">{{totalCount}}</text> {{ totalUnitText }} 预计回收可得</text>
  151. <uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" @tap="showPriceInfoPopups" />
  152. </view>
  153. <view class="amount-row">
  154. <uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @click="toggleDetailPanel" />
  155. <text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text>
  156. </view>
  157. </view>
  158. <button class="submit-btn" @click="submitOrder">预约上门取件</button>
  159. </view>
  160. </view>
  161. </view>
  162. <!-- 价格说明弹窗 -->
  163. <view v-if="showPriceInfoPopup" class="price-info-popup-mask" @click.self="closePriceInfoPopup">
  164. <view class="price-info-popup">
  165. <view class="price-info-popup-title">回收规则</view>
  166. <scroll-view class="price-info-popup-content" scroll-y>
  167. <!-- <view class="price-info-section">
  168. <view class="price-info-heading">关于旧衣质检</view>
  169. <view class="price-info-text">请确认本次回收旧衣是可以进行二次穿着的程度如回收旧衣有破损磨损开线变形起球发黄染色污渍配饰脱落或款式老旧等问题无法通过质检</view>
  170. </view>
  171. <view class="price-info-section">
  172. <view class="price-info-heading">质检报告</view>
  173. <view class="price-info-text">回收商收到衣后1-3个工作日内完成衣质检报告</view>
  174. </view>
  175. <view class="price-info-section">
  176. <view class="price-info-heading">质检结果与回收价格</view>
  177. <view class="price-info-text">若回收旧衣质检通过质检价格与用户提交订单时的预估价格一致回收商将按照预估价打款至您的小程序账户余额</view>
  178. </view> -->
  179. <uv-parse :content="huodong_text"></uv-parse>
  180. </scroll-view>
  181. <button class="price-info-popup-btn" @click="closePriceInfoPopup">我知道了</button>
  182. </view>
  183. </view>
  184. <!-- 根据角色显示不同的导航栏 -->
  185. <uv-tabbar
  186. v-if="ishow"
  187. :value="value"
  188. :fixed="true"
  189. @change="changeTo"
  190. class="uv-tabbar"
  191. >
  192. <uv-tabbar-item text="首页" >
  193. <template v-slot:active-icon>
  194. <image class="icon" src="/static/home/首页-点击.png"></image>
  195. </template>
  196. <template v-slot:inactive-icon>
  197. <image class="icon" src="/static/home/首页-未点击.png"></image>
  198. </template>
  199. </uv-tabbar-item>
  200. <uv-tabbar-item text="回收" >
  201. <template v-slot:active-icon>
  202. <image class="icon" src="/static/home/回收-点击.png"></image>
  203. </template>
  204. <template v-slot:inactive-icon>
  205. <image class="icon" src="/static/home/回收-未点击.png"></image>
  206. </template>
  207. </uv-tabbar-item>
  208. <uv-tabbar-item text="我的" >
  209. <template v-slot:active-icon>
  210. <image class="icon" src="/static/home/我的-点击.png"></image>
  211. </template>
  212. <template v-slot:inactive-icon>
  213. <image class="icon" src="/static/home/我的-未点击.png"></image>
  214. </template>
  215. </uv-tabbar-item>
  216. </uv-tabbar>
  217. <!-- 品牌索引弹窗 -->
  218. <view v-if="showBrandPopup" class="brand-popup-mask">
  219. <view class="brand-popup">
  220. <view class="brand-popup-header">
  221. <text class="brand-popup-close" @click="closeBrandPopup">关闭</text>
  222. <text class="brand-popup-title">可回收的品牌</text>
  223. </view>
  224. <view class="brand-popup-search">
  225. <input class="brand-search-input" v-model="brandSearch" placeholder="请输入要查询的内容" @input="onBrandSearchInput" />
  226. </view>
  227. <scroll-view class="brand-popup-list" scroll-y :scroll-into-view="scrollToView">
  228. <view v-for="letter in brandIndexList" :key="letter" :id="'brand-letter-' + letter">
  229. <view class="brand-letter">{{letter}}</view>
  230. <view v-for="brand in filteredBrandList.filter(b => b.letter === letter)" :key="brand.name" class="brand-item" @click="openBrandConfirm(brand)">
  231. <image :src="brand.logo" class="brand-logo" mode="aspectFit" />
  232. <text class="brand-name">{{brand.name}}</text>
  233. </view>
  234. </view>
  235. </scroll-view>
  236. <view class="brand-index-bar">
  237. <text v-for="letter in brandIndexList" :key="letter" :class="{active: currentLetter === letter}" @click="scrollToLetter(letter)">{{letter}}</text>
  238. </view>
  239. </view>
  240. </view>
  241. <!-- 回收规则弹窗 -->
  242. <view v-if="showRulePopup" class="rule-popup-mask" @click.self="closeRulePopup">
  243. <view class="rule-popup">
  244. <view class="rule-popup-title">回收规则</view>
  245. <scroll-view class="rule-popup-content" scroll-y>
  246. <uv-parse :content="ruleHtml"></uv-parse>
  247. </scroll-view>
  248. <button class="rule-popup-btn" @click="closeRulePopup">我知道了</button>
  249. <!-- <view class="rule-popup-close" @tap="closeRulePopup">
  250. <uni-icons type="close" size="36" color="#fff" />
  251. </view> -->
  252. </view>
  253. </view>
  254. <!-- 预约上门取件弹窗 -->
  255. <view v-if="showPickupConfirm" class="pickup-confirm-mask">
  256. <view class="pickup-confirm-popup">
  257. <view class="pickup-confirm-title">温馨提示</view>
  258. <view class="pickup-confirm-content">
  259. <uv-parse :content="recycle_toast"></uv-parse>
  260. <!-- <view class="confirm-item confirm-item-red">1.当前回收快递免费上门由于快递成本较高为避免不必要的成本及资源二次浪费不属于可回收品类或不符合回收标准的物品请勿寄出</view>
  261. <view class="confirm-item">2.已通过的回收物品将正常结算不符合回收要求的物品可选择快递取回运费自付请在订单结算后48小时内联系在线客服安排取回逾期未联系将默认捐赠无法再次取回</view>
  262. <view class="confirm-item">3.若用户寄出大量不可回收的物品平台有权限制下次回收权限或取消下次包邮服务</view>
  263. <view class="confirm-item">4.对于合格率高的回收订单平台将根据实际情况给予额外回收奖励</view> -->
  264. </view>
  265. <view class="pickup-confirm-btn-row">
  266. <button class="pickup-confirm-btn" @click="handlePickupCancel">取消回收</button>
  267. <button class="pickup-confirm-btn agree" @click="handlePickupAgree">我同意</button>
  268. </view>
  269. </view>
  270. </view>
  271. <!-- 品牌确认弹窗 -->
  272. <view v-if="showBrandConfirm" class="brand-confirm-mask" @click.self="closeBrandConfirm">
  273. <view class="brand-confirm-popup">
  274. <view class="brand-confirm-title">品牌确认提示</view>
  275. <view class="brand-confirm-logo-wrap">
  276. <image :src="brandConfirmInfo.logo" class="brand-confirm-logo" mode="aspectFit" />
  277. </view>
  278. <view class="brand-confirm-name">{{ brandConfirmInfo.name }}</view>
  279. <view class="brand-confirm-desc">请确认所选品牌是否与实物品牌信息一致否则将无法进行回收</view>
  280. <view class="brand-confirm-btn-row">
  281. <button class="brand-confirm-btn retry" @click="closeBrandConfirm">重新选择</button>
  282. <button class="brand-confirm-btn confirm" @click="confirmBrand">确认一致</button>
  283. </view>
  284. </view>
  285. </view>
  286. <!-- 减少数量时的品牌选择弹窗 -->
  287. <view v-if="showBrandReducePopup" class="brand-reduce-popup-mask" @click.self="closeBrandReducePopup">
  288. <view class="brand-reduce-popup">
  289. <view class="brand-reduce-popup-header">
  290. <text class="brand-reduce-popup-close" @click="closeBrandReducePopup">关闭</text>
  291. <text class="brand-reduce-popup-title">选择要减少的品牌</text>
  292. </view>
  293. <scroll-view class="brand-reduce-popup-list" scroll-y>
  294. <view v-for="brand in reduceBrandList" :key="brand.brandId" class="brand-item" @click="selectReduceBrand(brand)">
  295. <image :src="brand.logo" class="brand-logo" mode="aspectFit" />
  296. <text class="brand-name">{{brand.name}}</text>
  297. </view>
  298. </scroll-view>
  299. </view>
  300. </view>
  301. </view>
  302. </template>
  303. <script>
  304. import tabBarMixin from '../mixins/tabBarMixin.js'
  305. import { pinyin } from '../../utils/pinyin.js'
  306. export default {
  307. mixins: [tabBarMixin],
  308. data() {
  309. return {
  310. value:1,
  311. ishow:true,
  312. // 动态数据
  313. allProducts: {}, // { [categoryId]: [商品数组] }
  314. allProductsPage: {}, // { [categoryId]: 当前已加载页码 }
  315. allProductsTotal: {}, // { [categoryId]: 总数 }
  316. pageSize: 10,
  317. currentCategory: 0,
  318. tabbarHeight: 0,
  319. showDetailPanel: false,
  320. showBrandPopup: false,
  321. showRulePopup: false,
  322. ruleImgUrl: '/static/回收/回收规则.png',
  323. showPickupConfirm: false,
  324. showBrandConfirm: false,
  325. brandConfirmInfo: {
  326. logo: '',
  327. name: ''
  328. },
  329. brandList: [],
  330. brandIndexList: ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'],
  331. currentLetter: 'A',
  332. scrollToView: '',
  333. brandSearch: '',
  334. ruleHtml: '', // 回收规则富文本内容
  335. loadingMore: false,
  336. finished: false,
  337. pendingBrandIndex: null, // 记录待加一的品牌商品index
  338. showPriceInfoPopup: false,
  339. isWaitingForBrandSelection: false, // 等待品牌选择的标志
  340. showBrandReducePopup: false, // 显示减少数量时的品牌选择弹窗
  341. reduceItem: null, // 待减少数量的商品
  342. reduceBrandList: [], // 可减少的品牌列表
  343. viewedRuleItems: new Set(), // 已查看过规则的商品ID集合
  344. loadOptions: null, // 保存options参数
  345. userInfo: null, // 用户信息
  346. isUserBlacklisted: false, // 用户是否被拉黑
  347. currentProductId: null, // 当前查看品牌的商品ID
  348. searchTimer: null, // 搜索防抖定时器
  349. }
  350. },
  351. computed: {
  352. recycle_banner() {
  353. const item = getApp().globalData.configData.find(i => i.keyName === 'recycle_banner')
  354. return item ? item.keyContent : ''
  355. },
  356. recycle_toast() {
  357. const item = getApp().globalData.configData.find(i => i.keyName === 'recycle_toast')
  358. return item ? item.keyContent : ''
  359. },
  360. huodong_text() {
  361. const item = getApp().globalData.configData.find(i => i.keyName === 'huodong_text')
  362. return item ? item.keyContent : ''
  363. },
  364. // 当前分类的商品列表
  365. recycleList() {
  366. const currentCategoryId = this.categories[this.currentCategory]?.id
  367. return this.allProducts[currentCategoryId] || []
  368. },
  369. // 计算总数量
  370. totalCount() {
  371. return Object.values(this.allProducts).reduce((total, categoryItems) => {
  372. return total + categoryItems.reduce((sum, item) => {
  373. // 如果商品有品牌数量,汇总所有品牌的数量
  374. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  375. return sum + Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
  376. }
  377. // 否则使用原来的quantity字段
  378. return sum + (item.quantity || 0)
  379. }, 0)
  380. }, 0)
  381. },
  382. // 计算总单位文本
  383. totalUnitText() {
  384. // 获取所有已选商品的单位
  385. const units = new Set()
  386. Object.values(this.allProducts).forEach(categoryItems => {
  387. categoryItems.forEach(item => {
  388. let hasQuantity = false
  389. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  390. hasQuantity = Object.values(item.brandQuantities).some(qty => qty > 0)
  391. } else {
  392. hasQuantity = (item.quantity || 0) > 0
  393. }
  394. if (hasQuantity) {
  395. units.add(item.unit || '件')
  396. }
  397. })
  398. })
  399. // 如果只有一种单位,显示单位;如果混合多种单位,显示"项"
  400. if (units.size === 0) return '件'
  401. if (units.size === 1) return Array.from(units)[0]
  402. return '项'
  403. },
  404. // 计算总价格范围
  405. totalPriceRange() {
  406. const result = Object.values(this.allProducts).reduce((categoryTotal, categoryItems) => {
  407. return categoryItems.reduce((sum, item) => {
  408. let itemQuantity = 0
  409. // 如果商品有品牌数量,汇总所有品牌的数量
  410. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  411. itemQuantity = Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
  412. } else {
  413. itemQuantity = item.quantity || 0
  414. }
  415. if (itemQuantity > 0) {
  416. const minPrice = Number(item.price) || 0
  417. const maxPrice = Number(item.maxPrice) || Number(item.price) || 0
  418. sum.min += itemQuantity * minPrice
  419. sum.max += itemQuantity * maxPrice
  420. }
  421. return sum
  422. }, categoryTotal)
  423. }, { min: 0, max: 0 })
  424. return {
  425. min: result.min.toFixed(1),
  426. max: result.max.toFixed(1)
  427. }
  428. },
  429. // 计算总价格 (保持兼容性,使用最低价格)
  430. totalPrice() {
  431. return this.totalPriceRange.min
  432. },
  433. // 计算价格范围
  434. priceRange() {
  435. if (this.totalCount === 0) {
  436. return {
  437. min: '0.0',
  438. max: '0.0'
  439. }
  440. }
  441. return this.totalPriceRange
  442. },
  443. selectedProducts() {
  444. // 返回所有分类下所有已选商品,按品牌分组
  445. const products = []
  446. Object.values(this.allProducts).flat().forEach(item => {
  447. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  448. // 按品牌分别添加
  449. Object.entries(item.brandQuantities).forEach(([brandId, quantity]) => {
  450. if (quantity > 0) {
  451. const brandInfo = this.getBrandInfo(brandId)
  452. products.push({
  453. ...item,
  454. quantity: quantity,
  455. brandId: brandId,
  456. brandName: brandInfo ? brandInfo.name : '未知品牌',
  457. brandImage: brandInfo ? brandInfo.logo : '',
  458. uniqueKey: `${item.id}_${brandId}` // 用于区分同商品不同品牌
  459. })
  460. }
  461. })
  462. } else if (item.quantity > 0) {
  463. // 没有品牌的商品
  464. products.push({
  465. ...item,
  466. uniqueKey: item.id
  467. })
  468. }
  469. })
  470. return products
  471. },
  472. filteredBrandList() {
  473. return this.brandList
  474. },
  475. bannerList() {
  476. return getApp().globalData.bannerList || []
  477. },
  478. categories() {
  479. const list = getApp().globalData.pricePreviewList || []
  480. return list.filter(item => item.pid === '0').sort((a, b) => a.sort - b.sort)
  481. },
  482. minMoney() {
  483. const config = getApp().globalData.configData || [];
  484. const item = config.find(i => i.keyName === 'min_money');
  485. return item ? parseFloat(item.keyContent) : 0;
  486. }
  487. },
  488. methods: {
  489. showPriceInfoPopups() {
  490. console.log('showPriceInfoPopup called');
  491. this.showPriceInfoPopup = true
  492. },
  493. closePriceInfoPopup() {
  494. console.log('closePriceInfoPopup called');
  495. this.showPriceInfoPopup = false
  496. },
  497. changeTo(e){
  498. this.value = e
  499. if(e==2){
  500. uni.reLaunch({
  501. url: '/pages/component/my'
  502. });
  503. }else if(e==0){
  504. console.log(e,'111')
  505. uni.reLaunch({
  506. url: '/pages/component/home'
  507. });
  508. }
  509. },
  510. fetchGoodsList(categoryId, page = 1, callback) {
  511. this.$api('getClassGoodsList', {
  512. classId: categoryId,
  513. pageNo: page,
  514. pageSize: this.pageSize
  515. }, res => {
  516. if (res.code === 200 && res.result && Array.isArray(res.result.records)) {
  517. const oldList = this.allProducts[categoryId] || []
  518. const newList = page === 1 ? res.result.records : oldList.concat(res.result.records)
  519. this.$set(this.allProducts, categoryId, newList)
  520. this.$set(this.allProductsPage, categoryId, page)
  521. this.$set(this.allProductsTotal, categoryId, res.result.total)
  522. }
  523. if (callback) callback()
  524. })
  525. },
  526. // 获取分类商品总数
  527. getCategoryItemCount(index) {
  528. const categoryId = this.categories[index]?.id
  529. const categoryItems = this.allProducts[categoryId] || []
  530. return categoryItems.reduce((sum, item) => {
  531. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  532. return sum + Object.values(item.brandQuantities).reduce((brandSum, qty) => brandSum + qty, 0)
  533. }
  534. return sum + (item.quantity || 0)
  535. }, 0)
  536. },
  537. // 切换分类
  538. switchCategory(index) {
  539. this.currentCategory = index
  540. this.loadingMore = false
  541. this.finished = false
  542. const categoryId = this.categories[index]?.id
  543. // console.log(categoryId,'switchCategory')
  544. if (!this.allProducts[categoryId]) {
  545. this.fetchGoodsList(categoryId, 1)
  546. }
  547. },
  548. // 更新商品数量
  549. updateQuantity(index, delta) {
  550. const categoryId = this.categories[this.currentCategory]?.id
  551. const item = this.allProducts[categoryId]?.[index]
  552. if (!item) return
  553. // 如果是减少数量且delta为负数
  554. if (delta < 0) {
  555. // 检查是否有多个品牌
  556. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 1) {
  557. // 有多个品牌,显示选择弹窗
  558. this.reduceItem = { item, index, delta }
  559. this.reduceBrandList = Object.entries(item.brandQuantities)
  560. .filter(([brandId, quantity]) => quantity > 0)
  561. .map(([brandId, quantity]) => {
  562. const brandInfo = this.getBrandInfo(brandId)
  563. return {
  564. brandId,
  565. quantity,
  566. name: brandInfo ? brandInfo.name : '未知品牌',
  567. logo: brandInfo ? brandInfo.logo : ''
  568. }
  569. })
  570. this.showBrandReducePopup = true
  571. return
  572. } else if (item.brandQuantities && Object.keys(item.brandQuantities).length === 1) {
  573. // 只有一个品牌,直接减少
  574. const brandId = Object.keys(item.brandQuantities)[0]
  575. const currentQty = item.brandQuantities[brandId] || 0
  576. const newQty = Math.max(0, currentQty + delta)
  577. this.$set(item.brandQuantities, brandId, newQty)
  578. // 如果数量为0,删除该品牌
  579. if (newQty === 0) {
  580. this.$delete(item.brandQuantities, brandId)
  581. }
  582. return
  583. } else {
  584. // 没有品牌数量,使用原来的逻辑
  585. let newQuantity = (item.quantity || 0) + delta
  586. if (newQuantity < 0) newQuantity = 0
  587. this.$set(item, 'quantity', newQuantity)
  588. return
  589. }
  590. }
  591. // 品牌商品且数量为0且加一时
  592. if ((item.quantity || 0) === 0 && delta > 0) {
  593. this.pendingBrandIndex = index
  594. this.isWaitingForBrandSelection = true;
  595. this.showRules(item); // 先显示回收规则
  596. return
  597. }
  598. },
  599. // 选择要减少的品牌
  600. selectReduceBrand(brandInfo) {
  601. const { item, index, delta } = this.reduceItem
  602. const currentQty = item.brandQuantities[brandInfo.brandId] || 0
  603. const newQty = Math.max(0, currentQty + delta)
  604. this.$set(item.brandQuantities, brandInfo.brandId, newQty)
  605. // 如果数量为0,删除该品牌
  606. if (newQty === 0) {
  607. this.$delete(item.brandQuantities, brandInfo.brandId)
  608. }
  609. this.closeBrandReducePopup()
  610. },
  611. // 关闭减少品牌选择弹窗
  612. closeBrandReducePopup() {
  613. this.showBrandReducePopup = false
  614. this.reduceItem = null
  615. this.reduceBrandList = []
  616. },
  617. // 获取品牌信息
  618. getBrandInfo(brandId) {
  619. return this.brandList.find(brand => brand.id === brandId)
  620. },
  621. // 获取商品的总数量(所有品牌)
  622. getItemTotalQuantity(item) {
  623. if (item.brandQuantities && Object.keys(item.brandQuantities).length > 0) {
  624. return Object.values(item.brandQuantities).reduce((sum, qty) => sum + qty, 0)
  625. }
  626. return item.quantity || 0
  627. },
  628. // 显示回收规则
  629. showRules(item) {
  630. // 检查该商品是否已经查看过规则
  631. if (this.viewedRuleItems.has(item.id)) {
  632. // 如果已经查看过,直接跳过规则弹窗,进入品牌选择
  633. this.isWaitingForBrandSelection = false;
  634. this.getGoodsBrandList(item.id);
  635. this.showBrandPopup = true;
  636. return;
  637. }
  638. // 获取回收规则富文本
  639. this.$api('getGoodsRecycleRule', { goodsId: item.id }, res => {
  640. if (res.code === 200 && res.result) {
  641. this.ruleHtml = res.result
  642. } else {
  643. this.ruleHtml = '<p>暂无回收规则</p>'
  644. }
  645. this.showRulePopup = true
  646. })
  647. },
  648. showMore() {
  649. uni.showToast({
  650. title: '更多规则请咨询客服',
  651. icon: 'none'
  652. })
  653. },
  654. submitOrder() {
  655. // 检查用户是否被拉入黑名单
  656. if (this.isUserBlacklisted) {
  657. uni.showModal({
  658. title: '提示',
  659. content: '您的账户已被限制使用回收服务,如有疑问请联系客服。',
  660. showCancel: false,
  661. confirmText: '我知道了'
  662. })
  663. return
  664. }
  665. if (this.totalCount < 3) {
  666. uni.showToast({
  667. title: `各品类混合需要满3${this.totalUnitText}才能回收哦`,
  668. icon: 'none'
  669. })
  670. return
  671. }
  672. if (Number(this.totalPrice) < this.minMoney) {
  673. uni.showToast({
  674. title: `回收总价需满${this.minMoney}元才能预约`,
  675. icon: 'none'
  676. })
  677. return
  678. }
  679. this.showPickupConfirm = true;
  680. },
  681. handlePickupCancel() {
  682. this.showPickupConfirm = false;
  683. },
  684. handlePickupAgree() {
  685. this.showPickupConfirm = false;
  686. uni.showLoading({
  687. title: '提交中...'
  688. })
  689. setTimeout(() => {
  690. uni.hideLoading()
  691. uni.showToast({
  692. title: '预约成功',
  693. icon: 'success'
  694. })
  695. this.goToPickup()
  696. }, 1500)
  697. },
  698. goToPickup() {
  699. // 获取所有选中的衣物(所有分类)
  700. const selectedItems = this.selectedProducts.map(item => {
  701. const baseItem = {
  702. id: item.id,
  703. name: item.name,
  704. icon: item.image,
  705. quantity: item.quantity,
  706. unitPrice: item.price,
  707. maxPrice : item.maxPrice,
  708. desc: item.brandName ? `品牌:${item.brandName}` : '允许脏破烂,160码以上'
  709. }
  710. // 如果有品牌信息,添加品牌相关字段
  711. if (item.brandId) {
  712. baseItem.brandId = item.brandId
  713. baseItem.brandName = item.brandName
  714. baseItem.brandImage = item.brandImage
  715. }
  716. return baseItem
  717. })
  718. const itemsStr = encodeURIComponent(JSON.stringify(selectedItems))
  719. uni.navigateTo({
  720. url: `/pages/subcomponent/pickup?fromRecycle=true&items=${itemsStr}`
  721. })
  722. },
  723. checkBrand(index) {
  724. const categoryId = this.categories[this.currentCategory]?.id
  725. const item = this.allProducts[categoryId]?.[index]
  726. if (item?.id) {
  727. this.pendingBrandIndex = index
  728. this.getGoodsBrandList(item.id)
  729. this.showBrandPopup = true
  730. }
  731. },
  732. closeBrandPopup() {
  733. this.showBrandPopup = false
  734. // 如果用户取消品牌选择,重置状态
  735. this.pendingBrandIndex = null;
  736. this.isWaitingForBrandSelection = false;
  737. // 清理搜索状态
  738. this.brandSearch = ''
  739. this.currentProductId = null
  740. if (this.searchTimer) {
  741. clearTimeout(this.searchTimer)
  742. this.searchTimer = null
  743. }
  744. },
  745. scrollToLetter(letter) {
  746. this.currentLetter = letter
  747. this.scrollToView = 'brand-letter-' + letter
  748. },
  749. // 添加下拉刷新方法
  750. async refreshData() {
  751. try {
  752. // 这里可以添加刷新数据的逻辑,比如重新获取商品列表等
  753. // 示例:重新初始化数据
  754. this.currentCategory = 0
  755. Object.values(this.allProducts).forEach(categoryItems => {
  756. categoryItems.forEach(item => {
  757. item.quantity = 0
  758. if (item.brandQuantities) {
  759. item.brandQuantities = {}
  760. }
  761. })
  762. })
  763. // 清空已查看规则的记录
  764. this.viewedRuleItems.clear()
  765. // 模拟网络请求延迟
  766. await new Promise(resolve => setTimeout(resolve, 1000))
  767. uni.showToast({
  768. title: '刷新成功',
  769. icon: 'success'
  770. })
  771. } catch (error) {
  772. uni.showToast({
  773. title: '刷新失败',
  774. icon: 'none'
  775. })
  776. } finally {
  777. // 停止下拉刷新动画
  778. uni.stopPullDownRefresh()
  779. }
  780. },
  781. toggleDetailPanel() {
  782. this.showDetailPanel = !this.showDetailPanel
  783. },
  784. fetchUserInfo() {
  785. if(uni.getStorageSync('token')){
  786. this.login_status = getApp().globalData.login_status;
  787. this.$api("getUserByToken",{},(res)=>{
  788. if(res.code == 200){
  789. this.userInfo = res.result
  790. // 检查用户是否被拉入黑名单
  791. this.isUserBlacklisted = res.result.isBlack === 'Y'
  792. // isTuiType 为0用户,1推广达人,2推广大使
  793. }
  794. })
  795. } else {
  796. this.login_status = false;
  797. }
  798. },
  799. updateQuantityByProduct(item, delta) {
  800. // 在明细弹窗中更新数量
  801. if (item.brandId) {
  802. // 有品牌ID的商品
  803. const originalItem = this.findOriginalItem(item.id)
  804. if (originalItem && originalItem.brandQuantities) {
  805. const currentQty = originalItem.brandQuantities[item.brandId] || 0
  806. const newQty = Math.max(0, currentQty + delta)
  807. this.$set(originalItem.brandQuantities, item.brandId, newQty)
  808. // 如果数量为0,删除该品牌
  809. if (newQty === 0) {
  810. this.$delete(originalItem.brandQuantities, item.brandId)
  811. }
  812. // 同步更新显示的数量
  813. item.quantity = newQty
  814. }
  815. } else {
  816. // 没有品牌的商品
  817. if (!item.quantity) item.quantity = 0
  818. item.quantity += delta
  819. if (item.quantity < 0) item.quantity = 0
  820. // 同步到原商品
  821. const originalItem = this.findOriginalItem(item.id)
  822. if (originalItem) {
  823. this.$set(originalItem, 'quantity', item.quantity)
  824. }
  825. }
  826. },
  827. // 查找原始商品对象
  828. findOriginalItem(itemId) {
  829. for (const categoryItems of Object.values(this.allProducts)) {
  830. const item = categoryItems.find(i => i.id === itemId)
  831. if (item) return item
  832. }
  833. return null
  834. },
  835. openRulePopup() {
  836. this.showRulePopup = true
  837. },
  838. closeRulePopup() {
  839. this.showRulePopup = false;
  840. // 如果是在等待品牌选择的状态下关闭规则弹窗,则接着打开品牌选择
  841. if (this.isWaitingForBrandSelection) {
  842. this.isWaitingForBrandSelection = false; // 清除等待状态
  843. const categoryId = this.categories[this.currentCategory]?.id;
  844. const item = this.allProducts[categoryId]?.[this.pendingBrandIndex];
  845. // 记录该商品的规则已被查看
  846. this.viewedRuleItems.add(item.id);
  847. this.getGoodsBrandList(item.id);
  848. this.showBrandPopup = true; // 打开品牌索引弹窗
  849. }
  850. },
  851. loadMoreGoods() {
  852. const categoryId = this.categories[this.currentCategory]?.id
  853. const page = (this.allProductsPage[categoryId] || 1) + 1
  854. const total = this.allProductsTotal[categoryId] || 0
  855. const loaded = (this.allProducts[categoryId] || []).length
  856. if (this.loadingMore || this.finished) return
  857. if (loaded < total) {
  858. this.loadingMore = true
  859. this.fetchGoodsList(categoryId, page, () => {
  860. this.loadingMore = false
  861. // 判断是否加载完
  862. const newLoaded = (this.allProducts[categoryId] || []).length
  863. this.finished = newLoaded >= (this.allProductsTotal[categoryId] || 0)
  864. })
  865. } else {
  866. this.finished = true
  867. }
  868. },
  869. openBrandConfirm(brand) {
  870. this.brandConfirmInfo = {
  871. id: brand.id,
  872. logo: brand.logo,
  873. name: brand.name
  874. }
  875. this.showBrandConfirm = true
  876. },
  877. closeBrandConfirm() {
  878. this.showBrandConfirm = false
  879. },
  880. confirmBrand() {
  881. this.showBrandConfirm = false
  882. this.showBrandPopup = false
  883. // 确认后将待加一的商品数量+1,并记录品牌ID
  884. if (this.pendingBrandIndex !== null) {
  885. const categoryId = this.categories[this.currentCategory]?.id
  886. const item = this.allProducts[categoryId]?.[this.pendingBrandIndex]
  887. if (item) {
  888. // 初始化品牌数量对象
  889. if (!item.brandQuantities) {
  890. this.$set(item, 'brandQuantities', {})
  891. }
  892. // 增加该品牌的数量
  893. const currentQty = item.brandQuantities[this.brandConfirmInfo.id] || 0
  894. this.$set(item.brandQuantities, this.brandConfirmInfo.id, currentQty + 1)
  895. // 清除原来的quantity(如果存在)
  896. if (item.quantity) {
  897. this.$set(item, 'quantity', 0)
  898. }
  899. }
  900. this.pendingBrandIndex = null
  901. }
  902. },
  903. getGoodsBrandList(productId, searchName = '') {
  904. this.currentProductId = productId
  905. const params = { productId }
  906. if (searchName.trim()) {
  907. params.name = searchName.trim()
  908. }
  909. this.$api('getGoodsBrandList', params, res => {
  910. // console.log(res,'res')
  911. if (res && res.success && res.result && res.result.records) {
  912. this.brandList = res.result.records.map(item => {
  913. // 获取品牌名称的拼音首字母
  914. const firstChar = this.getPinyinFirstLetter(item.name)
  915. return {
  916. id: item.id,
  917. logo: item.image || '/static/brand/alexander.png',
  918. name: item.name,
  919. letter: firstChar
  920. }
  921. })
  922. // console.log(this.brandList,'this.brandList')
  923. }
  924. })
  925. },
  926. // 获取中文拼音首字母
  927. getPinyinFirstLetter(str) {
  928. if (!str) return '#'
  929. const firstChar = str.charAt(0)
  930. // 遍历pinyin对象,查找包含该汉字的拼音
  931. for (let key in pinyin) {
  932. const chars = pinyin[key]
  933. if (chars && chars.indexOf(firstChar) !== -1) {
  934. return key.charAt(0).toUpperCase()
  935. }
  936. }
  937. let index = this.brandIndexList.indexOf(firstChar.toUpperCase())
  938. if (index != -1) {
  939. return this.brandIndexList[index]
  940. }
  941. return '#'
  942. },
  943. // 品牌搜索输入事件处理
  944. onBrandSearchInput(e) {
  945. const searchValue = e.detail.value
  946. // 清除之前的定时器
  947. if (this.searchTimer) {
  948. clearTimeout(this.searchTimer)
  949. }
  950. // 设置防抖,500ms后执行搜索
  951. this.searchTimer = setTimeout(() => {
  952. if (this.currentProductId) {
  953. this.getGoodsBrandList(this.currentProductId, searchValue)
  954. }
  955. }, 500)
  956. },
  957. // 初始化页面数据的方法
  958. initializePageData() {
  959. const options = this.loadOptions || {}
  960. if (options && options.categoryId) {
  961. const idx = this.categories.findIndex(c => c.id == options.categoryId)
  962. if (idx !== -1) this.currentCategory = idx
  963. }
  964. if (this.categories.length > 0) {
  965. this.fetchGoodsList(this.categories[this.currentCategory].id, 1)
  966. }
  967. uni.$on('bannerListUpdated', () => {
  968. this.$forceUpdate && this.$forceUpdate()
  969. })
  970. if (getApp().globalData.bannerList && getApp().globalData.bannerList.length > 0) {
  971. this.$forceUpdate && this.$forceUpdate()
  972. }
  973. // 检查全局清空标志(兼容 reLaunch)
  974. if (getApp().globalData.shouldClearRecycle) {
  975. Object.values(this.allProducts).forEach(categoryItems => {
  976. categoryItems.forEach(item => {
  977. this.$set(item, 'quantity', 0)
  978. if (item.brandQuantities) {
  979. this.$set(item, 'brandQuantities', {})
  980. }
  981. })
  982. })
  983. // 清空已查看规则的记录
  984. this.viewedRuleItems.clear()
  985. this.showDetailPanel = false
  986. this.$forceUpdate()
  987. getApp().globalData.shouldClearRecycle = false
  988. }
  989. },
  990. },
  991. created() {
  992. this.currentCategory = 0
  993. this.$nextTick(() => {
  994. if (this.categories.length > 0) {
  995. const firstCategoryId = this.categories[0]?.id
  996. if (firstCategoryId) {
  997. this.fetchGoodsList(firstCategoryId, 1)
  998. }
  999. }
  1000. })
  1001. },
  1002. mounted() {
  1003. this.$nextTick(() => {
  1004. const query = uni.createSelectorQuery().in(this)
  1005. query.select('.uv-tabbar').boundingClientRect(rect => {
  1006. if (rect && rect.height) {
  1007. this.tabbarHeight = rect.height
  1008. } else {
  1009. this.tabbarHeight = uni.upx2px ? uni.upx2px(95) : 45
  1010. }
  1011. // console.log(this.tabbarHeight,'tabbarHeight')
  1012. }).exec()
  1013. })
  1014. },
  1015. onLoad(options) {
  1016. // 保存options参数
  1017. this.loadOptions = options
  1018. // 检查App数据是否已经准备好
  1019. if (!getApp().globalData.isAppDataReady) {
  1020. // 显示加载状态
  1021. uni.showLoading({
  1022. title: '加载中...',
  1023. mask: true
  1024. })
  1025. // 监听App数据准备完成事件
  1026. uni.$on('appDataReady', () => {
  1027. uni.hideLoading()
  1028. this.initializePageData()
  1029. })
  1030. } else {
  1031. // App数据已经准备好,直接初始化页面数据
  1032. this.initializePageData()
  1033. }
  1034. },
  1035. onUnload() {
  1036. uni.$off('bannerListUpdated')
  1037. // 移除事件监听
  1038. uni.$off('clearRecycleOrderData')
  1039. },
  1040. onShow() {
  1041. // 获取用户信息,检查黑名单状态
  1042. this.fetchUserInfo()
  1043. const id = getApp().globalData.targetRecycleCategoryId
  1044. if (id) {
  1045. const trySwitch = () => {
  1046. if (this.categories.length > 0) {
  1047. const idx = this.categories.findIndex(c => String(c.id) === String(id))
  1048. if (idx !== -1) {
  1049. this.currentCategory = idx
  1050. const categoryId = this.categories[idx]?.id
  1051. if (categoryId && !this.allProducts[categoryId]) {
  1052. this.loadingMore = false
  1053. this.finished = false
  1054. this.fetchGoodsList(categoryId, 1)
  1055. }
  1056. }
  1057. getApp().globalData.targetRecycleCategoryId = null
  1058. } else {
  1059. setTimeout(trySwitch, 100)
  1060. }
  1061. }
  1062. trySwitch()
  1063. }
  1064. // 检查全局清空标志
  1065. if (getApp().globalData.shouldClearRecycle) {
  1066. Object.values(this.allProducts).forEach(categoryItems => {
  1067. categoryItems.forEach(item => {
  1068. this.$set(item, 'quantity', 0)
  1069. if (item.brandQuantities) {
  1070. this.$set(item, 'brandQuantities', {})
  1071. }
  1072. })
  1073. })
  1074. // 清空已查看规则的记录
  1075. this.viewedRuleItems.clear()
  1076. this.showDetailPanel = false
  1077. this.$forceUpdate()
  1078. getApp().globalData.shouldClearRecycle = false
  1079. }
  1080. // 监听清除订单数据的事件
  1081. uni.$on('clearRecycleOrderData', () => {
  1082. // 清除所有商品的选中数量,保证响应式
  1083. Object.values(this.allProducts).forEach(categoryItems => {
  1084. categoryItems.forEach(item => {
  1085. this.$set(item, 'quantity', 0)
  1086. if (item.brandQuantities) {
  1087. this.$set(item, 'brandQuantities', {})
  1088. }
  1089. })
  1090. })
  1091. // 清空已查看规则的记录
  1092. this.viewedRuleItems.clear()
  1093. // 重置其他相关数据
  1094. this.showDetailPanel = false
  1095. this.$forceUpdate()
  1096. })
  1097. },
  1098. watch: {
  1099. categories(newVal) {
  1100. const id = getApp().globalData.targetRecycleCategoryId
  1101. const idx = newVal.findIndex(c => String(c.id) === String(id))
  1102. if (id && newVal.length > 0 && idx !== -1) {
  1103. this.currentCategory = idx
  1104. getApp().globalData.targetRecycleCategoryId = null
  1105. // 自动加载右侧商品
  1106. const categoryId = newVal[idx]?.id
  1107. if (categoryId && !this.allProducts[categoryId]) {
  1108. this.loadingMore = false
  1109. this.finished = false
  1110. this.fetchGoodsList(categoryId, 1)
  1111. }
  1112. }
  1113. }
  1114. },
  1115. }
  1116. </script>
  1117. <style lang="scss" scoped>
  1118. .container {
  1119. display: flex;
  1120. flex-direction: column;
  1121. height: 100vh;
  1122. background-color: #f5f5f5;
  1123. overflow: hidden;
  1124. }
  1125. .banner {
  1126. background: linear-gradient(135deg, #ff9500,#ff5e00);
  1127. position: relative;
  1128. height: 350rpx;
  1129. z-index: 1;
  1130. image {
  1131. width: 100%;
  1132. height: 90%;
  1133. }
  1134. }
  1135. .goods-list {
  1136. // flex: 1;
  1137. display: flex;
  1138. position: relative;
  1139. height: calc(100vh - 320rpx - 120rpx - env(safe-area-inset-bottom)); /* 减去banner和底部栏的高度 */
  1140. margin-top: -10rpx;
  1141. z-index: 2;
  1142. border-radius: 20rpx 20rpx 0 0;
  1143. overflow: hidden;
  1144. padding: 30rpx;
  1145. box-shadow: 0 -4rpx 8rpx rgba(0, 0, 0, 0.05);
  1146. background: linear-gradient(to bottom, #fff7e8, 20%,#ffffff);
  1147. .category-nav {
  1148. width: 20%;
  1149. background: #ffffff;
  1150. height: 100%;
  1151. border-right: 1rpx solid rgba(255, 126, 14, 0.1);
  1152. margin: 1rpx;
  1153. border-radius: 20rpx 0 0 0;
  1154. margin-right: 20rpx;
  1155. overflow-y: auto;
  1156. scrollbar-width: none; /* Firefox */
  1157. -ms-overflow-style: none; /* IE and Edge */
  1158. &::-webkit-scrollbar {
  1159. width: 0 !important;
  1160. display: none; /* Chrome, Safari, Opera */
  1161. }
  1162. .category-item {
  1163. position: relative;
  1164. padding: 28rpx 20rpx;
  1165. text-align: center;
  1166. font-family: PingFang SC;
  1167. font-weight: 600;
  1168. font-size: 15px;
  1169. line-height: 100%;
  1170. letter-spacing: 0px;
  1171. color: #666;
  1172. .category-dot {
  1173. position: absolute;
  1174. top: 15rpx;
  1175. right: 15rpx;
  1176. min-width: 32rpx;
  1177. height: 32rpx;
  1178. padding: 0 6rpx;
  1179. background: #ff7a0e;
  1180. border-radius: 16rpx;
  1181. color: #fff;
  1182. font-size: 20rpx;
  1183. text-align: center;
  1184. line-height: 32rpx;
  1185. box-sizing: border-box;
  1186. }
  1187. &.active {
  1188. color: #ff7a0e;
  1189. font-weight: bold;
  1190. background: #fff7e8;
  1191. position: relative;
  1192. &::before {
  1193. content: '';
  1194. position: absolute;
  1195. left: 0;
  1196. top: 50%;
  1197. transform: translateY(-50%);
  1198. width: 6rpx;
  1199. height: 36rpx;
  1200. background: #ff7a0e;
  1201. border-radius: 3rpx;
  1202. }
  1203. }
  1204. }
  1205. }
  1206. .goods-content {
  1207. flex: 1;
  1208. height: 100%;
  1209. padding: 0 30rpx;
  1210. background: #ffffff;
  1211. width: 70%;
  1212. margin: 1rpx;
  1213. margin-left: 0;
  1214. border-radius: 0 20rpx 0 0;
  1215. overflow-y: auto;
  1216. scrollbar-width: none; /* Firefox */
  1217. -ms-overflow-style: none; /* IE and Edge */
  1218. &::-webkit-scrollbar {
  1219. width: 0 !important;
  1220. display: none; /* Chrome, Safari, Opera */
  1221. }
  1222. }
  1223. }
  1224. .goods-item {
  1225. display: flex;
  1226. align-items: flex-start;
  1227. padding: 30rpx 0;
  1228. border-bottom: 1rpx solid #f5f5f5;
  1229. .goods-item-img {
  1230. width: 120rpx;
  1231. height: 120rpx;
  1232. border-radius: 24rpx;
  1233. background: #f8f8f8;
  1234. margin-right: 28rpx;
  1235. object-fit: contain;
  1236. flex-shrink: 0;
  1237. }
  1238. .goods-info-wrap {
  1239. flex: 1;
  1240. display: flex;
  1241. flex-direction: column;
  1242. justify-content: center;
  1243. min-width: 0;
  1244. }
  1245. .goods-header {
  1246. display: flex;
  1247. justify-content: space-between;
  1248. align-items: center;
  1249. margin-bottom: 10rpx;
  1250. }
  1251. .goods-name {
  1252. font-family: PingFang SC;
  1253. font-weight: 500;
  1254. font-size: 14px;
  1255. line-height: 140%;
  1256. letter-spacing: 0%;
  1257. vertical-align: middle;
  1258. color: #333;
  1259. font-weight: bold;
  1260. flex-shrink: 1;
  1261. flex-grow: 1;
  1262. }
  1263. .brand-check-placeholder {
  1264. flex-shrink: 0;
  1265. margin-left: 10rpx;
  1266. }
  1267. .brand-check {
  1268. display: flex;
  1269. flex-direction: row;
  1270. align-items: center;
  1271. justify-content: center;
  1272. border: 1px solid #f8a01d;
  1273. border-radius: 8rpx;
  1274. color: #ff7a0e;
  1275. font-family: PingFang SC;
  1276. font-weight: 400;
  1277. font-size: 12px;
  1278. padding: 4rpx 10rpx;
  1279. line-height: 1;
  1280. white-space: nowrap;
  1281. text {
  1282. margin-right: 4rpx;
  1283. }
  1284. }
  1285. .goods-desc {
  1286. font-size: 20rpx;
  1287. color: #999;
  1288. display: block;
  1289. margin-bottom: 20rpx;
  1290. white-space: nowrap;
  1291. overflow: hidden;
  1292. text-overflow: ellipsis;
  1293. }
  1294. .goods-info {
  1295. display: flex;
  1296. justify-content: space-between;
  1297. align-items: center;
  1298. }
  1299. .price-info {
  1300. display: flex;
  1301. align-items: baseline;
  1302. .price-symbol {
  1303. font-size: 24rpx;
  1304. color: #ff7a0e;
  1305. }
  1306. .price-value {
  1307. font-size: 36rpx;
  1308. color: #ff7a0e;
  1309. font-weight: bold;
  1310. margin: 0 4rpx;
  1311. }
  1312. .price-unit {
  1313. font-size: 24rpx;
  1314. color: #999;
  1315. }
  1316. }
  1317. .quantity-control {
  1318. display: flex;
  1319. align-items: center;
  1320. button {
  1321. width: 60rpx;
  1322. height: 60rpx;
  1323. padding: 0;
  1324. margin: 0;
  1325. display: flex;
  1326. align-items: center;
  1327. justify-content: center;
  1328. font-size: 28rpx;
  1329. color: #666;
  1330. background: #ffffff;
  1331. border: none;
  1332. border-radius: 50%;
  1333. &::after {
  1334. border: none;
  1335. }
  1336. &:active {
  1337. opacity: 0.8;
  1338. }
  1339. }
  1340. .quantity {
  1341. width: 80rpx;
  1342. text-align: center;
  1343. font-size: 32rpx;
  1344. color: #333;
  1345. }
  1346. }
  1347. .rules-link {
  1348. margin-top: 20rpx;
  1349. }
  1350. .rules-link .rules {
  1351. display: flex;
  1352. align-items: center;
  1353. font-family: PingFang SC;
  1354. font-weight: 400;
  1355. font-size: 12px;
  1356. line-height: 140%;
  1357. letter-spacing: 0%;
  1358. color: #666;
  1359. }
  1360. }
  1361. .other-unrecycle-card {
  1362. display: flex;
  1363. align-items: center;
  1364. background: #fff;
  1365. border-radius: 24rpx;
  1366. box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.06);
  1367. padding: 30rpx 30rpx 30rpx 30rpx;
  1368. margin: 30rpx 0 0 0;
  1369. }
  1370. .other-unrecycle-img {
  1371. width: 120rpx;
  1372. height: 120rpx;
  1373. border-radius: 24rpx;
  1374. background: #f8f8f8;
  1375. margin-right: 28rpx;
  1376. object-fit: contain;
  1377. flex-shrink: 0;
  1378. }
  1379. .other-unrecycle-info {
  1380. flex: 1;
  1381. display: flex;
  1382. flex-direction: column;
  1383. justify-content: center;
  1384. min-width: 0;
  1385. }
  1386. .other-unrecycle-title {
  1387. font-size: 30rpx;
  1388. color: #222;
  1389. font-weight: bold;
  1390. margin-bottom: 8rpx;
  1391. word-break: break-all;
  1392. white-space: normal;
  1393. overflow: visible;
  1394. }
  1395. .other-unrecycle-desc {
  1396. font-size: 24rpx;
  1397. color: #999;
  1398. margin-bottom: 12rpx;
  1399. text-overflow: ellipsis;
  1400. overflow: hidden;
  1401. white-space: nowrap;
  1402. }
  1403. .other-unrecycle-price-row {
  1404. display: flex;
  1405. align-items: center;
  1406. }
  1407. .other-unrecycle-price {
  1408. font-size: 28rpx;
  1409. color: #ff9c00;
  1410. font-weight: bold;
  1411. }
  1412. .other-unrecycle-btn {
  1413. width: 60rpx;
  1414. height: 60rpx;
  1415. margin-left: 24rpx;
  1416. border-radius: 50%;
  1417. background: #fff;
  1418. color: #666;
  1419. font-size: 36rpx;
  1420. border: none;
  1421. display: flex;
  1422. align-items: center;
  1423. justify-content: center;
  1424. }
  1425. .fixed-bottom-wrap {
  1426. position: fixed;
  1427. left: 0;
  1428. right: 0;
  1429. bottom: calc(v-bind('tabbarHeight + "px"') + env(safe-area-inset-bottom));
  1430. width: 100vw;
  1431. z-index: 1001;
  1432. background: transparent;
  1433. box-sizing: border-box;
  1434. pointer-events: auto;
  1435. }
  1436. .bottom-bar-divider {
  1437. width: 100%;
  1438. height: 1px;
  1439. background: #f0f0f0;
  1440. position: absolute;
  1441. left: 0;
  1442. bottom: 0;
  1443. z-index: 1;
  1444. }
  1445. .green-tip-bar {
  1446. width: 100%;
  1447. background: #eaffea;
  1448. color: #13ac47;
  1449. font-size: 20rpx;
  1450. // padding: 16rpx 30rpx 0 30rpx;
  1451. box-sizing: border-box;
  1452. text-align: left;
  1453. display: flex;
  1454. align-items: center;
  1455. justify-content: center;
  1456. // flex-direction: r;
  1457. .tip-highlight {
  1458. color: #ff9c00;
  1459. font-weight: bold;
  1460. font-size: 20rpx;
  1461. }
  1462. }
  1463. .bottom-bar {
  1464. width: 100%;
  1465. background-color: #fff;
  1466. display: flex;
  1467. align-items: center;
  1468. justify-content: space-between;
  1469. padding: 0 30rpx;
  1470. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  1471. height: 120rpx;
  1472. border-top-left-radius: 0;
  1473. border-top-right-radius: 0;
  1474. border-bottom-left-radius: env(safe-area-inset-bottom);
  1475. border-bottom-right-radius: env(safe-area-inset-bottom);
  1476. .bottom-left {
  1477. // flex: 1;
  1478. display: flex;
  1479. flex-direction: column;
  1480. justify-content: center;
  1481. .summary-row {
  1482. display: flex;
  1483. align-items: center;
  1484. font-size: 26rpx;
  1485. color: #333;
  1486. .summary-label {
  1487. color: #333;
  1488. }
  1489. .summary-count {
  1490. color: #ff9c00;
  1491. font-weight: bold;
  1492. font-size: 28rpx;
  1493. }
  1494. }
  1495. .amount-row {
  1496. display: flex;
  1497. align-items: center;
  1498. margin-top: 4rpx;
  1499. .amount {
  1500. color: #ff9c00;
  1501. font-size: 44rpx;
  1502. font-weight: bold;
  1503. vertical-align: middle;
  1504. }
  1505. }
  1506. }
  1507. .submit-btn {
  1508. width: 300rpx;
  1509. height: 88rpx;
  1510. background: linear-gradient(to right, #ffd01e, #ff8917);
  1511. border-radius: 44rpx;
  1512. color: #fff;
  1513. font-size: 32rpx;
  1514. font-weight: bold;
  1515. display: flex;
  1516. align-items: center;
  1517. justify-content: center;
  1518. border: none;
  1519. // margin-left: 0rpx;
  1520. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1521. &::after {
  1522. border: none;
  1523. }
  1524. &:active {
  1525. opacity: 0.9;
  1526. }
  1527. }
  1528. }
  1529. .detail-popup-mask {
  1530. position: fixed;
  1531. left: 0;
  1532. right: 0;
  1533. top: 0;
  1534. bottom: calc(90rpx + env(safe-area-inset-bottom)); /* tabbar高度+安全区 */
  1535. background: rgba(0,0,0,0.35);
  1536. z-index: 8;
  1537. display: flex;
  1538. align-items: flex-end;
  1539. justify-content: center;
  1540. }
  1541. .detail-popup {
  1542. width: 100vw;
  1543. max-width: none;
  1544. background: #fff;
  1545. border-radius: 48rpx 48rpx 0 0;
  1546. box-shadow: 0 8rpx 48rpx rgba(0,0,0,0.18);
  1547. display: flex;
  1548. flex-direction: column;
  1549. align-items: stretch;
  1550. position: relative;
  1551. padding: 0;
  1552. overflow: hidden;
  1553. min-height: 520rpx;
  1554. max-height: 80vh;
  1555. bottom: 0;
  1556. }
  1557. .detail-popup-close {
  1558. position: absolute;
  1559. right: 36rpx;
  1560. top: 36rpx;
  1561. font-size: 36rpx;
  1562. color: #bbb;
  1563. z-index: 2;
  1564. }
  1565. .popup-green-tip {
  1566. border-radius: 48rpx 48rpx 0 0;
  1567. font-size: 20rpx;
  1568. padding: 24rpx 30rpx 0 30rpx;
  1569. background: #eaffea;
  1570. color: #13ac47;
  1571. text-align: left;
  1572. }
  1573. .panel-header {
  1574. display: flex;
  1575. align-items: center;
  1576. justify-content: center;
  1577. font-size: 32rpx;
  1578. font-weight: bold;
  1579. padding: 40rpx 36rpx 0 36rpx;
  1580. background: #fff;
  1581. position: relative;
  1582. }
  1583. .panel-title {
  1584. font-size: 32rpx;
  1585. color: #222;
  1586. font-weight: bold;
  1587. text-align: center;
  1588. flex: 1;
  1589. }
  1590. .popup-panel-list {
  1591. flex: 1;
  1592. overflow-y: auto;
  1593. max-height: 36vh;
  1594. padding: 0 24rpx;
  1595. scrollbar-width: none; /* Firefox */
  1596. -ms-overflow-style: none; /* IE and Edge */
  1597. &::-webkit-scrollbar {
  1598. width: 0 !important;
  1599. display: none; /* Chrome, Safari, Opera */
  1600. }
  1601. }
  1602. .panel-item {
  1603. display: flex;
  1604. align-items: center;
  1605. justify-content: flex-start;
  1606. padding: 24rpx 0;
  1607. border-bottom: 1px solid #f0f0f0;
  1608. }
  1609. .panel-item-img {
  1610. width: 100rpx;
  1611. height: 100rpx;
  1612. margin-right: 20rpx;
  1613. border-radius: 16rpx;
  1614. background: #f8f8f8;
  1615. }
  1616. .panel-item-info {
  1617. flex: 1;
  1618. display: flex;
  1619. flex-direction: column;
  1620. justify-content: center;
  1621. min-width: 0;
  1622. }
  1623. .panel-item-name {
  1624. font-size: 30rpx;
  1625. color: #222;
  1626. font-weight: bold;
  1627. margin-bottom: 4rpx;
  1628. text-overflow: ellipsis;
  1629. overflow: hidden;
  1630. white-space: nowrap;
  1631. }
  1632. .panel-item-desc {
  1633. font-size: 24rpx;
  1634. color: #999;
  1635. margin-bottom: 4rpx;
  1636. text-overflow: ellipsis;
  1637. overflow: hidden;
  1638. white-space: nowrap;
  1639. }
  1640. .panel-item-price {
  1641. font-size: 26rpx;
  1642. color: #ff9c00;
  1643. margin-top: 2rpx;
  1644. }
  1645. .panel-quantity-control {
  1646. display: flex;
  1647. align-items: center;
  1648. margin-left: 20rpx;
  1649. }
  1650. .panel-quantity-control button {
  1651. width: 48rpx;
  1652. height: 48rpx;
  1653. padding: 0;
  1654. margin: 0 8rpx;
  1655. display: flex;
  1656. align-items: center;
  1657. justify-content: center;
  1658. font-size: 32rpx;
  1659. color: #666;
  1660. background: #ffffff;
  1661. border: none;
  1662. border-radius: 50%;
  1663. &::after {
  1664. border: none;
  1665. }
  1666. &:active {
  1667. opacity: 0.8;
  1668. }
  1669. }
  1670. .panel-quantity-control .quantity {
  1671. width: 40rpx;
  1672. text-align: center;
  1673. font-size: 28rpx;
  1674. color: #333;
  1675. }
  1676. .popup-bottom-bar {
  1677. width: 100%;
  1678. background-color: #fff;
  1679. display: flex;
  1680. align-items: center;
  1681. justify-content: space-between;
  1682. padding: 0 30rpx;
  1683. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  1684. height: 120rpx;
  1685. border-top: 1px solid #f0f0f0;
  1686. border-bottom-left-radius: 48rpx;
  1687. border-bottom-right-radius: 48rpx;
  1688. // padding-bottom: env(safe-area-inset-bottom);
  1689. .bottom-left {
  1690. display: flex;
  1691. flex-direction: column;
  1692. justify-content: center;
  1693. .summary-row {
  1694. display: flex;
  1695. align-items: center;
  1696. font-size: 26rpx;
  1697. color: #333;
  1698. .summary-label {
  1699. color: #333;
  1700. }
  1701. .summary-count {
  1702. color: #ff9c00;
  1703. font-weight: bold;
  1704. font-size: 28rpx;
  1705. }
  1706. }
  1707. .amount-row {
  1708. display: flex;
  1709. align-items: center;
  1710. margin-top: 4rpx;
  1711. .amount {
  1712. color: #ff9c00;
  1713. font-size: 44rpx;
  1714. font-weight: bold;
  1715. vertical-align: middle;
  1716. }
  1717. }
  1718. }
  1719. .submit-btn {
  1720. width: 300rpx;
  1721. height: 88rpx;
  1722. background: linear-gradient(to right, #ffd01e, #ff8917);
  1723. border-radius: 44rpx;
  1724. color: #fff;
  1725. font-size: 32rpx;
  1726. font-weight: bold;
  1727. display: flex;
  1728. align-items: center;
  1729. justify-content: center;
  1730. border: none;
  1731. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1732. &::after {
  1733. border: none;
  1734. }
  1735. &:active {
  1736. opacity: 0.9;
  1737. }
  1738. }
  1739. }
  1740. .brand-popup-mask {
  1741. position: fixed;
  1742. left: 0;
  1743. right: 0;
  1744. top: 0;
  1745. bottom: 0;
  1746. background: rgba(0,0,0,0.35);
  1747. z-index: 3000;
  1748. display: flex;
  1749. align-items: flex-end;
  1750. justify-content: center;
  1751. }
  1752. .brand-popup {
  1753. position: relative;
  1754. width: 100%;
  1755. max-width: 750px;
  1756. background: #fff;
  1757. border-radius: 32rpx 32rpx 0 0;
  1758. box-shadow: 0 -4rpx 24rpx rgba(0,0,0,0.08);
  1759. padding-bottom: 40rpx;
  1760. max-height: 90vh;
  1761. display: flex;
  1762. flex-direction: column;
  1763. overflow: hidden;
  1764. }
  1765. .brand-popup-header {
  1766. display: flex;
  1767. align-items: center;
  1768. justify-content: center;
  1769. padding: 32rpx 24rpx 0 24rpx;
  1770. font-size: 32rpx;
  1771. font-weight: bold;
  1772. position: relative;
  1773. }
  1774. .brand-popup-close {
  1775. position: absolute;
  1776. left: 24rpx;
  1777. font-size: 28rpx;
  1778. color: #888;
  1779. }
  1780. .brand-popup-title {
  1781. font-size: 32rpx;
  1782. color: #222;
  1783. font-weight: bold;
  1784. }
  1785. .brand-popup-search {
  1786. padding: 20rpx 24rpx 0 24rpx;
  1787. }
  1788. .brand-search-input {
  1789. width: 100%;
  1790. height: 60rpx;
  1791. border-radius: 30rpx;
  1792. background: #f5f5f5;
  1793. border: none;
  1794. padding-left: 40rpx;
  1795. font-size: 28rpx;
  1796. color: #888;
  1797. }
  1798. .brand-popup-list {
  1799. flex: 1;
  1800. overflow-y: auto;
  1801. max-height: 60vh;
  1802. padding: 0 24rpx;
  1803. scrollbar-width: none; /* Firefox */
  1804. -ms-overflow-style: none; /* IE and Edge */
  1805. &::-webkit-scrollbar {
  1806. width: 0 !important;
  1807. display: none; /* Chrome, Safari, Opera */
  1808. }
  1809. }
  1810. .brand-letter {
  1811. font-size: 28rpx;
  1812. color: #888;
  1813. margin: 24rpx 0 8rpx 0;
  1814. font-weight: bold;
  1815. }
  1816. .brand-item {
  1817. display: flex;
  1818. align-items: center;
  1819. padding: 16rpx 0;
  1820. border-bottom: 1px solid #f0f0f0;
  1821. }
  1822. .brand-logo {
  1823. width: 60rpx;
  1824. height: 60rpx;
  1825. margin-right: 20rpx;
  1826. border-radius: 8rpx;
  1827. background: #f8f8f8;
  1828. }
  1829. .brand-name {
  1830. font-size: 28rpx;
  1831. color: #222;
  1832. }
  1833. .brand-index-bar {
  1834. position: absolute;
  1835. right: 12rpx;
  1836. top: 120rpx;
  1837. width: 32rpx;
  1838. display: flex;
  1839. flex-direction: column;
  1840. align-items: center;
  1841. z-index: 10;
  1842. }
  1843. .brand-index-bar text {
  1844. font-size: 22rpx;
  1845. color: #bbb;
  1846. margin: 4rpx 0;
  1847. font-weight: bold;
  1848. &.active {
  1849. color: #ff9c00;
  1850. }
  1851. }
  1852. .rule-popup-mask {
  1853. position: fixed;
  1854. left: 0;
  1855. right: 0;
  1856. top: 0;
  1857. bottom: 0;
  1858. background: rgba(0,0,0,0.35);
  1859. z-index: 4000;
  1860. display: flex;
  1861. align-items: center;
  1862. justify-content: center;
  1863. }
  1864. .rule-popup {
  1865. width: 90vw;
  1866. max-width: 600rpx;
  1867. background: #fff;
  1868. border-radius: 48rpx;
  1869. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1870. display: flex;
  1871. flex-direction: column;
  1872. align-items: center;
  1873. position: relative;
  1874. padding-bottom: 40rpx;
  1875. }
  1876. .rule-popup-title {
  1877. font-size: 36rpx;
  1878. color: #222;
  1879. font-weight: bold;
  1880. text-align: center;
  1881. margin-top: 48rpx;
  1882. margin-bottom: 16rpx;
  1883. }
  1884. .rule-popup-content {
  1885. width: 100%;
  1886. max-height: 420rpx;
  1887. padding: 0 40rpx;
  1888. box-sizing: border-box;
  1889. overflow-y: auto;
  1890. scrollbar-width: none; /* Firefox */
  1891. -ms-overflow-style: none; /* IE and Edge */
  1892. &::-webkit-scrollbar {
  1893. width: 0 !important;
  1894. display: none; /* Chrome, Safari, Opera */
  1895. }
  1896. }
  1897. .rule-popup-desc {
  1898. font-size: 26rpx;
  1899. color: #888;
  1900. text-align: center;
  1901. margin-bottom: 24rpx;
  1902. margin-top: 0;
  1903. }
  1904. .rule-popup-warning {
  1905. width: 100%;
  1906. border: 2rpx solid #ffb800;
  1907. color: #ffb800;
  1908. background: #fffbe6;
  1909. border-radius: 32rpx;
  1910. font-size: 28rpx;
  1911. text-align: center;
  1912. padding: 16rpx 0;
  1913. margin-bottom: 24rpx;
  1914. font-weight: bold;
  1915. }
  1916. .rule-popup-img {
  1917. display: block;
  1918. margin: 0 auto 24rpx auto;
  1919. max-width: 80%;
  1920. max-height: 300rpx;
  1921. border-radius: 16rpx;
  1922. object-fit: contain;
  1923. }
  1924. .rule-popup-btn {
  1925. width: 80%;
  1926. height: 88rpx;
  1927. background: linear-gradient(to right, #ffd01e, #ff8917);
  1928. border-radius: 44rpx;
  1929. color: #fff;
  1930. font-size: 32rpx;
  1931. font-weight: bold;
  1932. display: flex;
  1933. align-items: center;
  1934. justify-content: center;
  1935. border: none;
  1936. margin: 0 auto;
  1937. margin-top: 16rpx;
  1938. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1939. &::after {
  1940. border: none;
  1941. }
  1942. &:active {
  1943. opacity: 0.9;
  1944. }
  1945. }
  1946. .rule-popup-close {
  1947. position: absolute;
  1948. right: 32rpx;
  1949. bottom: 32rpx;
  1950. width: 72rpx;
  1951. height: 72rpx;
  1952. background: #ff5a5f;
  1953. border-radius: 50%;
  1954. display: flex;
  1955. align-items: center;
  1956. justify-content: center;
  1957. z-index: 10;
  1958. box-shadow: 0 2rpx 8rpx rgba(255,90,95,0.12);
  1959. }
  1960. /* 预约上门取件弹窗样式 */
  1961. .pickup-confirm-mask {
  1962. position: fixed;
  1963. left: 0;
  1964. right: 0;
  1965. top: 0;
  1966. bottom: 0;
  1967. background: rgba(0,0,0,0.35);
  1968. z-index: 5000;
  1969. display: flex;
  1970. align-items: center;
  1971. justify-content: center;
  1972. }
  1973. .pickup-confirm-popup {
  1974. width: 90vw;
  1975. max-width: 600rpx;
  1976. background: #fff;
  1977. border-radius: 48rpx;
  1978. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1979. display: flex;
  1980. flex-direction: column;
  1981. align-items: center;
  1982. position: relative;
  1983. padding: 48rpx 36rpx 40rpx 36rpx;
  1984. }
  1985. .pickup-confirm-title {
  1986. font-size: 36rpx;
  1987. color: #222;
  1988. font-weight: bold;
  1989. text-align: center;
  1990. margin-bottom: 24rpx;
  1991. }
  1992. .pickup-confirm-content {
  1993. width: 100%;
  1994. font-size: 26rpx;
  1995. color: #333;
  1996. text-align: left;
  1997. line-height: 1.7;
  1998. margin-bottom: 36rpx;
  1999. .confirm-item {
  2000. margin-bottom: 24rpx;
  2001. &:last-child {
  2002. margin-bottom: 0;
  2003. }
  2004. }
  2005. .confirm-item-red {
  2006. color: red;
  2007. }
  2008. }
  2009. .pickup-confirm-btn-row {
  2010. width: 100%;
  2011. display: flex;
  2012. justify-content: space-between;
  2013. gap: 32rpx;
  2014. }
  2015. .pickup-confirm-btn {
  2016. flex: 1;
  2017. height: 88rpx;
  2018. border-radius: 44rpx;
  2019. font-size: 32rpx;
  2020. font-weight: bold;
  2021. display: flex;
  2022. align-items: center;
  2023. justify-content: center;
  2024. border: 2rpx solid #ffd01e;
  2025. background: #fff;
  2026. color: #ff9c00;
  2027. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  2028. }
  2029. .pickup-confirm-btn:not(.agree) {
  2030. background: #fff0d2;
  2031. }
  2032. .pickup-confirm-btn.agree {
  2033. background: linear-gradient(to right, #ffd01e, #ff8917);
  2034. color: #fff;
  2035. border: none;
  2036. }
  2037. .uv-tabbar {
  2038. z-index: 1000;
  2039. }
  2040. .loading-more {
  2041. text-align: center;
  2042. color: #999;
  2043. padding: 20rpx 0;
  2044. font-size: 26rpx;
  2045. }
  2046. .brand-confirm-mask {
  2047. position: fixed;
  2048. left: 0;
  2049. right: 0;
  2050. top: 0;
  2051. bottom: 0;
  2052. background: rgba(0,0,0,0.25);
  2053. z-index: 5001;
  2054. display: flex;
  2055. align-items: center;
  2056. justify-content: center;
  2057. }
  2058. .brand-confirm-popup {
  2059. width: 70vw;
  2060. max-width: 270px;
  2061. background: #fff;
  2062. border-radius: 32rpx;
  2063. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  2064. display: flex;
  2065. flex-direction: column;
  2066. align-items: center;
  2067. padding: 48rpx 20rpx 36rpx 20rpx;
  2068. position: relative;
  2069. }
  2070. .brand-confirm-title {
  2071. font-size: 36rpx;
  2072. color: #222;
  2073. font-weight: bold;
  2074. text-align: center;
  2075. margin-bottom: 24rpx;
  2076. }
  2077. .brand-confirm-logo-wrap {
  2078. width: 120rpx;
  2079. height: 120rpx;
  2080. background: #f8f8f8;
  2081. border-radius: 50%;
  2082. display: flex;
  2083. align-items: center;
  2084. justify-content: center;
  2085. margin-bottom: 18rpx;
  2086. }
  2087. .brand-confirm-logo {
  2088. width: 80rpx;
  2089. height: 80rpx;
  2090. border-radius: 50%;
  2091. }
  2092. .brand-confirm-name {
  2093. font-size: 28rpx;
  2094. color: #222;
  2095. font-weight: bold;
  2096. text-align: center;
  2097. margin-bottom: 16rpx;
  2098. }
  2099. .brand-confirm-desc {
  2100. font-size: 24rpx;
  2101. color: #999;
  2102. text-align: center;
  2103. margin-bottom: 32rpx;
  2104. line-height: 1.6;
  2105. }
  2106. .brand-confirm-btn-row {
  2107. width: 100%;
  2108. display: flex;
  2109. justify-content: space-between;
  2110. gap: 24rpx;
  2111. }
  2112. .brand-confirm-btn {
  2113. flex: 1;
  2114. height: 72rpx;
  2115. border-radius: 36rpx;
  2116. font-size: 28rpx;
  2117. font-weight: bold;
  2118. display: flex;
  2119. align-items: center;
  2120. justify-content: center;
  2121. border: none;
  2122. margin: 0 0;
  2123. }
  2124. .brand-confirm-btn.retry {
  2125. background: #fff;
  2126. color: #ff9c00;
  2127. border: 2rpx solid #ff9c00;
  2128. }
  2129. .brand-confirm-btn.confirm {
  2130. background: linear-gradient(to right, #ffd01e, #ff8917);
  2131. color: #fff;
  2132. border: none;
  2133. }
  2134. .price-info-popup-mask {
  2135. position: fixed;
  2136. left: 0;
  2137. right: 0;
  2138. top: 0;
  2139. bottom: 0;
  2140. background: rgba(0,0,0,0.4);
  2141. z-index: 6000;
  2142. display: flex;
  2143. align-items: center;
  2144. justify-content: center;
  2145. }
  2146. .price-info-popup {
  2147. width: 85vw;
  2148. max-width: 600rpx;
  2149. background: #fff;
  2150. border-radius: 24rpx;
  2151. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  2152. display: flex;
  2153. flex-direction: column;
  2154. align-items: center;
  2155. position: relative;
  2156. padding: 40rpx 40rpx;
  2157. .price-info-popup-title {
  2158. font-size: 34rpx;
  2159. color: #333;
  2160. font-weight: bold;
  2161. text-align: center;
  2162. margin-bottom: 30rpx;
  2163. }
  2164. .price-info-popup-content {
  2165. width: 100%;
  2166. max-height: 50vh;
  2167. .price-info-section {
  2168. margin-bottom: 30rpx;
  2169. &:last-child {
  2170. margin-bottom: 0;
  2171. }
  2172. .price-info-heading {
  2173. font-size: 28rpx;
  2174. color: #333;
  2175. font-weight: 500;
  2176. margin-bottom: 15rpx;
  2177. }
  2178. .price-info-text {
  2179. font-size: 26rpx;
  2180. color: #666;
  2181. line-height: 1.6;
  2182. }
  2183. }
  2184. }
  2185. .price-info-popup-btn {
  2186. width: 100%;
  2187. height: 88rpx;
  2188. background: linear-gradient(to right, #ffd01e, #ff8917);
  2189. border-radius: 44rpx;
  2190. color: #fff;
  2191. font-size: 32rpx;
  2192. font-weight: bold;
  2193. display: flex;
  2194. align-items: center;
  2195. justify-content: center;
  2196. border: none;
  2197. margin-top: 40rpx;
  2198. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  2199. &::after {
  2200. border: none;
  2201. }
  2202. &:active {
  2203. opacity: 0.9;
  2204. }
  2205. }
  2206. }
  2207. /* 减少数量时的品牌选择弹窗 */
  2208. .brand-reduce-popup-mask {
  2209. position: fixed;
  2210. left: 0;
  2211. right: 0;
  2212. top: 0;
  2213. bottom: 0;
  2214. background: rgba(0,0,0,0.35);
  2215. z-index: 5001;
  2216. display: flex;
  2217. align-items: center;
  2218. justify-content: center;
  2219. }
  2220. .brand-reduce-popup {
  2221. width: 70vw;
  2222. max-width: 270px;
  2223. background: #fff;
  2224. border-radius: 32rpx;
  2225. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  2226. display: flex;
  2227. flex-direction: column;
  2228. align-items: center;
  2229. padding: 48rpx 20rpx 36rpx 20rpx;
  2230. position: relative;
  2231. }
  2232. .brand-reduce-popup-header {
  2233. display: flex;
  2234. align-items: center;
  2235. justify-content: center;
  2236. padding: 32rpx 24rpx 0 24rpx;
  2237. font-size: 32rpx;
  2238. font-weight: bold;
  2239. position: relative;
  2240. }
  2241. .brand-reduce-popup-close {
  2242. position: absolute;
  2243. left: 24rpx;
  2244. font-size: 28rpx;
  2245. color: #888;
  2246. }
  2247. .brand-reduce-popup-title {
  2248. font-size: 32rpx;
  2249. color: #222;
  2250. font-weight: bold;
  2251. }
  2252. .brand-reduce-popup-list {
  2253. flex: 1;
  2254. overflow-y: auto;
  2255. max-height: 60vh;
  2256. padding: 0 24rpx;
  2257. scrollbar-width: none; /* Firefox */
  2258. -ms-overflow-style: none; /* IE and Edge */
  2259. &::-webkit-scrollbar {
  2260. width: 0 !important;
  2261. display: none; /* Chrome, Safari, Opera */
  2262. }
  2263. }
  2264. .brand-item {
  2265. display: flex;
  2266. align-items: center;
  2267. padding: 16rpx 0;
  2268. border-bottom: 1px solid #f0f0f0;
  2269. }
  2270. .brand-logo {
  2271. width: 60rpx;
  2272. height: 60rpx;
  2273. margin-right: 20rpx;
  2274. border-radius: 8rpx;
  2275. background: #f8f8f8;
  2276. }
  2277. .brand-name {
  2278. font-size: 28rpx;
  2279. color: #222;
  2280. }
  2281. </style>