global-list.vue 7.9 KB

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