list.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <template>
  2. <cwg-page-wrapper :isHeaderFixed="true">
  3. <view class="bank-transaction-page">
  4. <cwg-header color="#000" :title="pageTitle" />
  5. <!-- Tabs -->
  6. <view class="tabs-container" :style="{ top: statusBarHeight + 60 + 'px' }">
  7. <view :class="['tab-item', { 'tab-active': activeTab === 'recharge' }]" @click="activeTab = 'recharge'">
  8. <cwg-icon class="icons" name="plus-filled" :size="18"
  9. :color="activeTab === 'recharge' ? '#ea002a' : '#9ca3af'" />
  10. <view :class="['tab-text', { 'tab-text-active': activeTab === 'recharge' }]">{{ t('card.Status.t18') }}</view>
  11. <view v-if="activeTab === 'recharge'" class="tab-indicator" />
  12. </view>
  13. <view :class="['tab-item', { 'tab-active': activeTab === 'transaction' }]" @click="activeTab = 'transaction'">
  14. <cwg-icon class="icons" name="list" :size="18" :color="activeTab === 'transaction' ? '#ea002a' : '#9ca3af'" />
  15. <view :class="['tab-text', { 'tab-text-active': activeTab === 'transaction' }]">{{ t('Shop.Index.Transaction')
  16. }}</view>
  17. <view v-if="activeTab === 'transaction'" class="tab-indicator" />
  18. </view>
  19. <view :class="['tab-item', { 'tab-active': activeTab === 'deduction' }]" @click="activeTab = 'deduction'">
  20. <cwg-icon class="icons" name="trending-down" :size="18"
  21. :color="activeTab === 'deduction' ? '#ea002a' : '#9ca3af'" />
  22. <view :class="['tab-text', { 'tab-text-active': activeTab === 'deduction' }]">{{ t('card.tab20') }}
  23. </view>
  24. <view v-if="activeTab === 'deduction'" class="tab-indicator" />
  25. </view>
  26. </view>
  27. <view class="filters-container" :style="{ top: statusBarHeight + 113 + 'px' }">
  28. <view class="filter-item">
  29. <text class="filter-label">{{ t('card.Form.f52') }}</text>
  30. <cwg-picker v-model="currentTypeIndex" :options="currentTypeOptions" />
  31. </view>
  32. <view class="filter-item" v-if="activeTab !== 'deduction'">
  33. <text class="filter-label">{{ t('card.Form.f45') }}</text>
  34. <cwg-picker v-model="statusFilterIndex" :options="statusOptions" />
  35. </view>
  36. <view class="filter-item">
  37. <text class="filter-label">{{ t('card.Form.f51') }}</text>
  38. <view class="filter-picker" @click="open">
  39. <view class="picker-value">
  40. <text class="picker-text">{{ dateFilter || t('card.Form.f57') }}</text>
  41. <uni-icons type="calendar" size="14" color="#6b7280" class="picker-icon" />
  42. </view>
  43. </view>
  44. <cwg-date-picker v-model:show="show" v-model="dateFilter" mode="date" @confirm="onDateConfirm"
  45. :minDate="minDate" :maxDate="maxDate" />
  46. </view>
  47. <view class="reset-btn" @click="resetFilters">
  48. <uni-icons type="loop" size="16" color="#ea002a" />
  49. </view>
  50. </view>
  51. <!-- Content -->
  52. <view class="content">
  53. <!-- Recharge Records -->
  54. <RechargeList v-if="activeTab === 'recharge'" ref="rechargeListRef" :cardNo="cardNo"
  55. :typeIndex="currentTypeIndex" :statusIndex="statusFilterIndex" :dateFilter="dateFilter"
  56. :typeOptions="currentTypeOptions" />
  57. <!-- Transaction Records -->
  58. <TransactionList v-if="activeTab === 'transaction'" ref="transactionListRef" :cardNo="cardNo"
  59. :typeIndex="currentTypeIndex" :statusIndex="statusFilterIndex" :dateFilter="dateFilter"
  60. :typeOptions="currentTypeOptions" />
  61. <!-- Deduction Records -->
  62. <DeductionList v-if="activeTab === 'deduction'" ref="deductionListRef" :cardNo="cardNo"
  63. :typeIndex="currentTypeIndex" :statusIndex="statusFilterIndex" :dateFilter="dateFilter1"
  64. :typeOptions="currentTypeOptions" />
  65. </view>
  66. </view>
  67. </cwg-page-wrapper>
  68. </template>
  69. <script setup lang="ts">
  70. import { ref, computed, watch } from 'vue';
  71. import { onLoad } from '@dcloudio/uni-app';
  72. import { useI18n } from 'vue-i18n';
  73. import useGlobalStore from '@/stores/use-global-store';
  74. import RechargeList from './components/RechargeList.vue';
  75. import TransactionList from './components/TransactionList.vue';
  76. import DeductionList from './components/DeductionList.vue';
  77. import { rechargeType, transactionTypeMap, WITHDRAW_TYPE_MAP, transactionStatusMap, rechargeStatusMap } from '@/utils/dataMap';
  78. const globalStore = useGlobalStore()
  79. const statusBarHeight = computed(() => globalStore.statusBarHeight);
  80. const { t } = useI18n();
  81. const minDate = ref(new Date(2000, 0, 1).getTime());
  82. const maxDate = ref(new Date().getTime());
  83. const activeTab = ref<'recharge' | 'transaction' | 'deduction'>('recharge');
  84. const cardNo = ref('');
  85. onLoad((options) => {
  86. cardNo.value = options.cardNo || '';
  87. });
  88. const currentTypeOptions = computed(() => {
  89. if (activeTab.value === 'recharge') {
  90. return rechargeType;
  91. } else if (activeTab.value === 'transaction') {
  92. return transactionTypeMap;
  93. } else {
  94. return WITHDRAW_TYPE_MAP;
  95. }
  96. });
  97. const statusOptions = computed(() => {
  98. if (activeTab.value === 'recharge') {
  99. return rechargeStatusMap;
  100. } else if (activeTab.value === 'transaction') {
  101. return transactionStatusMap;
  102. } else {
  103. return [];
  104. }
  105. });
  106. console.log(statusOptions, 1212);
  107. // const statusOptions = ['全部', '成功', '处理中', '失败'];
  108. const currentTypeIndex = ref();
  109. const statusFilterIndex = ref();
  110. const dateFilter = ref('');
  111. const dateFilter1 = ref('');
  112. const pageTitle = computed(() => {
  113. if (activeTab.value === 'recharge') {
  114. return t('card.tab7'); // 充值记录
  115. } else if (activeTab.value === 'transaction') {
  116. return t('card.tab2'); // 交易记录
  117. } else {
  118. return t('card.tab20'); // 扣款记录
  119. }
  120. });
  121. const show = ref(false)
  122. const onDateConfirm = (e: any) => {
  123. if (activeTab.value == 'deduction') {
  124. dateFilter1.value = e.value;
  125. }
  126. dateFilter.value = e.formatted;
  127. };
  128. function open() {
  129. show.value = true
  130. }
  131. const resetFilters = () => {
  132. currentTypeIndex.value = undefined;
  133. statusFilterIndex.value = undefined;
  134. dateFilter.value = '';
  135. dateFilter1.value = '';
  136. };
  137. // 监听 tab 切换,重置查询条件
  138. watch(activeTab, () => {
  139. resetFilters();
  140. });
  141. </script>
  142. <style scoped lang="scss">
  143. @import "@/uni.scss";
  144. .page-wrapper {
  145. padding: 0;
  146. border: 0;
  147. }
  148. .bank-transaction-page {
  149. // background-color: #f9fafb;
  150. }
  151. .wallet-header {
  152. background: #fff;
  153. color: #000 !important;
  154. .header {
  155. color: #000 !important;
  156. }
  157. }
  158. /* Header */
  159. .header {
  160. background: linear-gradient(90deg, #ea002a 0%, #eb4e6b 100%);
  161. padding: px2rpx(16);
  162. padding-bottom: px2rpx(20);
  163. }
  164. .header-content {
  165. display: flex;
  166. flex-direction: column;
  167. gap: px2rpx(16);
  168. }
  169. .header-title {
  170. display: flex;
  171. align-items: center;
  172. gap: px2rpx(8);
  173. }
  174. .title-text {
  175. color: #ffffff;
  176. font-size: px2rpx(20);
  177. }
  178. .stats-container {
  179. display: flex;
  180. gap: px2rpx(12);
  181. }
  182. .stat-card {
  183. flex: 1;
  184. background-color: rgba(255, 255, 255, 0.15);
  185. backdrop-filter: blur(px2rpx(10));
  186. border-radius: px2rpx(12);
  187. padding: px2rpx(12);
  188. }
  189. .stat-header {
  190. display: flex;
  191. align-items: center;
  192. gap: px2rpx(6);
  193. margin-bottom: px2rpx(8);
  194. }
  195. .icons {
  196. width: px2rpx(20);
  197. height: px2rpx(20);
  198. }
  199. .stat-label {
  200. font-size: px2rpx(12);
  201. color: rgba(255, 255, 255, 0.9);
  202. }
  203. .stat-value {
  204. font-size: px2rpx(20);
  205. color: #ffffff;
  206. display: block;
  207. margin-bottom: px2rpx(4);
  208. }
  209. .stat-count {
  210. font-size: px2rpx(11);
  211. color: rgba(255, 255, 255, 0.8);
  212. }
  213. /* Tabs */
  214. .tabs-container {
  215. background-color: #ffffff;
  216. display: flex;
  217. border-bottom: 1px solid #e5e7eb;
  218. position: sticky;
  219. top: 0;
  220. z-index: 10;
  221. }
  222. .tab-item {
  223. flex: 1;
  224. display: flex;
  225. align-items: center;
  226. justify-content: center;
  227. gap: px2rpx(6);
  228. padding: px2rpx(16);
  229. position: relative;
  230. transition: all 0.3s;
  231. }
  232. .tab-text {
  233. font-size: px2rpx(15);
  234. color: #9ca3af;
  235. transition: color 0.3s;
  236. }
  237. .tab-text-active {
  238. color: #ea002a;
  239. }
  240. .tab-indicator {
  241. position: absolute;
  242. bottom: 0;
  243. left: 0;
  244. right: 0;
  245. height: px2rpx(3);
  246. background-color: #ea002a;
  247. border-radius: px2rpx(3);
  248. }
  249. /* Content */
  250. .content {
  251. padding: 0;
  252. }
  253. .filters-container {
  254. display: flex;
  255. align-items: center;
  256. gap: px2rpx(8);
  257. padding: px2rpx(12) px2rpx(6);
  258. background-color: #ffffff;
  259. position: sticky;
  260. top: 0;
  261. z-index: 10;
  262. overflow-x: auto;
  263. -webkit-overflow-scrolling: touch;
  264. }
  265. .filter-item {
  266. display: flex;
  267. align-items: center;
  268. gap: px2rpx(4);
  269. flex-shrink: 0;
  270. min-width: 0;
  271. }
  272. .filter-label {
  273. font-size: px2rpx(12);
  274. color: #6b7280;
  275. white-space: nowrap;
  276. flex-shrink: 0;
  277. }
  278. .reset-btn {
  279. background-color: #ffffff;
  280. border: 1px solid #e5e7eb;
  281. border-radius: px2rpx(8);
  282. padding: px2rpx(6) 0;
  283. display: flex;
  284. align-items: center;
  285. justify-content: center;
  286. flex-shrink: 0;
  287. min-width: px2rpx(36);
  288. }
  289. </style>