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

1744 lines
46 KiB

2 months ago
2 months 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
1 month 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
1 month 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
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
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
1 month 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
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
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
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
1 month 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: 400rpx;"
  12. >
  13. <swiper-item v-for="(item, index) in bannerList" :key="item.id || index">
  14. <image :src="item.image" mode="aspectFill" style="width: 100%; height: 100%;" />
  15. </swiper-item>
  16. </swiper>
  17. </view>
  18. <!-- 商品列表 -->
  19. <view class="goods-list">
  20. <!-- 左侧分类导航 -->
  21. <view class="category-nav">
  22. <view
  23. v-for="(category, index) in categories"
  24. :key="category.id || index"
  25. class="category-item"
  26. :class="{ active: currentCategory === index }"
  27. @tap="switchCategory(index)"
  28. >
  29. <view class="category-dot" v-if="getCategoryItemCount(index) > 0"></view>
  30. {{ category.title }}
  31. </view>
  32. </view>
  33. <!-- 右侧商品列表 -->
  34. <scroll-view class="goods-content" scroll-y @scrolltolower="loadMoreGoods">
  35. <view class="goods-section">
  36. <view class="goods-item" v-for="(item, index) in recycleList" :key="index">
  37. <image v-if="item.image" :src="item.image" class="goods-item-img" mode="aspectFit" />
  38. <view class="goods-info-wrap">
  39. <view class="goods-header">
  40. <text class="goods-name">{{item.name}}</text>
  41. <view class="rules-link" @tap="showRules(item)">
  42. <view class="rules">
  43. <text>回收规则</text>
  44. <uni-icons type="right" size="14" color="#999"></uni-icons>
  45. </view>
  46. </view>
  47. </view>
  48. <text class="goods-desc">{{item.service}}</text>
  49. <view class="goods-info">
  50. <view class="price-info">
  51. <text class="price-symbol">¥</text>
  52. <text class="price-value">{{item.price}}</text>
  53. <text class="price-unit">/</text>
  54. </view>
  55. <view class="quantity-control">
  56. <button class="btn-minus" @tap="updateQuantity(index, -1)">-</button>
  57. <text class="quantity">{{item.quantity || 0}}</text>
  58. <button class="btn-plus" @tap="updateQuantity(index, 1)">+</button>
  59. </view>
  60. </view>
  61. <view class="brand-check" v-if="item.shopCion" @tap="checkBrand(index)">
  62. <text>查看品牌</text>
  63. <uni-icons type="right" size="14" color="#ff7a0e"></uni-icons>
  64. </view>
  65. </view>
  66. </view>
  67. </view>
  68. <!-- 不可回收商品 -->
  69. <view class="other-unrecycle-card">
  70. <image class="other-unrecycle-img" src="/static/回收/衣物.png" mode="aspectFit" />
  71. <view class="other-unrecycle-info">
  72. <view class="other-unrecycle-title">其他上衣需咨询顾问暂不回收</view>
  73. <view class="other-unrecycle-desc">需连线回收顾问筛选</view>
  74. <view class="other-unrecycle-price-row">
  75. <text class="other-unrecycle-price">¥ /</text>
  76. </view>
  77. </view>
  78. <button class="other-unrecycle-btn" disabled>+</button>
  79. </view>
  80. <view v-if="loadingMore" class="loading-more">加载中...</view>
  81. <view v-else-if="finished" class="loading-more">没有更多了</view>
  82. </scroll-view>
  83. </view>
  84. <!-- 固定底部区域 -->
  85. <view class="fixed-bottom-wrap" v-if="!showDetailPanel" >
  86. <view class="green-tip-bar">
  87. 回收范围仅支持回收以上品类按件回收预计比称重回收多
  88. <text class="tip-highlight"> 3.76</text>
  89. </view>
  90. <view class="bottom-bar">
  91. <view class="bottom-left">
  92. <view class="summary-row">
  93. <text class="summary-label">已选 <text class="summary-count">{{totalCount}}</text> 预计回收可得</text>
  94. <uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" />
  95. </view>
  96. <view class="amount-row">
  97. <uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @tap="toggleDetailPanel" />
  98. <text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text>
  99. </view>
  100. </view>
  101. <button class="submit-btn" @tap="submitOrder">预约上门取件</button>
  102. </view>
  103. <view class="bottom-bar-divider"></view>
  104. </view>
  105. <!-- 明细弹窗遮罩和弹窗 -->
  106. <view v-if="showDetailPanel" class="detail-popup-mask" @tap.self="toggleDetailPanel">
  107. <view class="detail-popup" @tap.stop>
  108. <view class="detail-popup-close" @tap="toggleDetailPanel">×</view>
  109. <view class="green-tip-bar popup-green-tip">
  110. 回收范围仅支持回收以上品类按件回收预计比称重回收多
  111. <text class="tip-highlight"> 3.76</text>
  112. </view>
  113. <view class="panel-header">
  114. <text class="panel-title">已选商品明细</text>
  115. </view>
  116. <scroll-view class="panel-list popup-panel-list" scroll-y>
  117. <view v-for="(item, idx) in selectedProducts" :key="idx" class="panel-item">
  118. <image v-if="item.image" :src="item.image" class="panel-item-img" mode="aspectFit" />
  119. <view class="panel-item-info">
  120. <text class="panel-item-name">{{item.name}}</text>
  121. <text class="panel-item-desc">{{2222}}</text>
  122. <text class="panel-item-price">¥{{item.price}}/</text>
  123. </view>
  124. <view class="panel-quantity-control">
  125. <button class="btn-minus" @tap="updateQuantityByProduct(item, -1)">-</button>
  126. <text class="quantity">{{item.quantity}}</text>
  127. <button class="btn-plus" @tap="updateQuantityByProduct(item, 1)">+</button>
  128. </view>
  129. </view>
  130. </scroll-view>
  131. <view class="popup-bottom-bar">
  132. <view class="bottom-left">
  133. <view class="summary-row">
  134. <text class="summary-label">已选 <text class="summary-count">{{totalCount}}</text> 预计回收可得</text>
  135. <uni-icons type="help" size="18" color="#b2b2b2" style="margin: 0 8rpx;" />
  136. </view>
  137. <view class="amount-row">
  138. <uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @tap="toggleDetailPanel" />
  139. <text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text>
  140. </view>
  141. </view>
  142. <button class="submit-btn" @tap="submitOrder">预约上门取件</button>
  143. </view>
  144. </view>
  145. </view>
  146. <!-- 根据角色显示不同的导航栏 -->
  147. <uv-tabbar
  148. v-if="ishow"
  149. :value="value"
  150. :fixed="true"
  151. @change="changeTo"
  152. class="uv-tabbar"
  153. >
  154. <uv-tabbar-item text="首页" >
  155. <template v-slot:active-icon>
  156. <image class="icon" src="/static/home/首页-点击.png"></image>
  157. </template>
  158. <template v-slot:inactive-icon>
  159. <image class="icon" src="/static/home/首页-未点击.png"></image>
  160. </template>
  161. </uv-tabbar-item>
  162. <uv-tabbar-item text="回收" >
  163. <template v-slot:active-icon>
  164. <image class="icon" src="/static/home/回收-点击.png"></image>
  165. </template>
  166. <template v-slot:inactive-icon>
  167. <image class="icon" src="/static/home/回收-未点击.png"></image>
  168. </template>
  169. </uv-tabbar-item>
  170. <uv-tabbar-item text="我的" >
  171. <template v-slot:active-icon>
  172. <image class="icon" src="/static/home/我的-点击.png"></image>
  173. </template>
  174. <template v-slot:inactive-icon>
  175. <image class="icon" src="/static/home/我的-未点击.png"></image>
  176. </template>
  177. </uv-tabbar-item>
  178. </uv-tabbar>
  179. <!-- 品牌索引弹窗 -->
  180. <view v-if="showBrandPopup" class="brand-popup-mask">
  181. <view class="brand-popup">
  182. <view class="brand-popup-header">
  183. <text class="brand-popup-close" @tap="closeBrandPopup">关闭</text>
  184. <text class="brand-popup-title">可回收的品牌</text>
  185. </view>
  186. <view class="brand-popup-search">
  187. <input class="brand-search-input" v-model="brandSearch" placeholder="请输入要查询的内容" />
  188. </view>
  189. <scroll-view class="brand-popup-list" scroll-y :scroll-into-view="scrollToView">
  190. <view v-for="letter in brandIndexList" :key="letter" :id="'brand-letter-' + letter">
  191. <view class="brand-letter">{{letter}}</view>
  192. <view v-for="brand in filteredBrandList.filter(b => b.letter === letter)" :key="brand.name" class="brand-item" @tap="openBrandConfirm(brand)">
  193. <image :src="brand.logo" class="brand-logo" mode="aspectFit" />
  194. <text class="brand-name">{{brand.name}}</text>
  195. </view>
  196. </view>
  197. </scroll-view>
  198. <view class="brand-index-bar">
  199. <text v-for="letter in brandIndexList" :key="letter" :class="{active: currentLetter === letter}" @tap="scrollToLetter(letter)">{{letter}}</text>
  200. </view>
  201. </view>
  202. </view>
  203. <!-- 回收规则弹窗 -->
  204. <view v-if="showRulePopup" class="rule-popup-mask" @tap.self="closeRulePopup">
  205. <view class="rule-popup">
  206. <view class="rule-popup-title">回收规则</view>
  207. <scroll-view class="rule-popup-content" scroll-y>
  208. <rich-text :nodes="ruleHtml" />
  209. </scroll-view>
  210. <button class="rule-popup-btn" @tap="closeRulePopup">我知道了</button>
  211. <!-- <view class="rule-popup-close" @tap="closeRulePopup">
  212. <uni-icons type="close" size="36" color="#fff" />
  213. </view> -->
  214. </view>
  215. </view>
  216. <!-- 预约上门取件弹窗 -->
  217. <view v-if="showPickupConfirm" class="pickup-confirm-mask">
  218. <view class="pickup-confirm-popup">
  219. <view class="pickup-confirm-title">温馨提示</view>
  220. <view class="pickup-confirm-content">
  221. 1.当前回收快递免费上门由于快递成本较高为避免不必要的成本及资源二次浪费不属于可回收品类或不符合回收标准的物品请勿寄出<br/><br/>
  222. 2.已通过的回收物品将正常结算不符合回收要求的物品可选择快递取回运费自付请在订单结算后48小时内联系在线客服安排取回逾期未联系将默认捐赠无法再次取回<br/><br/>
  223. 3.若用户寄出大量不可回收的物品平台有权限制下次回收权限或取消下次包邮服务<br/><br/>
  224. 4.对于合格率高的回收订单平台将根据实际情况给予额外回收奖励
  225. </view>
  226. <view class="pickup-confirm-btn-row">
  227. <button class="pickup-confirm-btn" @tap="handlePickupCancel">取消回收</button>
  228. <button class="pickup-confirm-btn agree" @tap="handlePickupAgree">我同意</button>
  229. </view>
  230. </view>
  231. </view>
  232. <!-- 品牌确认弹窗 -->
  233. <view v-if="showBrandConfirm" class="brand-confirm-mask" @tap.self="closeBrandConfirm">
  234. <view class="brand-confirm-popup">
  235. <view class="brand-confirm-title">品牌确认提示</view>
  236. <view class="brand-confirm-logo-wrap">
  237. <image :src="brandConfirmInfo.logo" class="brand-confirm-logo" mode="aspectFit" />
  238. </view>
  239. <view class="brand-confirm-name">{{ brandConfirmInfo.name }}</view>
  240. <view class="brand-confirm-desc">请确认所选品牌是否与实物品牌信息一致否则将无法进行回收</view>
  241. <view class="brand-confirm-btn-row">
  242. <button class="brand-confirm-btn retry" @tap="closeBrandConfirm">重新选择</button>
  243. <button class="brand-confirm-btn confirm" @tap="confirmBrand">确认一致</button>
  244. </view>
  245. </view>
  246. </view>
  247. </view>
  248. </template>
  249. <script>
  250. import tabBarMixin from '../mixins/tabBarMixin.js'
  251. import { pinyin } from '../../utils/pinyin.js'
  252. export default {
  253. mixins: [tabBarMixin],
  254. data() {
  255. return {
  256. value:1,
  257. ishow:true,
  258. // 动态数据
  259. allProducts: {}, // { [categoryId]: [商品数组] }
  260. allProductsPage: {}, // { [categoryId]: 当前已加载页码 }
  261. allProductsTotal: {}, // { [categoryId]: 总数 }
  262. pageSize: 10,
  263. currentCategory: 0,
  264. tabbarHeight: 0,
  265. showDetailPanel: false,
  266. showBrandPopup: false,
  267. showRulePopup: false,
  268. ruleImgUrl: '/static/回收/回收规则.png',
  269. showPickupConfirm: false,
  270. showBrandConfirm: false,
  271. brandConfirmInfo: {
  272. logo: '',
  273. name: ''
  274. },
  275. brandList: [],
  276. 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'],
  277. currentLetter: 'A',
  278. scrollToView: '',
  279. brandSearch: '',
  280. ruleHtml: '', // 回收规则富文本内容
  281. loadingMore: false,
  282. finished: false,
  283. pendingBrandIndex: null, // 记录待加一的品牌商品index
  284. }
  285. },
  286. computed: {
  287. // 当前分类的商品列表
  288. recycleList() {
  289. const currentCategoryId = this.categories[this.currentCategory]?.id
  290. return this.allProducts[currentCategoryId] || []
  291. },
  292. // 计算总数量
  293. totalCount() {
  294. return Object.values(this.allProducts).reduce((total, categoryItems) => {
  295. return total + categoryItems.reduce((sum, item) => sum + (item.quantity || 0), 0)
  296. }, 0)
  297. },
  298. // 计算总价格
  299. totalPrice() {
  300. const total = Object.values(this.allProducts).reduce((categoryTotal, categoryItems) => {
  301. return categoryTotal + categoryItems.reduce((sum, item) => sum + (item.quantity || 0) * Number(item.price), 0)
  302. }, 0)
  303. return total.toFixed(1)
  304. },
  305. // 计算价格范围
  306. priceRange() {
  307. if (this.totalCount === 0) {
  308. return {
  309. min: '0.0',
  310. max: '0.0'
  311. }
  312. }
  313. const total = Number(this.totalPrice)
  314. const min = Math.max(0, (total - 2.2)).toFixed(1)
  315. const max = (total + 2.2).toFixed(1)
  316. return { min, max }
  317. },
  318. selectedProducts() {
  319. // 返回所有分类下所有已选商品
  320. return Object.values(this.allProducts).flat().filter(item => item.quantity > 0)
  321. },
  322. filteredBrandList() {
  323. if (!this.brandSearch) return this.brandList
  324. const keyword = this.brandSearch.trim().toLowerCase()
  325. return this.brandList.filter(b => b.name.toLowerCase().includes(keyword))
  326. },
  327. bannerList() {
  328. return getApp().globalData.bannerList || []
  329. },
  330. categories() {
  331. const list = getApp().globalData.pricePreviewList || []
  332. return list.filter(item => item.pid === '0').sort((a, b) => a.sort - b.sort)
  333. }
  334. },
  335. methods: {
  336. changeTo(e){
  337. this.value = e
  338. if(e==2){
  339. uni.reLaunch({
  340. url: '/pages/component/my'
  341. });
  342. }else if(e==0){
  343. console.log(e,'111')
  344. uni.reLaunch({
  345. url: '/pages/component/home'
  346. });
  347. }
  348. },
  349. fetchGoodsList(categoryId, page = 1, callback) {
  350. this.$api('getClassGoodsList', {
  351. classId: categoryId,
  352. pageNo: page,
  353. pageSize: this.pageSize
  354. }, res => {
  355. if (res.code === 200 && res.result && Array.isArray(res.result.records)) {
  356. const oldList = this.allProducts[categoryId] || []
  357. const newList = page === 1 ? res.result.records : oldList.concat(res.result.records)
  358. this.$set(this.allProducts, categoryId, newList)
  359. this.$set(this.allProductsPage, categoryId, page)
  360. this.$set(this.allProductsTotal, categoryId, res.result.total)
  361. }
  362. if (callback) callback()
  363. })
  364. },
  365. // 获取分类商品总数
  366. getCategoryItemCount(index) {
  367. const categoryId = this.categories[index]?.id
  368. const categoryItems = this.allProducts[categoryId] || []
  369. return categoryItems.reduce((sum, item) => sum + (item.quantity || 0), 0)
  370. },
  371. // 切换分类
  372. switchCategory(index) {
  373. this.currentCategory = index
  374. this.loadingMore = false
  375. this.finished = false
  376. const categoryId = this.categories[index]?.id
  377. // console.log(categoryId,'switchCategory')
  378. if (!this.allProducts[categoryId]) {
  379. this.fetchGoodsList(categoryId, 1)
  380. }
  381. },
  382. // 更新商品数量
  383. updateQuantity(index, delta) {
  384. const categoryId = this.categories[this.currentCategory]?.id
  385. const item = this.allProducts[categoryId]?.[index]
  386. if (!item) return
  387. // 品牌商品且数量为0且加一时弹出品牌索引弹窗
  388. if (item.shopCion && (item.quantity || 0) === 0 && delta > 0) {
  389. this.pendingBrandIndex = index
  390. // console.log(item.shopCion,'item.shopCion')
  391. this.getGoodsBrandList(item.shopCion)
  392. this.showBrandPopup = true // 打开品牌索引弹窗
  393. return
  394. }
  395. let newQuantity = (item.quantity || 0) + delta
  396. if (newQuantity < 0) newQuantity = 0
  397. this.$set(item, 'quantity', newQuantity)
  398. },
  399. // 显示回收规则
  400. showRules(item) {
  401. // 获取回收规则富文本
  402. this.$api('getGoodsRecycleRule', { goodsId: item.id }, res => {
  403. if (res.code === 200 && res.result) {
  404. this.ruleHtml = res.result
  405. } else {
  406. this.ruleHtml = '<p>暂无回收规则</p>'
  407. }
  408. this.showRulePopup = true
  409. })
  410. },
  411. showMore() {
  412. uni.showToast({
  413. title: '更多规则请咨询客服',
  414. icon: 'none'
  415. })
  416. },
  417. submitOrder() {
  418. if (this.totalCount === 0) {
  419. uni.showToast({
  420. title: '请选择要回收的物品',
  421. icon: 'none'
  422. })
  423. return
  424. }
  425. this.showPickupConfirm = true;
  426. },
  427. handlePickupCancel() {
  428. this.showPickupConfirm = false;
  429. },
  430. handlePickupAgree() {
  431. this.showPickupConfirm = false;
  432. uni.showLoading({
  433. title: '提交中...'
  434. })
  435. setTimeout(() => {
  436. uni.hideLoading()
  437. uni.showToast({
  438. title: '预约成功',
  439. icon: 'success'
  440. })
  441. this.goToPickup()
  442. }, 1500)
  443. },
  444. goToPickup() {
  445. // 获取所有选中的衣物(所有分类)
  446. const selectedItems = this.selectedProducts.map(item => ({
  447. id: item.id,
  448. name: item.name,
  449. icon: item.image,
  450. quantity: item.quantity,
  451. unitPrice: item.price,
  452. desc: '允许脏破烂,160码以上'
  453. }))
  454. const itemsStr = encodeURIComponent(JSON.stringify(selectedItems))
  455. uni.navigateTo({
  456. url: `/pages/subcomponent/pickup?fromRecycle=true&items=${itemsStr}`
  457. })
  458. },
  459. checkBrand(index) {
  460. const categoryId = this.categories[this.currentCategory]?.id
  461. const item = this.allProducts[categoryId]?.[index]
  462. if (item?.shopCion) {
  463. this.pendingBrandIndex = index
  464. this.getGoodsBrandList(item.shopCion)
  465. this.showBrandPopup = true
  466. }
  467. },
  468. closeBrandPopup() {
  469. this.showBrandPopup = false
  470. },
  471. scrollToLetter(letter) {
  472. this.currentLetter = letter
  473. this.scrollToView = 'brand-letter-' + letter
  474. },
  475. // 添加下拉刷新方法
  476. async refreshData() {
  477. try {
  478. // 这里可以添加刷新数据的逻辑,比如重新获取商品列表等
  479. // 示例:重新初始化数据
  480. this.currentCategory = 0
  481. Object.values(this.allProducts).forEach(categoryItems => {
  482. categoryItems.forEach(item => {
  483. item.quantity = 0
  484. })
  485. })
  486. // 模拟网络请求延迟
  487. await new Promise(resolve => setTimeout(resolve, 1000))
  488. uni.showToast({
  489. title: '刷新成功',
  490. icon: 'success'
  491. })
  492. } catch (error) {
  493. uni.showToast({
  494. title: '刷新失败',
  495. icon: 'none'
  496. })
  497. } finally {
  498. // 停止下拉刷新动画
  499. uni.stopPullDownRefresh()
  500. }
  501. },
  502. toggleDetailPanel() {
  503. this.showDetailPanel = !this.showDetailPanel
  504. },
  505. updateQuantityByProduct(item, delta) {
  506. if (!item.quantity) item.quantity = 0
  507. item.quantity += delta
  508. if (item.quantity < 0) item.quantity = 0
  509. this.updateTotal()
  510. },
  511. openRulePopup() {
  512. this.showRulePopup = true
  513. },
  514. closeRulePopup() {
  515. this.showRulePopup = false
  516. },
  517. loadMoreGoods() {
  518. const categoryId = this.categories[this.currentCategory]?.id
  519. const page = (this.allProductsPage[categoryId] || 1) + 1
  520. const total = this.allProductsTotal[categoryId] || 0
  521. const loaded = (this.allProducts[categoryId] || []).length
  522. if (this.loadingMore || this.finished) return
  523. if (loaded < total) {
  524. this.loadingMore = true
  525. this.fetchGoodsList(categoryId, page, () => {
  526. this.loadingMore = false
  527. // 判断是否加载完
  528. const newLoaded = (this.allProducts[categoryId] || []).length
  529. this.finished = newLoaded >= (this.allProductsTotal[categoryId] || 0)
  530. })
  531. } else {
  532. this.finished = true
  533. }
  534. },
  535. openBrandConfirm(brand) {
  536. this.brandConfirmInfo = {
  537. logo: brand.logo,
  538. name: brand.name
  539. }
  540. this.showBrandConfirm = true
  541. },
  542. closeBrandConfirm() {
  543. this.showBrandConfirm = false
  544. },
  545. confirmBrand() {
  546. this.showBrandConfirm = false
  547. this.showBrandPopup = false
  548. // 确认后将待加一的商品数量+1
  549. if (this.pendingBrandIndex !== null) {
  550. const categoryId = this.categories[this.currentCategory]?.id
  551. const item = this.allProducts[categoryId]?.[this.pendingBrandIndex]
  552. if (item) {
  553. this.$set(item, 'quantity', 1)
  554. }
  555. this.pendingBrandIndex = null
  556. }
  557. },
  558. getGoodsBrandList(iconId) {
  559. this.$api('getGoodsBrandList', { iconId }, res => {
  560. // console.log(res,'res')
  561. if (res && res.success && res.result && res.result.records) {
  562. this.brandList = res.result.records.map(item => {
  563. // 获取品牌名称的拼音首字母
  564. const firstChar = this.getPinyinFirstLetter(item.name)
  565. return {
  566. logo: item.image || '/static/brand/alexander.png',
  567. name: item.name,
  568. letter: firstChar
  569. }
  570. })
  571. // console.log(this.brandList,'this.brandList')
  572. }
  573. })
  574. },
  575. // 获取中文拼音首字母
  576. getPinyinFirstLetter(str) {
  577. if (!str) return '#'
  578. const firstChar = str.charAt(0)
  579. // 遍历pinyin对象,查找包含该汉字的拼音
  580. for (let key in pinyin) {
  581. const chars = pinyin[key]
  582. if (chars && chars.indexOf(firstChar) !== -1) {
  583. return key.charAt(0).toUpperCase()
  584. }
  585. }
  586. return '#'
  587. },
  588. },
  589. created() {
  590. this.currentCategory = 0
  591. this.$nextTick(() => {
  592. if (this.categories.length > 0) {
  593. const firstCategoryId = this.categories[0]?.id
  594. if (firstCategoryId) {
  595. this.fetchGoodsList(firstCategoryId, 1)
  596. }
  597. }
  598. })
  599. },
  600. mounted() {
  601. this.$nextTick(() => {
  602. const query = uni.createSelectorQuery().in(this)
  603. query.select('.uv-tabbar').boundingClientRect(rect => {
  604. if (rect && rect.height) {
  605. this.tabbarHeight = rect.height
  606. } else {
  607. this.tabbarHeight = uni.upx2px ? uni.upx2px(95) : 45
  608. }
  609. // console.log(this.tabbarHeight,'tabbarHeight')
  610. }).exec()
  611. })
  612. },
  613. onLoad(options) {
  614. if (options && options.categoryId) {
  615. const idx = this.categories.findIndex(c => c.id == options.categoryId)
  616. if (idx !== -1) this.currentCategory = idx
  617. }
  618. this.fetchGoodsList(this.categories[this.currentCategory].id, 1)
  619. uni.$on('bannerListUpdated', () => {
  620. this.$forceUpdate && this.$forceUpdate()
  621. })
  622. if (getApp().globalData.bannerList && getApp().globalData.bannerList.length > 0) {
  623. this.$forceUpdate && this.$forceUpdate()
  624. }
  625. // 检查全局清空标志(兼容 reLaunch)
  626. if (getApp().globalData.shouldClearRecycle) {
  627. Object.values(this.allProducts).forEach(categoryItems => {
  628. categoryItems.forEach(item => {
  629. this.$set(item, 'quantity', 0)
  630. })
  631. })
  632. this.showDetailPanel = false
  633. this.$forceUpdate()
  634. getApp().globalData.shouldClearRecycle = false
  635. }
  636. },
  637. onUnload() {
  638. uni.$off('bannerListUpdated')
  639. // 移除事件监听
  640. uni.$off('clearRecycleOrderData')
  641. },
  642. onShow() {
  643. const id = getApp().globalData.targetRecycleCategoryId
  644. if (id) {
  645. const trySwitch = () => {
  646. if (this.categories.length > 0) {
  647. const idx = this.categories.findIndex(c => String(c.id) === String(id))
  648. if (idx !== -1) {
  649. this.currentCategory = idx
  650. const categoryId = this.categories[idx]?.id
  651. if (categoryId && !this.allProducts[categoryId]) {
  652. this.loadingMore = false
  653. this.finished = false
  654. this.fetchGoodsList(categoryId, 1)
  655. }
  656. }
  657. getApp().globalData.targetRecycleCategoryId = null
  658. } else {
  659. setTimeout(trySwitch, 100)
  660. }
  661. }
  662. trySwitch()
  663. }
  664. // 检查全局清空标志
  665. if (getApp().globalData.shouldClearRecycle) {
  666. Object.values(this.allProducts).forEach(categoryItems => {
  667. categoryItems.forEach(item => {
  668. this.$set(item, 'quantity', 0)
  669. })
  670. })
  671. this.showDetailPanel = false
  672. this.$forceUpdate()
  673. getApp().globalData.shouldClearRecycle = false
  674. }
  675. // 监听清除订单数据的事件
  676. uni.$on('clearRecycleOrderData', () => {
  677. // 清除所有商品的选中数量,保证响应式
  678. Object.values(this.allProducts).forEach(categoryItems => {
  679. categoryItems.forEach(item => {
  680. this.$set(item, 'quantity', 0)
  681. })
  682. })
  683. // 重置其他相关数据
  684. this.showDetailPanel = false
  685. this.$forceUpdate()
  686. })
  687. },
  688. watch: {
  689. categories(newVal) {
  690. const id = getApp().globalData.targetRecycleCategoryId
  691. const idx = newVal.findIndex(c => String(c.id) === String(id))
  692. if (id && newVal.length > 0 && idx !== -1) {
  693. this.currentCategory = idx
  694. getApp().globalData.targetRecycleCategoryId = null
  695. // 自动加载右侧商品
  696. const categoryId = newVal[idx]?.id
  697. if (categoryId && !this.allProducts[categoryId]) {
  698. this.loadingMore = false
  699. this.finished = false
  700. this.fetchGoodsList(categoryId, 1)
  701. }
  702. }
  703. }
  704. },
  705. }
  706. </script>
  707. <style lang="scss" scoped>
  708. .container {
  709. display: flex;
  710. flex-direction: column;
  711. height: 100vh;
  712. background-color: #f5f5f5;
  713. overflow: hidden;
  714. }
  715. .banner {
  716. background: linear-gradient(135deg, #ff9500,#ff5e00);
  717. position: relative;
  718. height: 400rpx;
  719. z-index: 1;
  720. image {
  721. width: 100%;
  722. height: 100%;
  723. }
  724. }
  725. .goods-list {
  726. // flex: 1;
  727. display: flex;
  728. position: relative;
  729. height: calc(100vh - 400rpx - 120rpx - env(safe-area-inset-bottom)); /* 减去banner和底部栏的高度 */
  730. margin-top: -10rpx;
  731. z-index: 2;
  732. border-radius: 20rpx 20rpx 0 0;
  733. overflow: hidden;
  734. padding: 30rpx;
  735. box-shadow: 0 -4rpx 8rpx rgba(0, 0, 0, 0.05);
  736. background: linear-gradient(to bottom, #fff7e8, 20%,#ffffff);
  737. .category-nav {
  738. width: 20%;
  739. background: #ffffff;
  740. height: 100%;
  741. border-right: 1rpx solid rgba(255, 126, 14, 0.1);
  742. margin: 1rpx;
  743. border-radius: 20rpx 0 0 0;
  744. margin-right: 20rpx;
  745. overflow-y: auto;
  746. scrollbar-width: none; /* Firefox */
  747. -ms-overflow-style: none; /* IE and Edge */
  748. &::-webkit-scrollbar {
  749. width: 0 !important;
  750. display: none; /* Chrome, Safari, Opera */
  751. }
  752. .category-item {
  753. position: relative;
  754. padding: 30rpx 20rpx;
  755. text-align: center;
  756. font-family: PingFang SC;
  757. font-weight: 600;
  758. font-size: 14px;
  759. line-height: 100%;
  760. letter-spacing: 0px;
  761. color: #666;
  762. .category-dot {
  763. position: absolute;
  764. top: 15rpx;
  765. right: 15rpx;
  766. width: 12rpx;
  767. height: 12rpx;
  768. background: #ff7a0e;
  769. border-radius: 50%;
  770. }
  771. &.active {
  772. color: #ff7a0e;
  773. font-weight: bold;
  774. background: #fff7e8;
  775. position: relative;
  776. &::before {
  777. content: '';
  778. position: absolute;
  779. left: 0;
  780. top: 50%;
  781. transform: translateY(-50%);
  782. width: 6rpx;
  783. height: 36rpx;
  784. background: #ff7a0e;
  785. border-radius: 3rpx;
  786. }
  787. }
  788. }
  789. }
  790. .goods-content {
  791. flex: 1;
  792. height: 100%;
  793. padding: 0 30rpx;
  794. background: #ffffff;
  795. width: 70%;
  796. margin: 1rpx;
  797. margin-left: 0;
  798. border-radius: 0 20rpx 0 0;
  799. overflow-y: auto;
  800. scrollbar-width: none; /* Firefox */
  801. -ms-overflow-style: none; /* IE and Edge */
  802. &::-webkit-scrollbar {
  803. width: 0 !important;
  804. display: none; /* Chrome, Safari, Opera */
  805. }
  806. }
  807. }
  808. .goods-item {
  809. display: flex;
  810. align-items: flex-start;
  811. padding: 30rpx 0;
  812. border-bottom: 1rpx solid #f5f5f5;
  813. .goods-item-img {
  814. width: 120rpx;
  815. height: 120rpx;
  816. border-radius: 24rpx;
  817. background: #f8f8f8;
  818. margin-right: 28rpx;
  819. object-fit: contain;
  820. flex-shrink: 0;
  821. }
  822. .goods-info-wrap {
  823. flex: 1;
  824. display: flex;
  825. flex-direction: column;
  826. justify-content: center;
  827. min-width: 0;
  828. }
  829. .goods-header {
  830. display: flex;
  831. justify-content: space-between;
  832. align-items: center;
  833. margin-bottom: 10rpx;
  834. }
  835. .goods-name {
  836. font-family: PingFang SC;
  837. font-weight: 500;
  838. font-size: 14px;
  839. line-height: 140%;
  840. letter-spacing: 0%;
  841. vertical-align: middle;
  842. color: #333;
  843. font-weight: bold;
  844. }
  845. .goods-desc {
  846. font-size: 24rpx;
  847. color: #999;
  848. display: block;
  849. margin-bottom: 20rpx;
  850. }
  851. .goods-info {
  852. display: flex;
  853. justify-content: space-between;
  854. align-items: center;
  855. }
  856. .price-info {
  857. display: flex;
  858. align-items: baseline;
  859. .price-symbol {
  860. font-size: 24rpx;
  861. color: #ff7a0e;
  862. }
  863. .price-value {
  864. font-size: 36rpx;
  865. color: #ff7a0e;
  866. font-weight: bold;
  867. margin: 0 4rpx;
  868. }
  869. .price-unit {
  870. font-size: 24rpx;
  871. color: #999;
  872. }
  873. }
  874. .quantity-control {
  875. display: flex;
  876. align-items: center;
  877. button {
  878. width: 60rpx;
  879. height: 60rpx;
  880. padding: 0;
  881. margin: 0;
  882. display: flex;
  883. align-items: center;
  884. justify-content: center;
  885. font-size: 28rpx;
  886. color: #666;
  887. background: #f8f8f8;
  888. border: none;
  889. border-radius: 50%;
  890. &::after {
  891. border: none;
  892. }
  893. &:active {
  894. opacity: 0.8;
  895. }
  896. }
  897. .quantity {
  898. width: 80rpx;
  899. text-align: center;
  900. font-size: 32rpx;
  901. color: #333;
  902. }
  903. }
  904. .brand-check {
  905. // margin-top: 20rpx;
  906. // margin:0 auto;
  907. display: flex;
  908. flex-direction: row;
  909. align-items: center;
  910. // justify-content: center;
  911. border: 1px solid #f8a01d;
  912. width: 40%;
  913. // background: #fff7f0;
  914. border-radius: 8rpx;
  915. color: #ff7a0e;
  916. font-family: PingFang SC;
  917. font-weight: 400;
  918. font-size: 12px;
  919. line-height: 140%;
  920. letter-spacing: 0%;
  921. text {
  922. // margin-right: 8rpx;
  923. }
  924. }
  925. }
  926. .rules-link{
  927. .rules{
  928. font-family: PingFang SC;
  929. font-weight: 400;
  930. font-size: 12px;
  931. line-height: 140%;
  932. letter-spacing: 0%;
  933. }
  934. }
  935. .other-unrecycle-card {
  936. display: flex;
  937. align-items: center;
  938. background: #fff;
  939. border-radius: 24rpx;
  940. box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.06);
  941. padding: 30rpx 30rpx 30rpx 30rpx;
  942. margin: 30rpx 0 0 0;
  943. }
  944. .other-unrecycle-img {
  945. width: 120rpx;
  946. height: 120rpx;
  947. border-radius: 24rpx;
  948. background: #f8f8f8;
  949. margin-right: 28rpx;
  950. object-fit: contain;
  951. flex-shrink: 0;
  952. }
  953. .other-unrecycle-info {
  954. flex: 1;
  955. display: flex;
  956. flex-direction: column;
  957. justify-content: center;
  958. min-width: 0;
  959. }
  960. .other-unrecycle-title {
  961. font-size: 30rpx;
  962. color: #222;
  963. font-weight: bold;
  964. margin-bottom: 8rpx;
  965. word-break: break-all;
  966. white-space: normal;
  967. overflow: visible;
  968. }
  969. .other-unrecycle-desc {
  970. font-size: 24rpx;
  971. color: #999;
  972. margin-bottom: 12rpx;
  973. text-overflow: ellipsis;
  974. overflow: hidden;
  975. white-space: nowrap;
  976. }
  977. .other-unrecycle-price-row {
  978. display: flex;
  979. align-items: center;
  980. }
  981. .other-unrecycle-price {
  982. font-size: 28rpx;
  983. color: #ff9c00;
  984. font-weight: bold;
  985. }
  986. .other-unrecycle-btn {
  987. width: 60rpx;
  988. height: 60rpx;
  989. margin-left: 24rpx;
  990. border-radius: 50%;
  991. background: #f5f5f5;
  992. color: #ccc;
  993. font-size: 36rpx;
  994. border: none;
  995. display: flex;
  996. align-items: center;
  997. justify-content: center;
  998. pointer-events: none;
  999. }
  1000. .fixed-bottom-wrap {
  1001. position: fixed;
  1002. left: 0;
  1003. right: 0;
  1004. bottom: calc(v-bind('tabbarHeight + "px"') + env(safe-area-inset-bottom));
  1005. width: 100vw;
  1006. z-index: 1001;
  1007. background: transparent;
  1008. box-sizing: border-box;
  1009. pointer-events: auto;
  1010. }
  1011. .bottom-bar-divider {
  1012. width: 100%;
  1013. height: 1px;
  1014. background: #f0f0f0;
  1015. position: absolute;
  1016. left: 0;
  1017. bottom: 0;
  1018. z-index: 1;
  1019. }
  1020. .green-tip-bar {
  1021. width: 100%;
  1022. background: #eaffea;
  1023. color: #13ac47;
  1024. font-size: 20rpx;
  1025. // padding: 16rpx 30rpx 0 30rpx;
  1026. box-sizing: border-box;
  1027. text-align: left;
  1028. display: flex;
  1029. align-items: center;
  1030. justify-content: center;
  1031. // flex-direction: r;
  1032. .tip-highlight {
  1033. color: #ff9c00;
  1034. font-weight: bold;
  1035. font-size: 20rpx;
  1036. }
  1037. }
  1038. .bottom-bar {
  1039. width: 100%;
  1040. background-color: #fff;
  1041. display: flex;
  1042. align-items: center;
  1043. justify-content: space-between;
  1044. padding: 0 30rpx;
  1045. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  1046. height: 120rpx;
  1047. border-top-left-radius: 0;
  1048. border-top-right-radius: 0;
  1049. border-bottom-left-radius: env(safe-area-inset-bottom);
  1050. border-bottom-right-radius: env(safe-area-inset-bottom);
  1051. .bottom-left {
  1052. // flex: 1;
  1053. display: flex;
  1054. flex-direction: column;
  1055. justify-content: center;
  1056. .summary-row {
  1057. display: flex;
  1058. align-items: center;
  1059. font-size: 26rpx;
  1060. color: #333;
  1061. .summary-label {
  1062. color: #333;
  1063. }
  1064. .summary-count {
  1065. color: #ff9c00;
  1066. font-weight: bold;
  1067. font-size: 28rpx;
  1068. }
  1069. }
  1070. .amount-row {
  1071. display: flex;
  1072. align-items: center;
  1073. margin-top: 4rpx;
  1074. .amount {
  1075. color: #ff9c00;
  1076. font-size: 44rpx;
  1077. font-weight: bold;
  1078. vertical-align: middle;
  1079. }
  1080. }
  1081. }
  1082. .submit-btn {
  1083. width: 300rpx;
  1084. height: 88rpx;
  1085. background: linear-gradient(to right, #ffd01e, #ff8917);
  1086. border-radius: 44rpx;
  1087. color: #fff;
  1088. font-size: 32rpx;
  1089. font-weight: bold;
  1090. display: flex;
  1091. align-items: center;
  1092. justify-content: center;
  1093. border: none;
  1094. // margin-left: 0rpx;
  1095. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1096. &::after {
  1097. border: none;
  1098. }
  1099. &:active {
  1100. opacity: 0.9;
  1101. }
  1102. }
  1103. }
  1104. .detail-popup-mask {
  1105. position: fixed;
  1106. left: 0;
  1107. right: 0;
  1108. top: 0;
  1109. bottom: calc(90rpx + env(safe-area-inset-bottom)); /* tabbar高度+安全区 */
  1110. background: rgba(0,0,0,0.35);
  1111. z-index: 8;
  1112. display: flex;
  1113. align-items: flex-end;
  1114. justify-content: center;
  1115. }
  1116. .detail-popup {
  1117. width: 100vw;
  1118. max-width: none;
  1119. background: #fff;
  1120. border-radius: 48rpx 48rpx 0 0;
  1121. box-shadow: 0 8rpx 48rpx rgba(0,0,0,0.18);
  1122. display: flex;
  1123. flex-direction: column;
  1124. align-items: stretch;
  1125. position: relative;
  1126. padding: 0;
  1127. overflow: hidden;
  1128. min-height: 520rpx;
  1129. max-height: 80vh;
  1130. bottom: 0;
  1131. }
  1132. .detail-popup-close {
  1133. position: absolute;
  1134. right: 36rpx;
  1135. top: 36rpx;
  1136. font-size: 36rpx;
  1137. color: #bbb;
  1138. z-index: 2;
  1139. }
  1140. .popup-green-tip {
  1141. border-radius: 48rpx 48rpx 0 0;
  1142. font-size: 20rpx;
  1143. padding: 24rpx 30rpx 0 30rpx;
  1144. background: #eaffea;
  1145. color: #13ac47;
  1146. text-align: left;
  1147. }
  1148. .panel-header {
  1149. display: flex;
  1150. align-items: center;
  1151. justify-content: center;
  1152. font-size: 32rpx;
  1153. font-weight: bold;
  1154. padding: 40rpx 36rpx 0 36rpx;
  1155. background: #fff;
  1156. position: relative;
  1157. }
  1158. .panel-title {
  1159. font-size: 32rpx;
  1160. color: #222;
  1161. font-weight: bold;
  1162. text-align: center;
  1163. flex: 1;
  1164. }
  1165. .popup-panel-list {
  1166. flex: 1;
  1167. overflow-y: auto;
  1168. max-height: 36vh;
  1169. padding: 0 24rpx;
  1170. scrollbar-width: none; /* Firefox */
  1171. -ms-overflow-style: none; /* IE and Edge */
  1172. &::-webkit-scrollbar {
  1173. width: 0 !important;
  1174. display: none; /* Chrome, Safari, Opera */
  1175. }
  1176. }
  1177. .panel-item {
  1178. display: flex;
  1179. align-items: center;
  1180. justify-content: flex-start;
  1181. padding: 24rpx 0;
  1182. border-bottom: 1px solid #f0f0f0;
  1183. }
  1184. .panel-item-img {
  1185. width: 100rpx;
  1186. height: 100rpx;
  1187. margin-right: 20rpx;
  1188. border-radius: 16rpx;
  1189. background: #f8f8f8;
  1190. }
  1191. .panel-item-info {
  1192. flex: 1;
  1193. display: flex;
  1194. flex-direction: column;
  1195. justify-content: center;
  1196. min-width: 0;
  1197. }
  1198. .panel-item-name {
  1199. font-size: 30rpx;
  1200. color: #222;
  1201. font-weight: bold;
  1202. margin-bottom: 4rpx;
  1203. text-overflow: ellipsis;
  1204. overflow: hidden;
  1205. white-space: nowrap;
  1206. }
  1207. .panel-item-desc {
  1208. font-size: 24rpx;
  1209. color: #999;
  1210. margin-bottom: 4rpx;
  1211. text-overflow: ellipsis;
  1212. overflow: hidden;
  1213. white-space: nowrap;
  1214. }
  1215. .panel-item-price {
  1216. font-size: 26rpx;
  1217. color: #ff9c00;
  1218. margin-top: 2rpx;
  1219. }
  1220. .panel-quantity-control {
  1221. display: flex;
  1222. align-items: center;
  1223. margin-left: 20rpx;
  1224. }
  1225. .panel-quantity-control button {
  1226. width: 48rpx;
  1227. height: 48rpx;
  1228. padding: 0;
  1229. margin: 0 8rpx;
  1230. display: flex;
  1231. align-items: center;
  1232. justify-content: center;
  1233. font-size: 32rpx;
  1234. color: #666;
  1235. background: #f5f5f5;
  1236. border: none;
  1237. border-radius: 50%;
  1238. &::after {
  1239. border: none;
  1240. }
  1241. &:active {
  1242. opacity: 0.8;
  1243. }
  1244. }
  1245. .panel-quantity-control .quantity {
  1246. width: 40rpx;
  1247. text-align: center;
  1248. font-size: 28rpx;
  1249. color: #333;
  1250. }
  1251. .popup-bottom-bar {
  1252. width: 100%;
  1253. background-color: #fff;
  1254. display: flex;
  1255. align-items: center;
  1256. justify-content: space-between;
  1257. padding: 0 30rpx;
  1258. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  1259. height: 120rpx;
  1260. border-top: 1px solid #f0f0f0;
  1261. border-bottom-left-radius: 48rpx;
  1262. border-bottom-right-radius: 48rpx;
  1263. // padding-bottom: env(safe-area-inset-bottom);
  1264. .bottom-left {
  1265. display: flex;
  1266. flex-direction: column;
  1267. justify-content: center;
  1268. .summary-row {
  1269. display: flex;
  1270. align-items: center;
  1271. font-size: 26rpx;
  1272. color: #333;
  1273. .summary-label {
  1274. color: #333;
  1275. }
  1276. .summary-count {
  1277. color: #ff9c00;
  1278. font-weight: bold;
  1279. font-size: 28rpx;
  1280. }
  1281. }
  1282. .amount-row {
  1283. display: flex;
  1284. align-items: center;
  1285. margin-top: 4rpx;
  1286. .amount {
  1287. color: #ff9c00;
  1288. font-size: 44rpx;
  1289. font-weight: bold;
  1290. vertical-align: middle;
  1291. }
  1292. }
  1293. }
  1294. .submit-btn {
  1295. width: 300rpx;
  1296. height: 88rpx;
  1297. background: linear-gradient(to right, #ffd01e, #ff8917);
  1298. border-radius: 44rpx;
  1299. color: #fff;
  1300. font-size: 32rpx;
  1301. font-weight: bold;
  1302. display: flex;
  1303. align-items: center;
  1304. justify-content: center;
  1305. border: none;
  1306. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1307. &::after {
  1308. border: none;
  1309. }
  1310. &:active {
  1311. opacity: 0.9;
  1312. }
  1313. }
  1314. }
  1315. .brand-popup-mask {
  1316. position: fixed;
  1317. left: 0;
  1318. right: 0;
  1319. top: 0;
  1320. bottom: 0;
  1321. background: rgba(0,0,0,0.35);
  1322. z-index: 3000;
  1323. display: flex;
  1324. align-items: flex-end;
  1325. justify-content: center;
  1326. }
  1327. .brand-popup {
  1328. position: relative;
  1329. width: 100%;
  1330. max-width: 750px;
  1331. background: #fff;
  1332. border-radius: 32rpx 32rpx 0 0;
  1333. box-shadow: 0 -4rpx 24rpx rgba(0,0,0,0.08);
  1334. padding-bottom: 40rpx;
  1335. max-height: 90vh;
  1336. display: flex;
  1337. flex-direction: column;
  1338. overflow: hidden;
  1339. }
  1340. .brand-popup-header {
  1341. display: flex;
  1342. align-items: center;
  1343. justify-content: center;
  1344. padding: 32rpx 24rpx 0 24rpx;
  1345. font-size: 32rpx;
  1346. font-weight: bold;
  1347. position: relative;
  1348. }
  1349. .brand-popup-close {
  1350. position: absolute;
  1351. left: 24rpx;
  1352. font-size: 28rpx;
  1353. color: #888;
  1354. }
  1355. .brand-popup-title {
  1356. font-size: 32rpx;
  1357. color: #222;
  1358. font-weight: bold;
  1359. }
  1360. .brand-popup-search {
  1361. padding: 20rpx 24rpx 0 24rpx;
  1362. }
  1363. .brand-search-input {
  1364. width: 100%;
  1365. height: 60rpx;
  1366. border-radius: 30rpx;
  1367. background: #f5f5f5;
  1368. border: none;
  1369. padding-left: 40rpx;
  1370. font-size: 28rpx;
  1371. color: #888;
  1372. }
  1373. .brand-popup-list {
  1374. flex: 1;
  1375. overflow-y: auto;
  1376. max-height: 60vh;
  1377. padding: 0 24rpx;
  1378. scrollbar-width: none; /* Firefox */
  1379. -ms-overflow-style: none; /* IE and Edge */
  1380. &::-webkit-scrollbar {
  1381. width: 0 !important;
  1382. display: none; /* Chrome, Safari, Opera */
  1383. }
  1384. }
  1385. .brand-letter {
  1386. font-size: 28rpx;
  1387. color: #888;
  1388. margin: 24rpx 0 8rpx 0;
  1389. font-weight: bold;
  1390. }
  1391. .brand-item {
  1392. display: flex;
  1393. align-items: center;
  1394. padding: 16rpx 0;
  1395. border-bottom: 1px solid #f0f0f0;
  1396. }
  1397. .brand-logo {
  1398. width: 60rpx;
  1399. height: 60rpx;
  1400. margin-right: 20rpx;
  1401. border-radius: 8rpx;
  1402. background: #f8f8f8;
  1403. }
  1404. .brand-name {
  1405. font-size: 28rpx;
  1406. color: #222;
  1407. }
  1408. .brand-index-bar {
  1409. position: absolute;
  1410. right: 12rpx;
  1411. top: 120rpx;
  1412. width: 32rpx;
  1413. display: flex;
  1414. flex-direction: column;
  1415. align-items: center;
  1416. z-index: 10;
  1417. }
  1418. .brand-index-bar text {
  1419. font-size: 22rpx;
  1420. color: #bbb;
  1421. margin: 4rpx 0;
  1422. font-weight: bold;
  1423. &.active {
  1424. color: #ff9c00;
  1425. }
  1426. }
  1427. .rule-popup-mask {
  1428. position: fixed;
  1429. left: 0;
  1430. right: 0;
  1431. top: 0;
  1432. bottom: 0;
  1433. background: rgba(0,0,0,0.35);
  1434. z-index: 4000;
  1435. display: flex;
  1436. align-items: center;
  1437. justify-content: center;
  1438. }
  1439. .rule-popup {
  1440. width: 90vw;
  1441. max-width: 600rpx;
  1442. background: #fff;
  1443. border-radius: 48rpx;
  1444. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1445. display: flex;
  1446. flex-direction: column;
  1447. align-items: center;
  1448. position: relative;
  1449. padding-bottom: 40rpx;
  1450. }
  1451. .rule-popup-title {
  1452. font-size: 36rpx;
  1453. color: #222;
  1454. font-weight: bold;
  1455. text-align: center;
  1456. margin-top: 48rpx;
  1457. margin-bottom: 16rpx;
  1458. }
  1459. .rule-popup-content {
  1460. width: 100%;
  1461. max-height: 420rpx;
  1462. padding: 0 40rpx;
  1463. box-sizing: border-box;
  1464. overflow-y: auto;
  1465. scrollbar-width: none; /* Firefox */
  1466. -ms-overflow-style: none; /* IE and Edge */
  1467. &::-webkit-scrollbar {
  1468. width: 0 !important;
  1469. display: none; /* Chrome, Safari, Opera */
  1470. }
  1471. }
  1472. .rule-popup-desc {
  1473. font-size: 26rpx;
  1474. color: #888;
  1475. text-align: center;
  1476. margin-bottom: 24rpx;
  1477. margin-top: 0;
  1478. }
  1479. .rule-popup-warning {
  1480. width: 100%;
  1481. border: 2rpx solid #ffb800;
  1482. color: #ffb800;
  1483. background: #fffbe6;
  1484. border-radius: 32rpx;
  1485. font-size: 28rpx;
  1486. text-align: center;
  1487. padding: 16rpx 0;
  1488. margin-bottom: 24rpx;
  1489. font-weight: bold;
  1490. }
  1491. .rule-popup-img {
  1492. display: block;
  1493. margin: 0 auto 24rpx auto;
  1494. max-width: 80%;
  1495. max-height: 300rpx;
  1496. border-radius: 16rpx;
  1497. object-fit: contain;
  1498. }
  1499. .rule-popup-btn {
  1500. width: 80%;
  1501. height: 88rpx;
  1502. background: linear-gradient(to right, #ffd01e, #ff8917);
  1503. border-radius: 44rpx;
  1504. color: #fff;
  1505. font-size: 32rpx;
  1506. font-weight: bold;
  1507. display: flex;
  1508. align-items: center;
  1509. justify-content: center;
  1510. border: none;
  1511. margin: 0 auto;
  1512. margin-top: 16rpx;
  1513. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1514. &::after {
  1515. border: none;
  1516. }
  1517. &:active {
  1518. opacity: 0.9;
  1519. }
  1520. }
  1521. .rule-popup-close {
  1522. position: absolute;
  1523. right: 32rpx;
  1524. bottom: 32rpx;
  1525. width: 72rpx;
  1526. height: 72rpx;
  1527. background: #ff5a5f;
  1528. border-radius: 50%;
  1529. display: flex;
  1530. align-items: center;
  1531. justify-content: center;
  1532. z-index: 10;
  1533. box-shadow: 0 2rpx 8rpx rgba(255,90,95,0.12);
  1534. }
  1535. /* 预约上门取件弹窗样式 */
  1536. .pickup-confirm-mask {
  1537. position: fixed;
  1538. left: 0;
  1539. right: 0;
  1540. top: 0;
  1541. bottom: 0;
  1542. background: rgba(0,0,0,0.35);
  1543. z-index: 5000;
  1544. display: flex;
  1545. align-items: center;
  1546. justify-content: center;
  1547. }
  1548. .pickup-confirm-popup {
  1549. width: 90vw;
  1550. max-width: 600rpx;
  1551. background: #fff;
  1552. border-radius: 48rpx;
  1553. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1554. display: flex;
  1555. flex-direction: column;
  1556. align-items: center;
  1557. position: relative;
  1558. padding: 48rpx 36rpx 40rpx 36rpx;
  1559. }
  1560. .pickup-confirm-title {
  1561. font-size: 36rpx;
  1562. color: #222;
  1563. font-weight: bold;
  1564. text-align: center;
  1565. margin-bottom: 24rpx;
  1566. }
  1567. .pickup-confirm-content {
  1568. font-size: 26rpx;
  1569. color: #333;
  1570. text-align: left;
  1571. line-height: 1.7;
  1572. margin-bottom: 36rpx;
  1573. }
  1574. .pickup-confirm-btn-row {
  1575. width: 100%;
  1576. display: flex;
  1577. justify-content: space-between;
  1578. gap: 32rpx;
  1579. }
  1580. .pickup-confirm-btn {
  1581. flex: 1;
  1582. height: 88rpx;
  1583. border-radius: 44rpx;
  1584. font-size: 32rpx;
  1585. font-weight: bold;
  1586. display: flex;
  1587. align-items: center;
  1588. justify-content: center;
  1589. border: 2rpx solid #ffd01e;
  1590. background: #fff;
  1591. color: #ff9c00;
  1592. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  1593. }
  1594. .pickup-confirm-btn:not(.agree) {
  1595. background: #fff0d2;
  1596. }
  1597. .pickup-confirm-btn.agree {
  1598. background: linear-gradient(to right, #ffd01e, #ff8917);
  1599. color: #fff;
  1600. border: none;
  1601. }
  1602. .uv-tabbar {
  1603. z-index: 1000;
  1604. }
  1605. .loading-more {
  1606. text-align: center;
  1607. color: #999;
  1608. padding: 20rpx 0;
  1609. font-size: 26rpx;
  1610. }
  1611. .brand-confirm-mask {
  1612. position: fixed;
  1613. left: 0;
  1614. right: 0;
  1615. top: 0;
  1616. bottom: 0;
  1617. background: rgba(0,0,0,0.25);
  1618. z-index: 5001;
  1619. display: flex;
  1620. align-items: center;
  1621. justify-content: center;
  1622. }
  1623. .brand-confirm-popup {
  1624. width: 70vw;
  1625. max-width: 270px;
  1626. background: #fff;
  1627. border-radius: 32rpx;
  1628. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1629. display: flex;
  1630. flex-direction: column;
  1631. align-items: center;
  1632. padding: 48rpx 20rpx 36rpx 20rpx;
  1633. position: relative;
  1634. }
  1635. .brand-confirm-title {
  1636. font-size: 36rpx;
  1637. color: #222;
  1638. font-weight: bold;
  1639. text-align: center;
  1640. margin-bottom: 24rpx;
  1641. }
  1642. .brand-confirm-logo-wrap {
  1643. width: 120rpx;
  1644. height: 120rpx;
  1645. background: #f8f8f8;
  1646. border-radius: 50%;
  1647. display: flex;
  1648. align-items: center;
  1649. justify-content: center;
  1650. margin-bottom: 18rpx;
  1651. }
  1652. .brand-confirm-logo {
  1653. width: 80rpx;
  1654. height: 80rpx;
  1655. border-radius: 50%;
  1656. }
  1657. .brand-confirm-name {
  1658. font-size: 28rpx;
  1659. color: #222;
  1660. font-weight: bold;
  1661. text-align: center;
  1662. margin-bottom: 16rpx;
  1663. }
  1664. .brand-confirm-desc {
  1665. font-size: 24rpx;
  1666. color: #999;
  1667. text-align: center;
  1668. margin-bottom: 32rpx;
  1669. line-height: 1.6;
  1670. }
  1671. .brand-confirm-btn-row {
  1672. width: 100%;
  1673. display: flex;
  1674. justify-content: space-between;
  1675. gap: 24rpx;
  1676. }
  1677. .brand-confirm-btn {
  1678. flex: 1;
  1679. height: 72rpx;
  1680. border-radius: 36rpx;
  1681. font-size: 28rpx;
  1682. font-weight: bold;
  1683. display: flex;
  1684. align-items: center;
  1685. justify-content: center;
  1686. border: none;
  1687. margin: 0 0;
  1688. }
  1689. .brand-confirm-btn.retry {
  1690. background: #fff;
  1691. color: #ff9c00;
  1692. border: 2rpx solid #ff9c00;
  1693. }
  1694. .brand-confirm-btn.confirm {
  1695. background: linear-gradient(to right, #ffd01e, #ff8917);
  1696. color: #fff;
  1697. border: none;
  1698. }
  1699. </style>