index.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <template>
  2. <cwg-page-wrapper>
  3. <view v-if="isShow">
  4. <view v-if="typesList.length > 0 && cardList.length > 0" class="add-apply" @click="addApply">{{ t("cards.p10") }}
  5. </view>
  6. <FirstApply v-if="cardList.length == 0" />
  7. <cwg-page-more-wrapper ref="loadMoreWrapperRef" v-else :loading="loading" :refresher-enabled="true"
  8. @refresh="handleRefresh">
  9. <VirtualCard />
  10. </cwg-page-more-wrapper>
  11. </view>
  12. </cwg-page-wrapper>
  13. </template>
  14. <script setup lang="ts">
  15. import { ref, onMounted, watch, computed } from "vue";
  16. import type { CardInfo } from "@/api/ucard";
  17. import { useI18n } from "vue-i18n";
  18. import useRouter from "@/hooks/useRouter";
  19. import { ucardApi } from "@/api/ucard";
  20. import FirstApply from "./components/FirstApply.vue";
  21. import VirtualCard from "./components/VirtualCard.vue";
  22. import useUserStore from "@/stores/use-user-store";
  23. import useCardStore from "@/stores/use-card-store";
  24. import useGlobalStore from "@/stores/use-global-store";
  25. const userStore = useUserStore();
  26. const cardStore = useCardStore();
  27. const router = useRouter();
  28. const { t } = useI18n();
  29. const globalStore = useGlobalStore();
  30. const cardList = ref<CardInfo[]>([]);
  31. const typesList = ref<CardInfo[]>([]);
  32. const isShow = ref(false);
  33. async function applyList() {
  34. // 后台刷新最新卡列表,不阻塞已有缓存渲染
  35. globalStore.setFullScreenLoading(true);
  36. try {
  37. const [response1, response2, response3] = await Promise.all([
  38. ucardApi.cardList({ page: { current: 1, row: 10 }, a: 1 }),
  39. ucardApi.applyList({
  40. page: { current: 1, row: 100 },
  41. }),
  42. ucardApi.cardTypesList(),
  43. ]);
  44. const [data1, data2, data3] = await Promise.all([
  45. response1.data,
  46. response2.data,
  47. response3.data,
  48. ]);
  49. const merged = await mergeLists(data1, data2);
  50. cardList.value = merged;
  51. cardStore.saveUserCard(merged);
  52. cardTypesList(data3, data2);
  53. cardStore.saveApplyCard(data2);
  54. isShow.value = true;
  55. } catch (error) {
  56. // 请求失败时保留本地缓存的 cardList,避免页面空白
  57. console.error('加载卡片列表失败', error);
  58. } finally {
  59. globalStore.setFullScreenLoading(false);
  60. isShow.value = true;
  61. }
  62. }
  63. async function cardTypesList(data1, data2) {
  64. try {
  65. const recordTypeIds = new Set(
  66. data2.map((item) => {
  67. if (item.status != "fail" && item.status != "cancel") {
  68. return item.cardTypeId;
  69. }
  70. })
  71. );
  72. const result1 = data1;
  73. const result2 = result1.filter(
  74. (item) => !recordTypeIds.has(item.cardTypeId)
  75. );
  76. const result3 = result2.filter((item) =>
  77. item.supportHolderRegin.includes(this.businessForm.country)
  78. );
  79. typesList.value = result3;
  80. } catch (error) {
  81. typesList.value = [];
  82. }
  83. }
  84. async function mergeLists(list1, list2) {
  85. const mainList = list1.map((item) => {
  86. const { status, ...rest } = item;
  87. return {
  88. ...rest,
  89. activateStatus: status || null,
  90. status,
  91. };
  92. });
  93. const list3 = list2.filter((i) => i.status != "fail");
  94. const mainList2 = list3.map((item) => {
  95. const { status, ...rest } = item;
  96. return {
  97. ...rest,
  98. applyStatus: status || null,
  99. status,
  100. };
  101. });
  102. const mainCardTypeIds = new Set(mainList.map((i) => i.cardTypeId));
  103. const filteredList2 = mainList2.filter(
  104. (i) => !mainCardTypeIds.has(i.cardTypeId)
  105. );
  106. const latestMap = {};
  107. for (const item of filteredList2) {
  108. const cur = latestMap[item.cardTypeId];
  109. if (
  110. !cur ||
  111. new Date(item.addTime).getTime() > new Date(cur.addTime).getTime()
  112. ) {
  113. latestMap[item.cardTypeId] = { ...item, aId: item.id };
  114. }
  115. }
  116. const latestList2 = Object.values(latestMap);
  117. const latestList3 = latestList2.filter((i) => i.status != "cancel");
  118. return [...latestList3, ...mainList];
  119. }
  120. function addApply() {
  121. router.push("/select/card");
  122. }
  123. const loadMoreWrapperRef = ref<any>(null);
  124. const loading = ref(false);
  125. const finished = ref(false);
  126. const handleRefresh = async () => {
  127. await applyList();
  128. // 停止下拉刷新动画
  129. if (loadMoreWrapperRef.value) {
  130. loadMoreWrapperRef.value.stopRefresh();
  131. }
  132. };
  133. onMounted(() => {
  134. // 1. 先用本地缓存的卡列表快速渲染,提升首屏速度
  135. const cachedCards = (cardStore.userCard as any) || [];
  136. if (Array.isArray(cachedCards) && cachedCards.length > 0) {
  137. cardList.value = cachedCards as CardInfo[];
  138. isShow.value = true;
  139. }
  140. // 2. 异步拉取最新数据并更新缓存
  141. applyList();
  142. });
  143. </script>
  144. <style scoped lang="scss">
  145. @import "@/uni.scss";
  146. .add-apply {
  147. position: fixed;
  148. top: px2rpx(14);
  149. right: px2rpx(10);
  150. cursor: pointer;
  151. z-index: 100;
  152. display: flex;
  153. flex-direction: row;
  154. justify-content: center;
  155. align-items: center;
  156. gap: px2rpx(4);
  157. min-width: px2rpx(100);
  158. padding: 0 px2rpx(10);
  159. height: px2rpx(30);
  160. background: #ea002a;
  161. border-radius: px2rpx(100);
  162. font-size: px2rpx(14);
  163. color: #ffffff;
  164. }
  165. </style>