payment-history.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <cwg-header :title="t('Home.page_customer.item4')" />
  4. <view class="info-card">
  5. <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
  6. @reset="handleReset" />
  7. <cwg-tabel ref="tableRef" :columns="columns" :mobilePrimaryFields="mobilePrimaryFields"
  8. :queryParams="search" :api="listApi" :show-operation="false" :showPagination="false">
  9. <template #avatar="{ row }">
  10. <image :src="row.avatar" class="avatar" mode="widthFix" />
  11. <cwg-file :path="row.path" />
  12. </template>
  13. <template #type="{ row }">
  14. {{typeMap.find(item => item.value === row.type)?.text}}
  15. </template>
  16. <template #status="{ row }">
  17. <view class="status-box">
  18. <view v-if="row.type == 1">
  19. <text v-if="
  20. row.status == 1 &&
  21. row.callbackStatus == 0 &&
  22. (row.expireTime == null || time < row.expireTime)
  23. " class="status-tag status-pending" v-t="'State.ToBeProcessed'"></text>
  24. <text v-if="
  25. row.status == 2 &&
  26. row.executionStatus == 2 &&
  27. row.callbackStatus == 1
  28. " class="status-tag status-completed" v-t="'State.Completed'"></text>
  29. <text v-if="
  30. row.status == 2 &&
  31. (row.executionStatus == 1 || row.executionStatus == 0) &&
  32. row.callbackStatus != 2 &&
  33. (row.expireTime == null || time < row.expireTime)
  34. " class="status-tag status-processing" v-t="'State.InTheProcessing'"></text>
  35. <text v-if="
  36. row.callbackStatus == 2 ||
  37. row.status == 3 ||
  38. row.executionStatus == 3
  39. " class="status-tag status-failed" v-t="'State.Refused'"></text>
  40. <text v-if="
  41. !(
  42. row.status == 2 &&
  43. row.executionStatus == 2 &&
  44. row.callbackStatus == 1
  45. ) &&
  46. !(
  47. row.callbackStatus == 2 ||
  48. row.status == 3 ||
  49. row.executionStatus == 3
  50. ) &&
  51. row.expireTime != null &&
  52. time > row.expireTime
  53. " class="status-tag status-expired" v-t="'State.expireTime'"></text>
  54. </view>
  55. <view v-if="row.type == 2">
  56. <text v-if="
  57. row.status == 1 &&
  58. row.callbackStatus == 0 &&
  59. (row.expireTime == null || time < row.expireTime)
  60. " class="status-tag status-pending" v-t="'State.ToBeProcessed'"></text>
  61. <text v-if="
  62. row.status == 2 &&
  63. row.executionStatus == 2 &&
  64. row.submitStatus == 2
  65. " class="status-tag status-completed" v-t="'State.Completed'"></text>
  66. <text v-if="
  67. (row.status == 2 &&
  68. (row.executionStatus == 1 || row.executionStatus == 0) &&
  69. row.callbackStatus != 2) ||
  70. (row.status == 2 &&
  71. row.executionStatus == 2 &&
  72. row.callbackStatus == 0 &&
  73. row.type == 2 &&
  74. row.submitStatus != 2)
  75. " class="status-tag status-processing" v-t="'State.InTheProcessing'"></text>
  76. <text v-if="
  77. row.callbackStatus == 2 ||
  78. row.status == 3 ||
  79. row.executionStatus == 3
  80. " class="status-tag status-failed" v-t="'State.Refused'"></text>
  81. </view>
  82. <text v-if="row.status == 5" class="status-tag status-cancelled" v-t="'State.Cancelled'"></text>
  83. <!-- 取消按钮(保留原有功能,不添加状态类名) -->
  84. <view v-if="
  85. row.status == 1 &&
  86. row.callbackStatus == 0 &&
  87. (row.expireTime == null || time < row.expireTime) &&
  88. row.type == 2
  89. ">
  90. <text class="status-tag btn crm-cursor status-cancelled" v-t="'Btn.Cancel'"
  91. @click.stop="cancleConfirm(row.id, 1)"></text>
  92. </view>
  93. <view v-if="
  94. row.status == 2 &&
  95. row.executionStatus == 2 &&
  96. row.backstageStatus == 1 &&
  97. row.type == 2
  98. ">
  99. <text class="status-tag btn crm-cursor status-cancelled" v-t="'Btn.Cancel'"
  100. @click.stop="cancleConfirm(row.id, 2)"></text>
  101. </view>
  102. </view>
  103. </template>
  104. <template #btn="{ row }">
  105. <text :class="['operation-btn', row.status !== 4 ? 'disabled' : '']" @click="openAddFile(row)">
  106. <cwg-icon name="crm-image" :size="16" color="#1d293d" />
  107. <text v-t="'State.Again'" />
  108. </text>
  109. </template>
  110. </cwg-tabel>
  111. </view>
  112. </cwg-page-wrapper>
  113. </template>
  114. <script setup lang="ts">
  115. import { computed, ref, nextTick } from 'vue';
  116. import { useI18n } from 'vue-i18n';
  117. const { t, locale } = useI18n();
  118. import { financialApi } from '@/service/financial';
  119. import { useConfirm } from '@/hooks/useConfirm'
  120. const search = ref({})
  121. const typeMap = computed(() => ([
  122. { value: null, text: t('Custom.PaymentHistory.All') },
  123. { value: 1, text: t('Custom.PaymentHistory.Deposit') },
  124. { value: 2, text: t('Custom.PaymentHistory.Withdrawals') }
  125. ]));
  126. const orderStatusMap = computed(() => ([
  127. { value: null, text: t('Custom.PaymentHistory.All') },
  128. { value: 1, text: t('State.ToBeProcessed') },
  129. { value: 2, text: t('State.Completed') },
  130. { value: 3, text: t('State.InTheProcessing') },
  131. { value: 4, text: t('State.Refused') },
  132. { value: 5, text: t('State.expireTime') },
  133. { value: 6, text: t('State.Cancelled') },
  134. ]));
  135. const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
  136. const time = computed(() => {
  137. let timezone = 2; //目标时区时间,东3区 东时区正数 西市区负数
  138. let offset_GMT = new Date().getTimezoneOffset(); // 本地时间和格林威治的时间差,单位为分钟
  139. let nowDate = new Date().getTime(); // 本地时间距 1970 年 1 月 1 日午夜(GMT 时间)之间的毫秒数
  140. let now = new Date(
  141. nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000
  142. );
  143. //当前时间
  144. let hh = now.getHours(); //时
  145. let mm = now.getMinutes(); //分
  146. let ss = now.getSeconds(); //分
  147. let clock;
  148. let year = now.getFullYear(); //年
  149. let month = now.getMonth() + 1; //月
  150. let day = now.getDate(); //日
  151. if (month < 10) {
  152. month = "0" + month;
  153. }
  154. if (day < 10) {
  155. day = "0" + day;
  156. }
  157. if (hh < 10) {
  158. hh = "0" + hh;
  159. }
  160. if (mm < 10) {
  161. mm = "0" + mm;
  162. }
  163. if (ss < 10) {
  164. ss = "0" + ss;
  165. }
  166. clock = year + "-" + month + "-" + day + " " + hh + ":" + mm + ":" + ss;
  167. return clock;
  168. })
  169. // 表格列配置
  170. const columns = computed(() => [
  171. {
  172. prop: 'serial',
  173. label: t('Custom.PaymentHistory.Serial'),
  174. align: 'left'
  175. },
  176. {
  177. prop: 'login',
  178. label: t('Custom.PaymentHistory.TradingAccount'),
  179. align: 'left'
  180. },
  181. {
  182. prop: 'type',
  183. label: t('Custom.PaymentHistory.payType'),
  184. align: 'left',
  185. slot: 'type'
  186. },
  187. {
  188. prop: 'channelName',
  189. label: t('Custom.PaymentHistory.PaymentMethod'),
  190. formatter: ({ row }) => isZh.value ? row.channelName : row.channelEnName,
  191. align: 'left'
  192. },
  193. {
  194. prop: 'amount',
  195. label: t('Custom.PaymentHistory.Amount'),
  196. formatter: ({ row }) => row.amount + ' ' + row.currency,
  197. align: 'left'
  198. },
  199. {
  200. prop: 'addTime',
  201. label: t('Custom.PaymentHistory.ApplicationDate'),
  202. type: 'date',
  203. dateFormat: 'YYYY-MM-DD HH:mm',
  204. align: 'left'
  205. },
  206. {
  207. prop: 'status',
  208. label: t('Custom.PaymentHistory.Status'),
  209. slot: 'status',
  210. align: 'left'
  211. },
  212. {
  213. prop: 'note',
  214. label: t('Custom.Recording.Note'),
  215. type: 'note',
  216. align: 'right'
  217. },
  218. {
  219. prop: 'more',
  220. type: 'more',
  221. width: 20,
  222. align: 'right'
  223. },
  224. ])
  225. // 动态传入筛选字段配置
  226. const filterFields = computed(() => [
  227. { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: null },
  228. {
  229. key: 'orderStatus', type: 'select', label: t('Custom.PaymentHistory.Status'), placeholder: t('placeholder.choose'), options: orderStatusMap.value, defaultValue: null
  230. },
  231. { key: 'login', type: 'input', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), defaultValue: '' },
  232. { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
  233. ])
  234. const searchParams = ref({})
  235. const handleSearch = (params) => {
  236. search.value = params
  237. nextTick(() => {
  238. tableRef.value.refreshTable()
  239. })
  240. }
  241. const handleReset = (emptyParams) => {
  242. search.value = emptyParams
  243. nextTick(() => {
  244. tableRef.value.refreshTable()
  245. })
  246. }
  247. const mobilePrimaryFields = computed(() => [
  248. {
  249. prop: 'serial',
  250. label: t('Custom.PaymentHistory.Serial'),
  251. align: 'left'
  252. },
  253. {
  254. prop: 'status',
  255. label: t('Custom.PaymentHistory.Status'),
  256. slot: 'status',
  257. align: 'left'
  258. },
  259. {
  260. prop: 'amount',
  261. label: t('Custom.PaymentHistory.Amount'),
  262. formatter: ({ row }) => row.amount + ' ' + row.currency,
  263. align: 'right'
  264. },
  265. {
  266. prop: 'more',
  267. type: 'more',
  268. width: 20,
  269. align: 'right'
  270. },
  271. ])
  272. const listApi = ref(null)
  273. listApi.value = financialApi.BalanceList
  274. const confirm = useConfirm()
  275. const cancleConfirm = async (id, type) => {
  276. try {
  277. await confirm({
  278. title: t('Msg.SystemPrompt'),
  279. content: t('Msg.Cancle'),
  280. confirmText: t('Btn.item6'),
  281. cancelText: t('Btn.item7')
  282. })
  283. cancle(id, type)
  284. } catch (error) {
  285. }
  286. }
  287. const tableRef = ref(null)
  288. const cancle = async (id, type) => {
  289. try {
  290. let api
  291. if (type == 1) {
  292. api = financialApi.withdrawCancel
  293. } else {
  294. api = financialApi.withdrawCancelBackstage
  295. }
  296. let res = await api({ id: id });
  297. if (res.code == 200) {
  298. uni.showToast({
  299. title: t("Msg.Success"),
  300. icon: 'success'
  301. });
  302. tableRef.value.refreshTable()
  303. } else {
  304. uni.showToast({
  305. title: res.msg,
  306. icon: 'error'
  307. });
  308. }
  309. } catch (error) {
  310. console.log('取消删除')
  311. }
  312. }
  313. </script>
  314. <style scoped lang="scss">
  315. @import "@/uni.scss";
  316. .avatar {
  317. width: px2rpx(60);
  318. height: px2rpx(60);
  319. border-radius: 4px;
  320. }
  321. .content-title {
  322. display: flex;
  323. justify-content: space-between;
  324. align-items: center;
  325. font-size: px2rpx(20);
  326. font-weight: 500;
  327. .content-title-btns {
  328. margin: px2rpx(8) 0;
  329. display: flex;
  330. align-items: center;
  331. justify-content: center;
  332. gap: px2rpx(12);
  333. .btn-primary {
  334. min-width: px2rpx(120);
  335. background-color: var(--color-error);
  336. color: white;
  337. padding: 0 px2rpx(12);
  338. border: none;
  339. font-size: px2rpx(14);
  340. text-align: center;
  341. cursor: pointer;
  342. display: flex;
  343. align-items: center;
  344. justify-content: center;
  345. gap: px2rpx(8);
  346. }
  347. .btn-primary:active {
  348. background-color: var(--color-navy-700);
  349. }
  350. }
  351. }
  352. .operation-btn {
  353. :deep(text) {
  354. display: flex;
  355. align-items: center;
  356. justify-content: center;
  357. gap: px2rpx(4);
  358. cursor: pointer;
  359. background-color: var(--color-slate-150);
  360. padding: px2rpx(8) 0;
  361. }
  362. }
  363. .operation-btn.disabled {
  364. cursor: not-allowed;
  365. opacity: 0.5;
  366. }
  367. </style>