cart.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. <template>
  2. <view class="container">
  3. <scroll-view
  4. scroll-y
  5. class="cart-list"
  6. refresher-enabled
  7. :refresher-triggered="refreshing"
  8. @refresherrefresh="onRefresh"
  9. >
  10. <view v-if="loading && items.length === 0" class="loading-wrap">
  11. <text class="loading-text">加载中...</text>
  12. </view>
  13. <view v-else-if="items.length === 0" class="empty-wrap">
  14. <text class="empty-icon">🛒</text>
  15. <text class="empty-text">购物车是空的</text>
  16. </view>
  17. <view v-else>
  18. <view class="select-all-row">
  19. <view class="checkbox-wrap" @click="toggleSelectAll">
  20. <text :class="['checkbox', selectAll ? 'checked' : '']">
  21. <text v-if="selectAll" class="checkmark">✓</text>
  22. </text>
  23. <text class="select-all-text">全选</text>
  24. </view>
  25. </view>
  26. <view
  27. v-for="item in items"
  28. :key="item.productId"
  29. class="cart-item-card"
  30. >
  31. <view class="item-left">
  32. <image
  33. class="item-image"
  34. :src="getImageUrl(item.coverImage) || '/static/default-product.png'"
  35. mode="aspectFill"
  36. />
  37. </view>
  38. <view class="item-right">
  39. <view class="item-header">
  40. <view class="checkbox-wrap" @click="toggleItem(item.productId)">
  41. <text :class="['checkbox', item.checked ? 'checked' : '']">
  42. <text v-if="item.checked" class="checkmark">✓</text>
  43. </text>
  44. </view>
  45. <text class="item-name">{{ item.productName }}</text>
  46. <text v-if="item.giftLevelName" class="gift-badge">🎁购此送 {{ item.giftLevelName }}会员</text>
  47. <text v-if="item.specDesc" class="item-spec">{{ item.specDesc }}</text>
  48. <text v-if="item.supplierName" class="item-supplier">供应商: {{ item.supplierName }}</text>
  49. <text class="item-delete" @click="onRemove(item.productId)">🗑</text>
  50. </view>
  51. <view class="item-bottom">
  52. <view class="item-price-wrap">
  53. <text class="item-price">{{ formatPriceWithSymbol(item.unitPrice) }}</text>
  54. <text v-if="isMember && item.memberPrice && item.memberPrice > 0 && item.memberPrice < item.unitPrice" class="item-member-price">会员 {{ formatPriceWithSymbol(item.memberPrice) }}</text>
  55. </view>
  56. <view class="stepper">
  57. <text class="stepper-btn" @click="decreaseQty(item)">−</text>
  58. <text class="stepper-value">{{ item.quantity }}</text>
  59. <text class="stepper-btn" @click="increaseQty(item)">+</text>
  60. </view>
  61. </view>
  62. <text class="item-subtotal">小计: {{ formatPriceWithSymbol(item.subtotal) }}</text>
  63. </view>
  64. </view>
  65. </view>
  66. </scroll-view>
  67. <!-- Member Upgrade Hint -->
  68. <view v-if="!isMember && items.length > 0" class="member-hint" @click="goUpgrade">
  69. <text class="member-hint-icon">👑</text>
  70. <text class="member-hint-text">开通会员享全场商品折扣</text>
  71. <text class="member-hint-action">去开通 ›</text>
  72. </view>
  73. <!-- Gift Membership Progress -->
  74. <view v-if="!giftPromo || (giftPromo && !giftPromo.active)" class="gift-progress-section">
  75. <view v-if="giftProgress && !giftIsGranted && giftProgress.threshold > 0" class="gift-progress-section">
  76. <view class="gift-progress-header">
  77. <text class="gift-progress-icon">🎁</text>
  78. <text class="gift-progress-title">购会员商品累计进度</text>
  79. <text class="gift-progress-percent">{{ giftProgressPercent }}%</text>
  80. </view>
  81. <view class="gift-progress-bar-bg">
  82. <view class="gift-progress-bar-fill" :style="{ width: giftProgressPercent + '%' }"></view>
  83. </view>
  84. <view class="gift-progress-info">
  85. <text class="gift-progress-accumulated">已累 {{ giftAccumulatedYuan }} 元</text>
  86. <text class="gift-progress-remaining">还差 {{ giftRemainingYuan }} 元送会员</text>
  87. </view>
  88. <view v-if="giftProgress.grantedLevel" class="gift-progress-level">
  89. <text>达标将赠送 {{ giftProgress.grantedLevel === 'FAMILY' ? '家庭会员' : giftProgress.grantedLevel }} 权益</text>
  90. </view>
  91. </view>
  92. </view>
  93. <!-- 送会员一口价促销区 -->
  94. <view v-if="giftPromo && giftPromo.active && !giftIsGranted" class="gift-promo-section">
  95. <view class="gift-promo-header">
  96. <text class="gift-promo-icon">🎁</text>
  97. <text class="gift-promo-title">购会员商品原价满 {{ giftPromo.threshold / 100 }} 元,实付 {{ giftPromo.payPrice / 100 }} 元</text>
  98. </view>
  99. <view class="gift-promo-bar">
  100. <view v-if="giftPromo.remaining > 0" class="gift-promo-bar-fill" :style="{ width: Math.max(0, (100 - giftPromo.remaining / giftPromo.threshold * 100)) + '%' }"></view>
  101. <view v-else class="gift-promo-bar-fill"></view>
  102. </view>
  103. <view v-if="giftPromo.remaining > 0" class="gift-promo-remaining">还差 {{ giftPromo.remaining / 100 }} 元达标</view>
  104. <view v-else class="gift-promo-reached">已达标,支付后自动赠送会员</view>
  105. </view>
  106. <view v-else-if="giftIsGranted" class="gift-granted-hint">
  107. <text class="gift-granted-icon">✅</text>
  108. <text class="gift-granted-text">本家庭已赠送会员,请前往会员中心查看</text>
  109. </view>
  110. <view class="bottom-bar" v-if="items.length > 0">
  111. <view class="bottom-left">
  112. <text class="selected-count">已选 {{ selectedCount }} 件</text>
  113. </view>
  114. <view class="bottom-right" v-if="isMember">
  115. <view class="price-compare">
  116. <text class="price-original-label">原价</text>
  117. <text class="price-original">{{ formatPriceWithSymbol(originalTotalAmount) }}</text>
  118. </view>
  119. <view class="price-compare">
  120. <text class="price-estimate-label">预计支付</text>
  121. <text class="price-estimate">{{ formatPriceWithSymbol(estimatedTotalAmount) }}</text>
  122. </view>
  123. </view>
  124. <view class="bottom-right" v-else>
  125. <text class="total-amount-label">合计: </text>
  126. <text class="total-amount">{{ formatPriceWithSymbol(totalAmount) }}</text>
  127. </view>
  128. <button
  129. :class="['checkout-btn', selectedCount === 0 ? 'disabled' : '']"
  130. :disabled="selectedCount === 0"
  131. @click="goCheckout"
  132. >去结算</button>
  133. </view>
  134. </view>
  135. </template>
  136. <script>
  137. import config from '@/config.js'
  138. import { getMyMembership, getGiftMembershipProgress } from '@/utils/api.js'
  139. export default {
  140. data() {
  141. return {
  142. items: [],
  143. loading: false,
  144. refreshing: false,
  145. userId: null,
  146. isMember: false,
  147. giftProgress: null, // 赠会员累计进度
  148. giftPromo: null // 送会员一口价促销信息
  149. }
  150. },
  151. computed: {
  152. selectAll() {
  153. return this.items.length > 0 && this.items.every(function(item) {
  154. return item.checked
  155. })
  156. },
  157. selectedCount() {
  158. var count = 0
  159. for (var i = 0; i < this.items.length; i++) {
  160. if (this.items[i].checked) {
  161. count = count + this.items[i].quantity
  162. }
  163. }
  164. return count
  165. },
  166. totalAmount() {
  167. if (this.giftPromo && this.giftPromo.active === true && this.giftPromo.discount > 0) {
  168. return this.originalTotalAmount - this.giftPromo.discount
  169. }
  170. var total = 0
  171. for (var i = 0; i < this.items.length; i++) {
  172. if (this.items[i].checked) {
  173. total = total + (this.items[i].subtotal || this.items[i].unitPrice * this.items[i].quantity)
  174. }
  175. }
  176. return total
  177. },
  178. originalTotalAmount() {
  179. var total = 0
  180. for (var i = 0; i < this.items.length; i++) {
  181. if (this.items[i].checked) {
  182. var price = this.items[i].unitPrice || 0
  183. total = total + (price * this.items[i].quantity)
  184. }
  185. }
  186. return total
  187. },
  188. estimatedTotalAmount() {
  189. var total = 0
  190. for (var i = 0; i < this.items.length; i++) {
  191. if (this.items[i].checked) {
  192. var price = this.items[i].unitPrice || 0
  193. // 会员可用会员价,优先用会员价
  194. if (this.isMember && this.items[i].memberPrice && this.items[i].memberPrice > 0 && this.items[i].memberPrice < price) {
  195. price = this.items[i].memberPrice
  196. }
  197. // 送会员一口价折扣:若达标,从总价中减去 discount
  198. if (this.giftPromo && this.giftPromo.active === true && this.giftPromo.discount > 0 && this.items[i].giftLevelName) {
  199. // 不在此处单独扣除,由 totalAmount() 已在 top 层减去;这里仅作备选兼容
  200. }
  201. total = total + (price * this.items[i].quantity)
  202. }
  203. }
  204. // 若 giftPromo 达标,从总价中减去 discount
  205. if (this.giftPromo && this.giftPromo.active === true && this.giftPromo.discount > 0) {
  206. total = total - this.giftPromo.discount
  207. }
  208. return total
  209. },
  210. hasGiftItems() {
  211. for (var i = 0; i < this.items.length; i++) {
  212. if (this.items[i].checked && this.items[i].giftLevelName) return true
  213. }
  214. return false
  215. },
  216. giftLevelNames() {
  217. var names = []
  218. for (var i = 0; i < this.items.length; i++) {
  219. if (this.items[i].checked && this.items[i].giftLevelName) {
  220. names.push(this.items[i].giftLevelName)
  221. }
  222. }
  223. return names
  224. },
  225. // 累计进度相关计算
  226. giftAccumulatedYuan() {
  227. if (!this.giftProgress || !this.giftProgress.accumulatedAmount) return 0
  228. return this.giftProgress.accumulatedAmount / 100
  229. },
  230. giftThresholdYuan() {
  231. if (!this.giftProgress || !this.giftProgress.threshold) return 0
  232. return this.giftProgress.threshold / 100
  233. },
  234. giftRemainingYuan() {
  235. if (!this.giftProgress || !this.giftProgress.remaining) return 0
  236. return this.giftProgress.remaining / 100
  237. },
  238. giftProgressPercent() {
  239. if (!this.giftProgress || !this.giftProgress.threshold || this.giftProgress.threshold <= 0) return 0
  240. if (!this.giftProgress.accumulatedAmount) return 0
  241. return Math.min(100, Math.round(this.giftProgress.accumulatedAmount / this.giftProgress.threshold * 100))
  242. },
  243. giftIsGranted() {
  244. return this.giftProgress && this.giftProgress.granted === 1
  245. }
  246. },
  247. onLoad() {
  248. this.userId = uni.getStorageSync('userId')
  249. this.checkMemberStatus()
  250. this.loadCart()
  251. this.loadGiftProgress()
  252. this._onLoadFired = true
  253. },
  254. onShow() {
  255. if (this._onLoadFired) {
  256. this._onLoadFired = false
  257. } else if (this.items.length > 0) {
  258. this.loadCart()
  259. this.loadGiftProgress()
  260. }
  261. },
  262. methods: {
  263. checkMemberStatus() {
  264. var that = this
  265. getMyMembership().then(function(res) {
  266. if (res.data && res.data.memberLevel) {
  267. that.isMember = res.data.memberLevel !== 'FREE'
  268. }
  269. }).catch(function() {})
  270. },
  271. loadGiftProgress() {
  272. var that = this
  273. getGiftMembershipProgress().then(function(res) {
  274. if (res.data && res.data.code === 200) {
  275. that.giftProgress = res.data.data || null
  276. }
  277. }).catch(function() {})
  278. },
  279. goUpgrade() {
  280. uni.navigateTo({ url: '/pages/membership/upgrade' })
  281. },
  282. loadCart() {
  283. if (this.loading) return
  284. this.loading = true
  285. var that = this
  286. uni.request({
  287. url: config.api('/api/cart/list'),
  288. method: 'POST',
  289. data: {},
  290. header: {
  291. 'Content-Type': 'application/json',
  292. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  293. },
  294. success: function(res) {
  295. that.loading = false
  296. that.refreshing = false
  297. if (res.data && res.data.code === 200) {
  298. var result = res.data.data
  299. that.items = result.items || []
  300. for (var i = 0; i < that.items.length; i++) {
  301. that.items[i].checked = true
  302. that.items[i].subtotal = that.items[i].unitPrice * that.items[i].quantity
  303. }
  304. that.giftPromo = result.giftPromo || null
  305. that.loadGiftProgress()
  306. }
  307. },
  308. fail: function() {
  309. that.loading = false
  310. that.refreshing = false
  311. uni.showToast({ title: '加载购物车失败', icon: 'none' })
  312. }
  313. })
  314. },
  315. onRefresh() {
  316. this.refreshing = true
  317. this.loadCart()
  318. },
  319. toggleSelectAll() {
  320. var newState = !this.selectAll
  321. for (var i = 0; i < this.items.length; i++) {
  322. this.items[i].checked = newState
  323. }
  324. },
  325. toggleItem(productId) {
  326. for (var i = 0; i < this.items.length; i++) {
  327. if (this.items[i].productId === productId) {
  328. this.items[i].checked = !this.items[i].checked
  329. break
  330. }
  331. }
  332. },
  333. increaseQty(item) {
  334. item.quantity = item.quantity + 1
  335. item.subtotal = item.unitPrice * item.quantity
  336. this.syncQty(item)
  337. },
  338. decreaseQty(item) {
  339. if (item.quantity <= 1) return
  340. item.quantity = item.quantity - 1
  341. item.subtotal = item.unitPrice * item.quantity
  342. this.syncQty(item)
  343. },
  344. syncQty(item) {
  345. uni.request({
  346. url: config.api('/api/cart/update'),
  347. method: 'POST',
  348. data: { productId: item.productId, quantity: item.quantity },
  349. header: {
  350. 'Content-Type': 'application/json',
  351. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  352. }
  353. })
  354. },
  355. onRemove(productId) {
  356. var that = this
  357. uni.showModal({
  358. title: '确认删除',
  359. content: '确定从购物车移除该商品吗?',
  360. success: function(confirm) {
  361. if (confirm.confirm) {
  362. uni.request({
  363. url: config.api('/api/cart/remove'),
  364. method: 'POST',
  365. data: { productId: productId },
  366. header: {
  367. 'Content-Type': 'application/json',
  368. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  369. },
  370. success: function(res) {
  371. if (res.data && res.data.code === 200) {
  372. uni.showToast({ title: '已移除', icon: 'success' })
  373. that.loadCart()
  374. }
  375. }
  376. })
  377. }
  378. }
  379. })
  380. },
  381. goCheckout() {
  382. if (this.selectedCount === 0) {
  383. uni.showToast({ title: '请选择商品', icon: 'none' })
  384. return
  385. }
  386. var selected = []
  387. for (var i = 0; i < this.items.length; i++) {
  388. if (this.items[i].checked) {
  389. var item = {
  390. productId: this.items[i].productId,
  391. productName: this.items[i].productName,
  392. coverImage: this.items[i].coverImage,
  393. unitPrice: this.items[i].unitPrice,
  394. quantity: this.items[i].quantity
  395. }
  396. if (this.items[i].skuId) {
  397. item.skuId = this.items[i].skuId
  398. }
  399. if (this.items[i].specDesc) {
  400. item.specDesc = this.items[i].specDesc
  401. }
  402. if (this.items[i].giftLevelName) {
  403. item.giftLevelName = this.items[i].giftLevelName
  404. }
  405. if (this.items[i].memberPrice) {
  406. item.memberPrice = this.items[i].memberPrice
  407. }
  408. selected.push(item)
  409. }
  410. }
  411. uni.setStorageSync('checkoutItems', selected)
  412. uni.setStorageSync('checkoutGiftPromo', this.giftPromo)
  413. uni.navigateTo({ url: '/pages/shop/checkout/checkout' })
  414. },
  415. formatPriceWithSymbol(price) {
  416. if (price === null || price === undefined) return '0.00'
  417. return (Number(price) / 100).toFixed(2)
  418. },
  419. getImageUrl: function(path) {
  420. return config.API_BASE_URL + path
  421. }
  422. }
  423. }
  424. </script>
  425. <style scoped>
  426. .container {
  427. display: flex;
  428. flex-direction: column;
  429. height: 100vh;
  430. background: #f5f5f5;
  431. }
  432. .cart-list {
  433. flex: 1;
  434. height: calc(100vh - 120rpx);
  435. }
  436. .loading-wrap,
  437. .empty-wrap {
  438. display: flex;
  439. flex-direction: column;
  440. align-items: center;
  441. padding-top: 200rpx;
  442. }
  443. .loading-text {
  444. font-size: 26rpx;
  445. color: #999;
  446. }
  447. .empty-icon {
  448. font-size: 100rpx;
  449. margin-bottom: 30rpx;
  450. }
  451. .empty-text {
  452. font-size: 28rpx;
  453. color: #999;
  454. }
  455. .select-all-row {
  456. background: #fff;
  457. padding: 20rpx 30rpx;
  458. border-bottom: 1rpx solid #f0f0f0;
  459. }
  460. .checkbox-wrap {
  461. display: flex;
  462. align-items: center;
  463. }
  464. .checkbox {
  465. width: 36rpx;
  466. height: 36rpx;
  467. border-radius: 50%;
  468. border: 2rpx solid #ddd;
  469. display: flex;
  470. align-items: center;
  471. justify-content: center;
  472. margin-right: 12rpx;
  473. font-size: 24rpx;
  474. color: #fff;
  475. }
  476. .checkbox.checked {
  477. background: #F97316;
  478. border-color: #F97316;
  479. }
  480. .checkmark {
  481. color: #fff;
  482. font-weight: bold;
  483. }
  484. .select-all-text {
  485. font-size: 26rpx;
  486. color: #333;
  487. }
  488. .cart-item-card {
  489. display: flex;
  490. background: #fff;
  491. margin: 16rpx 20rpx;
  492. border-radius: 16rpx;
  493. padding: 20rpx;
  494. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
  495. }
  496. .item-left {
  497. margin-right: 16rpx;
  498. }
  499. .item-image {
  500. width: 180rpx;
  501. height: 180rpx;
  502. border-radius: 12rpx;
  503. background: #f0f0f0;
  504. }
  505. .item-right {
  506. flex: 1;
  507. display: flex;
  508. flex-direction: column;
  509. justify-content: space-between;
  510. min-width: 0;
  511. }
  512. .item-header {
  513. display: flex;
  514. align-items: center;
  515. }
  516. .item-name {
  517. flex: 1;
  518. font-size: 26rpx;
  519. color: #333;
  520. overflow: hidden;
  521. text-overflow: ellipsis;
  522. white-space: nowrap;
  523. margin-left: 4rpx;
  524. }
  525. .item-spec {
  526. font-size: 22rpx;
  527. color: #999;
  528. display: block;
  529. margin-top: 4rpx;
  530. flex-basis: 100%;
  531. }
  532. .item-supplier {
  533. font-size: 20rpx;
  534. color: #0EA5E9;
  535. display: block;
  536. margin-top: 2rpx;
  537. flex-basis: 100%;
  538. }
  539. .item-delete {
  540. font-size: 32rpx;
  541. padding: 4rpx 8rpx;
  542. color: #ccc;
  543. }
  544. .item-bottom {
  545. display: flex;
  546. justify-content: space-between;
  547. align-items: center;
  548. margin-top: 12rpx;
  549. }
  550. .item-price {
  551. font-size: 28rpx;
  552. color: #F97316;
  553. font-weight: bold;
  554. }
  555. .stepper {
  556. display: flex;
  557. align-items: center;
  558. border: 1rpx solid #e0e0e0;
  559. border-radius: 8rpx;
  560. overflow: hidden;
  561. }
  562. .stepper-btn {
  563. width: 56rpx;
  564. height: 48rpx;
  565. text-align: center;
  566. line-height: 48rpx;
  567. font-size: 28rpx;
  568. color: #666;
  569. background: #fafafa;
  570. }
  571. .stepper-value {
  572. width: 64rpx;
  573. height: 48rpx;
  574. text-align: center;
  575. line-height: 48rpx;
  576. font-size: 26rpx;
  577. color: #333;
  578. border-left: 1rpx solid #e0e0e0;
  579. border-right: 1rpx solid #e0e0e0;
  580. }
  581. .item-subtotal {
  582. font-size: 22rpx;
  583. color: #999;
  584. text-align: right;
  585. margin-top: 8rpx;
  586. }
  587. .bottom-bar {
  588. position: fixed;
  589. bottom: 0;
  590. left: 0;
  591. right: 0;
  592. height: 100rpx;
  593. background: #fff;
  594. border-top: 1rpx solid #eee;
  595. display: flex;
  596. align-items: center;
  597. justify-content: space-between;
  598. padding: 0 30rpx;
  599. z-index: 100;
  600. }
  601. .bottom-left {
  602. display: flex;
  603. align-items: baseline;
  604. }
  605. .selected-count {
  606. font-size: 24rpx;
  607. color: #666;
  608. margin-right: 12rpx;
  609. }
  610. .total-amount-label {
  611. font-size: 24rpx;
  612. color: #666;
  613. }
  614. .total-amount {
  615. font-size: 36rpx;
  616. color: #F97316;
  617. font-weight: bold;
  618. }
  619. .checkout-btn {
  620. background: linear-gradient(135deg, #F97316, #FB923C);
  621. color: #fff;
  622. font-size: 28rpx;
  623. font-weight: bold;
  624. padding: 0 50rpx;
  625. height: 72rpx;
  626. line-height: 72rpx;
  627. border-radius: 36rpx;
  628. border: none;
  629. }
  630. .checkout-btn::after {
  631. border: none;
  632. }
  633. .checkout-btn.disabled {
  634. background: #ddd;
  635. color: #999;
  636. }
  637. /* Member Upgrade Hint */
  638. .member-hint {
  639. display: flex;
  640. align-items: center;
  641. padding: 20rpx 30rpx;
  642. background: linear-gradient(135deg, #FFF7ED, #FFEDD5);
  643. border-top: 2rpx solid #FED7AA;
  644. border-bottom: 2rpx solid #FED7AA;
  645. }
  646. .member-hint-icon {
  647. font-size: 32rpx;
  648. margin-right: 12rpx;
  649. }
  650. .member-hint-text {
  651. flex: 1;
  652. font-size: 26rpx;
  653. color: #C2410C;
  654. }
  655. .member-hint-action {
  656. font-size: 26rpx;
  657. color: #F97316;
  658. font-weight: 500;
  659. }
  660. .price-original {
  661. font-size: 22rpx;
  662. color: #94A3B8;
  663. text-decoration: line-through;
  664. }
  665. .price-estimate {
  666. font-size: 34rpx;
  667. font-weight: bold;
  668. color: #F97316;
  669. }
  670. /* Gift badge */
  671. .gift-badge {
  672. font-size: 20rpx;
  673. color: #F97316;
  674. background: #FFF7ED;
  675. padding: 2rpx 8rpx;
  676. border-radius: 6rpx;
  677. margin-left: 8rpx;
  678. border: 1rpx solid #FED7AA;
  679. }
  680. /* Member price */
  681. .item-member-price {
  682. font-size: 22rpx;
  683. color: #10B981;
  684. margin-left: 8rpx;
  685. }
  686. .item-price-wrap {
  687. display: flex;
  688. align-items: baseline;
  689. }
  690. /* Gift membership progress */
  691. .gift-progress-section {
  692. padding: 20rpx 30rpx;
  693. background: #FFF7ED;
  694. border-top: 2rpx solid #FED7AA;
  695. }
  696. .gift-progress-header {
  697. display: flex;
  698. align-items: center;
  699. margin-bottom: 16rpx;
  700. }
  701. .gift-progress-icon {
  702. font-size: 32rpx;
  703. margin-right: 12rpx;
  704. }
  705. .gift-progress-title {
  706. flex: 1;
  707. font-size: 26rpx;
  708. color: #C2410C;
  709. font-weight: 500;
  710. }
  711. .gift-progress-percent {
  712. font-size: 28rpx;
  713. color: #F97316;
  714. font-weight: bold;
  715. }
  716. .gift-progress-bar-bg {
  717. height: 20rpx;
  718. background: #FED7AA;
  719. border-radius: 10rpx;
  720. overflow: hidden;
  721. margin-bottom: 12rpx;
  722. }
  723. .gift-progress-bar-fill {
  724. height: 100%;
  725. background: linear-gradient(90deg, #F97316, #FB923C);
  726. border-radius: 10rpx;
  727. transition: width 0.3s ease;
  728. }
  729. .gift-progress-info {
  730. display: flex;
  731. justify-content: space-between;
  732. }
  733. .gift-progress-accumulated {
  734. font-size: 22rpx;
  735. color: #9A3412;
  736. }
  737. .gift-progress-remaining {
  738. font-size: 22rpx;
  739. color: #C2410C;
  740. font-weight: 500;
  741. }
  742. .gift-progress-level {
  743. margin-top: 12rpx;
  744. font-size: 22rpx;
  745. color: #F97316;
  746. }
  747. /* Gift promo section */
  748. .gift-promo-section {
  749. padding: 20rpx 30rpx;
  750. background: linear-gradient(135deg, #FEF3C7, #FDE68A);
  751. border-top: 2rpx solid #F59E0B;
  752. }
  753. .gift-promo-header {
  754. display: flex;
  755. align-items: center;
  756. margin-bottom: 12rpx;
  757. }
  758. .gift-promo-icon {
  759. font-size: 32rpx;
  760. margin-right: 12rpx;
  761. }
  762. .gift-promo-title {
  763. flex: 1;
  764. font-size: 26rpx;
  765. color: #92400E;
  766. font-weight: 500;
  767. }
  768. .gift-promo-bar {
  769. height: 16rpx;
  770. background: #FCD34D;
  771. border-radius: 8rpx;
  772. overflow: hidden;
  773. margin-bottom: 8rpx;
  774. }
  775. .gift-promo-bar-fill {
  776. height: 100%;
  777. background: linear-gradient(90deg, #F59E0B, #D97706);
  778. border-radius: 8rpx;
  779. }
  780. .gift-promo-remaining {
  781. font-size: 22rpx;
  782. color: #92400E;
  783. }
  784. .gift-promo-reached {
  785. font-size: 22rpx;
  786. color: #D97706;
  787. font-weight: bold;
  788. }
  789. .gift-granted-hint {
  790. display: flex;
  791. align-items: center;
  792. padding: 20rpx 30rpx;
  793. background: #F0FDF4;
  794. border-top: 2rpx solid #BBF7D0;
  795. }
  796. .gift-granted-icon {
  797. font-size: 32rpx;
  798. margin-right: 12rpx;
  799. }
  800. .gift-granted-text {
  801. flex: 1;
  802. font-size: 26rpx;
  803. color: #15803D;
  804. }
  805. </style>