index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <template>
  2. <view class="level-center">
  3. <!-- 用户当前等级信息 -->
  4. <view class="user-level-info">
  5. <view class="level-header">
  6. <view class="level-left">
  7. <view class="level-title">{{currentLevel.name}}</view>
  8. <view class="level-desc">距离下一个等级 <text class="next-level">{{nextLevel.name}}</text></view>
  9. </view>
  10. <view class="level-right" @click="goToOrderHistory">
  11. <view class="order-history">
  12. <u-icon name="clock" size="40" color="#fff"></u-icon>
  13. <text>购买记录</text>
  14. </view>
  15. </view>
  16. </view>
  17. <!-- 会员权益卡片 -->
  18. </view>
  19. <!-- 等级进度条 -->
  20. <view class="level-progress">
  21. <view class="progress-header">
  22. <text class="progress-title">升级进度</text>
  23. <text class="progress-percent">{{progressPercentage}}%</text>
  24. </view>
  25. <view class="progress-bar">
  26. <view class="progress-inner" :style="{width: progressPercentage + '%'}">
  27. <view class="progress-dot"></view>
  28. </view>
  29. </view>
  30. <view class="level-points">
  31. <view class="current-points">
  32. <text class="point-label">已购买商品</text>
  33. <text class="point-value">{{currentBuyCount}}个</text>
  34. </view>
  35. <view class="next-level-points">
  36. <text class="point-label">还需购买</text>
  37. <text class="point-value highlight">{{remainingCount}}个</text>
  38. </view>
  39. </view>
  40. </view>
  41. <!-- 等级图标展示 -->
  42. <view class="level-icons">
  43. <view v-for="(level, index) in levels" :key="index" class="level-icon-item"
  44. :class="{'active': currentLevelIndex >= index}">
  45. <view class="level-icon-wrapper" :class="[level.effect]">
  46. <image :src="level.icon" mode="aspectFit" class="level-icon"
  47. :style="level.color ? {filter: 'drop-shadow(0 0 3px '+level.color+') brightness(1.1)', fill: level.color} : ''">
  48. </image>
  49. </view>
  50. <text class="level-name" :style="level.color ? {color: level.color} : ''">{{level.name}}</text>
  51. </view>
  52. </view>
  53. <!-- 等级说明 -->
  54. <view class="level-description">
  55. <view class="desc-header">
  56. <text class="desc-title">等级说明</text>
  57. <text class="desc-subtitle">会员等级越高 优惠越多</text>
  58. </view>
  59. <!-- 等级计算规则说明 -->
  60. <view class="level-rule-notice">
  61. <view class="rule-icon">
  62. <u-icon name="info-circle" size="32" color="#8B5CF6"></u-icon>
  63. </view>
  64. <view class="rule-content">
  65. <view class="rule-title">等级计算规则</view>
  66. <view class="rule-text">您的会员等级将在每月1号根据上个月的购买商品数量进行更新,购买数量越多等级越高</view>
  67. </view>
  68. </view>
  69. <view class="desc-list">
  70. <view class="desc-item" v-for="(level, index) in levels" :key="index">
  71. <view class="desc-left">
  72. <view class="desc-level">{{level.name}}</view>
  73. <view class="desc-benefits">{{level.benefits}}</view>
  74. </view>
  75. <view class="desc-right">
  76. <view class="desc-requirement">购买商品数达到 <text class="highlight">{{level.requirement}}</text> 个
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </view>
  83. </template>
  84. <script>
  85. import {
  86. countPaidProducts
  87. } from '@/config/api';
  88. export default {
  89. data() {
  90. return {
  91. currentBuyCount: 0, // 当前购买数量,从接口获取
  92. levels: [{
  93. name: '普通会员',
  94. requirement: 0,
  95. icon: '/static/images/level.png',
  96. color: '',
  97. effect: ''
  98. },
  99. {
  100. name: 'S级会员',
  101. requirement: 20,
  102. icon: '/static/images/level1.png',
  103. color: '#FFB800',
  104. effect: 'level-effect-s'
  105. },
  106. {
  107. name: 'SS级会员',
  108. requirement: 40,
  109. icon: '/static/images/level2.png',
  110. color: '#FF6B6B',
  111. effect: 'level-effect-ss'
  112. },
  113. {
  114. name: 'SSS级会员',
  115. requirement: 55,
  116. icon: '/static/images/level3.png',
  117. color: '#8B5CF6',
  118. effect: 'level-effect-sss'
  119. }
  120. ]
  121. }
  122. },
  123. computed: {
  124. currentLevelIndex() {
  125. for (let i = this.levels.length - 1; i >= 0; i--) {
  126. if (this.currentBuyCount >= this.levels[i].requirement) {
  127. return i;
  128. }
  129. }
  130. return 0;
  131. },
  132. currentLevel() {
  133. return this.levels[this.currentLevelIndex];
  134. },
  135. nextLevel() {
  136. return this.levels[Math.min(this.currentLevelIndex + 1, this.levels.length - 1)];
  137. },
  138. remainingCount() {
  139. if (this.currentLevelIndex === this.levels.length - 1) {
  140. return 0;
  141. }
  142. return this.nextLevel.requirement - this.currentBuyCount;
  143. },
  144. progressPercentage() {
  145. if (this.currentLevelIndex === this.levels.length - 1) {
  146. return 100;
  147. }
  148. const currentRange = this.nextLevel.requirement - this.currentLevel.requirement;
  149. const currentProgress = this.currentBuyCount - this.currentLevel.requirement;
  150. return Math.min(Math.floor((currentProgress / currentRange) * 100), 100);
  151. }
  152. },
  153. onLoad() {
  154. this.getCurrentBuyCount();
  155. },
  156. methods: {
  157. // 获取当前用户购买商品数量
  158. async getCurrentBuyCount() {
  159. try {
  160. const res = await countPaidProducts();
  161. if (res.code === 200 && res.data !== null) {
  162. this.currentBuyCount = res.data.productCount;
  163. }
  164. } catch (error) {
  165. console.error('获取购买商品数量失败:', error);
  166. // 如果接口调用失败,保持默认值
  167. }
  168. },
  169. goToOrderHistory() {
  170. uni.navigateTo({
  171. url: '/packageOrder/pages/list/index'
  172. });
  173. }
  174. }
  175. }
  176. </script>
  177. <style lang="scss" scoped>
  178. .level-center {
  179. padding: 20rpx;
  180. background-color: #f5f5f5;
  181. min-height: 100vh;
  182. .user-level-info {
  183. background: linear-gradient(135deg, #8B5CF6 0%, #6366F1 100%);
  184. padding: 30rpx;
  185. border-radius: 20rpx;
  186. color: #fff;
  187. margin-bottom: 30rpx;
  188. box-shadow: 0 4rpx 20rpx rgba(99, 102, 241, 0.2);
  189. .level-header {
  190. display: flex;
  191. justify-content: space-between;
  192. align-items: flex-start;
  193. margin-bottom: 30rpx;
  194. .level-left {
  195. .level-title {
  196. font-size: 44rpx;
  197. font-weight: bold;
  198. margin-bottom: 10rpx;
  199. }
  200. .level-desc {
  201. font-size: 28rpx;
  202. opacity: 0.9;
  203. .next-level {
  204. color: #FFD700;
  205. font-weight: bold;
  206. }
  207. }
  208. }
  209. .level-right {
  210. .order-history {
  211. display: flex;
  212. flex-direction: column;
  213. align-items: center;
  214. background: rgba(255, 255, 255, 0.1);
  215. padding: 10rpx 20rpx;
  216. border-radius: 12rpx;
  217. text {
  218. font-size: 24rpx;
  219. margin-top: 6rpx;
  220. }
  221. }
  222. }
  223. }
  224. .level-benefits {
  225. display: flex;
  226. justify-content: space-around;
  227. margin-top: 20rpx;
  228. background: rgba(255, 255, 255, 0.1);
  229. border-radius: 16rpx;
  230. padding: 20rpx 0;
  231. .benefit-item {
  232. display: flex;
  233. flex-direction: column;
  234. align-items: center;
  235. .benefit-value {
  236. font-size: 32rpx;
  237. font-weight: bold;
  238. margin-bottom: 6rpx;
  239. }
  240. .benefit-label {
  241. font-size: 24rpx;
  242. opacity: 0.8;
  243. }
  244. }
  245. }
  246. }
  247. .level-progress {
  248. background-color: #fff;
  249. padding: 30rpx;
  250. border-radius: 20rpx;
  251. margin-bottom: 30rpx;
  252. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  253. .progress-header {
  254. display: flex;
  255. justify-content: space-between;
  256. align-items: center;
  257. margin-bottom: 20rpx;
  258. .progress-title {
  259. font-size: 28rpx;
  260. color: #333;
  261. font-weight: bold;
  262. }
  263. .progress-percent {
  264. font-size: 28rpx;
  265. color: #8B5CF6;
  266. font-weight: bold;
  267. }
  268. }
  269. .progress-bar {
  270. height: 16rpx;
  271. background-color: #E5E7EB;
  272. border-radius: 8rpx;
  273. overflow: hidden;
  274. margin-bottom: 20rpx;
  275. position: relative;
  276. .progress-inner {
  277. height: 100%;
  278. background: linear-gradient(to right, #8B5CF6, #6366F1);
  279. border-radius: 8rpx;
  280. transition: width 0.3s;
  281. position: relative;
  282. .progress-dot {
  283. position: absolute;
  284. right: -6rpx;
  285. top: 50%;
  286. transform: translateY(-50%);
  287. width: 12rpx;
  288. height: 12rpx;
  289. background: #fff;
  290. border-radius: 50%;
  291. box-shadow: 0 0 10rpx rgba(139, 92, 246, 0.5);
  292. }
  293. }
  294. }
  295. .level-points {
  296. display: flex;
  297. justify-content: space-between;
  298. font-size: 24rpx;
  299. .current-points,
  300. .next-level-points {
  301. display: flex;
  302. flex-direction: column;
  303. align-items: flex-start;
  304. .point-label {
  305. color: #666;
  306. margin-bottom: 4rpx;
  307. }
  308. .point-value {
  309. font-size: 28rpx;
  310. font-weight: bold;
  311. color: #333;
  312. &.highlight {
  313. color: #FF6B6B;
  314. }
  315. }
  316. }
  317. }
  318. }
  319. .level-icons {
  320. display: flex;
  321. justify-content: space-between;
  322. align-items: center;
  323. background-color: #fff;
  324. padding: 30rpx;
  325. border-radius: 20rpx;
  326. margin-bottom: 30rpx;
  327. .level-icon-item {
  328. display: flex;
  329. flex-direction: column;
  330. align-items: center;
  331. opacity: 0.5;
  332. &.active {
  333. opacity: 1;
  334. }
  335. .level-icon-wrapper {
  336. width: 90rpx;
  337. height: 90rpx;
  338. display: flex;
  339. align-items: center;
  340. justify-content: center;
  341. margin-bottom: 10rpx;
  342. position: relative;
  343. &.level-effect-s {
  344. &::before {
  345. content: '';
  346. position: absolute;
  347. width: 110%;
  348. height: 110%;
  349. border-radius: 50%;
  350. border: 2rpx solid rgba(255, 184, 0, 0.3);
  351. animation: borderPulse 1.5s ease-in-out infinite;
  352. }
  353. }
  354. &.level-effect-ss {
  355. &::before {
  356. content: '';
  357. position: absolute;
  358. width: 115%;
  359. height: 115%;
  360. border-radius: 50%;
  361. background: linear-gradient(45deg,
  362. rgba(255, 107, 107, 0.1) 0%,
  363. rgba(255, 107, 107, 0.2) 50%,
  364. rgba(255, 107, 107, 0.1) 100%);
  365. animation: shine 2s linear infinite;
  366. }
  367. }
  368. &.level-effect-sss {
  369. &::before {
  370. content: '';
  371. position: absolute;
  372. width: 120%;
  373. height: 120%;
  374. border-radius: 50%;
  375. background: linear-gradient(135deg,
  376. rgba(139, 92, 246, 0.1) 0%,
  377. rgba(139, 92, 246, 0.2) 50%,
  378. rgba(139, 92, 246, 0.1) 100%);
  379. animation: shine 1.5s linear infinite;
  380. }
  381. }
  382. .level-icon {
  383. width: 70rpx;
  384. height: 70rpx;
  385. position: relative;
  386. z-index: 1;
  387. }
  388. }
  389. @keyframes borderPulse {
  390. 0% {
  391. transform: scale(1);
  392. opacity: 0.6;
  393. }
  394. 50% {
  395. transform: scale(1.1);
  396. opacity: 0.3;
  397. }
  398. 100% {
  399. transform: scale(1);
  400. opacity: 0.6;
  401. }
  402. }
  403. @keyframes shine {
  404. 0% {
  405. transform: rotate(0deg);
  406. opacity: 0.3;
  407. }
  408. 50% {
  409. opacity: 0.5;
  410. }
  411. 100% {
  412. transform: rotate(360deg);
  413. opacity: 0.3;
  414. }
  415. }
  416. .level-name {
  417. font-size: 24rpx;
  418. color: #666;
  419. }
  420. }
  421. }
  422. .level-description {
  423. background-color: #fff;
  424. padding: 30rpx;
  425. border-radius: 20rpx;
  426. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  427. .level-rule-notice {
  428. display: flex;
  429. align-items: flex-start;
  430. background: linear-gradient(135deg, rgba(139, 92, 246, 0.05) 0%, rgba(99, 102, 241, 0.05) 100%);
  431. border: 2rpx solid rgba(139, 92, 246, 0.1);
  432. border-radius: 16rpx;
  433. padding: 24rpx;
  434. margin-bottom: 30rpx;
  435. .rule-icon {
  436. margin-right: 16rpx;
  437. margin-top: 4rpx;
  438. flex-shrink: 0;
  439. }
  440. .rule-content {
  441. flex: 1;
  442. .rule-title {
  443. font-size: 28rpx;
  444. font-weight: bold;
  445. color: #8B5CF6;
  446. margin-bottom: 8rpx;
  447. }
  448. .rule-text {
  449. font-size: 26rpx;
  450. color: #666;
  451. line-height: 1.5;
  452. }
  453. }
  454. }
  455. .desc-header {
  456. display: flex;
  457. justify-content: space-between;
  458. align-items: center;
  459. margin-bottom: 30rpx;
  460. padding-bottom: 20rpx;
  461. border-bottom: 2rpx solid #f5f5f5;
  462. .desc-title {
  463. font-size: 32rpx;
  464. font-weight: bold;
  465. color: #333;
  466. }
  467. .desc-subtitle {
  468. font-size: 24rpx;
  469. color: #666;
  470. }
  471. }
  472. .desc-list {
  473. .desc-item {
  474. display: flex;
  475. justify-content: space-between;
  476. align-items: center;
  477. padding: 24rpx 0;
  478. border-bottom: 1px solid #f5f5f5;
  479. &:last-child {
  480. border-bottom: none;
  481. }
  482. .desc-left {
  483. .desc-level {
  484. font-size: 28rpx;
  485. color: #333;
  486. font-weight: bold;
  487. margin-bottom: 6rpx;
  488. }
  489. .desc-benefits {
  490. font-size: 24rpx;
  491. color: #666;
  492. }
  493. }
  494. .desc-right {
  495. .desc-requirement {
  496. font-size: 26rpx;
  497. color: #666;
  498. .highlight {
  499. color: #8B5CF6;
  500. font-weight: bold;
  501. }
  502. }
  503. }
  504. }
  505. }
  506. }
  507. }
  508. </style>