transfer-history.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <cwg-header :title="t('Ib.Report.Tit1')" />
  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" :immediate="false" :mobilePrimaryFields="mobilePrimaryFields"
  8. :queryParams="search" :api="listApi" :show-operation="false">
  9. <template #symbol="{ row }">
  10. <view class="symbol-cell">
  11. <view class="pair">{{ row.ticket || '--' }}</view>
  12. <view class="desc">{{ getAccountTypeText(row.loginType) }} - {{ row.login || '--' }}</view>
  13. </view>
  14. </template>
  15. <template #profit="{ row }">
  16. <view class="symbol-cell">
  17. <text>{{ row.amount || 0 }}</text>
  18. </view>
  19. </template>
  20. <template #accountType="{ row }">
  21. {{ getAccountTypeText(row.type || row.loginType) }}
  22. </template>
  23. <template #status="{ row }">
  24. <view class="symbol-cell">
  25. <text v-t="'State.ToBeProcessed'" v-if="row.status == 1"></text>
  26. <text v-t="'State.InTheProcessing'" v-if="row.status == 2 && row.tradeStatus == 1"></text>
  27. <text v-t="'State.Completed'" v-if="row.status == 2 && row.tradeStatus == 2"></text>
  28. <text v-t="'State.Refused'" v-if="row.status == 3 || row.tradeStatus == 3"></text>
  29. </view>
  30. </template>
  31. </cwg-tabel>
  32. </view>
  33. </cwg-page-wrapper>
  34. </template>
  35. <script setup lang="ts">
  36. import { computed, ref, nextTick, reactive } from 'vue';
  37. import { useI18n } from 'vue-i18n';
  38. import { onLoad } from '@dcloudio/uni-app'
  39. const { t, locale } = useI18n();
  40. import { documentaryApi } from '@/service/documentary';
  41. import { useAccountOptions } from '@/composables/useAccountOptions'
  42. const { loginOptions, isLoaded, isSuccess } = useAccountOptions()
  43. const search = reactive({
  44. login: null,
  45. type: null,
  46. orderStatus: null,
  47. date: null
  48. })
  49. // 账户类型映射
  50. const accountTypeMap = {
  51. 1: 'AccountType.ClassicAccount',
  52. 2: 'AccountType.SeniorAccount',
  53. 5: 'AccountType.SpeedAccount',
  54. 6: 'AccountType.SpeedAccount',
  55. 7: 'AccountType.StandardAccount',
  56. 8: 'AccountType.CentAccount'
  57. }
  58. // 获取账户类型文本
  59. const getAccountTypeText = (type: number) => {
  60. const key = accountTypeMap[type as keyof typeof accountTypeMap]
  61. return key ? t(key) : '--'
  62. }
  63. // 表格列配置
  64. const columns = computed(() => [
  65. {
  66. prop: 'ticket',
  67. label: t('Documentary.TundManagement.item5'),
  68. align: 'center',
  69. formatter: ({ row }) => row.ticket || '--'
  70. },
  71. {
  72. prop: 'login',
  73. label: t('Documentary.console.item4'),
  74. align: 'center',
  75. formatter: ({ row }) => row.login || '--'
  76. },
  77. {
  78. prop: 'loginType',
  79. label: t('Label.Type'),
  80. align: 'center',
  81. slot: 'accountType'
  82. },
  83. {
  84. prop: 'amount',
  85. label: t('Label.Amount'),
  86. align: 'center',
  87. formatter: ({ row }) => row.amount || '0' | NumberFormat1
  88. },
  89. {
  90. prop: 'addTime',
  91. label: t('Documentary.TundManagement.item6'),
  92. align: 'center',
  93. formatter: ({ row }) => row.addTime || '--'
  94. },
  95. {
  96. prop: 'approveTime',
  97. label: t('Documentary.TundManagement.item7'),
  98. align: 'center',
  99. formatter: ({ row }) => row.approveTime || '--'
  100. },
  101. {
  102. prop: 'status',
  103. label: t('Label.State'),
  104. align: 'center',
  105. slot: 'status'
  106. },
  107. {
  108. prop: 'approveDesc',
  109. label: t('Label.Note'),
  110. align: 'center',
  111. className: 'bor-r',
  112. type: 'note'
  113. }
  114. ])
  115. const mobilePrimaryFields = computed(() => [
  116. {
  117. prop: 'symbol',
  118. label: t('Documentary.TundManagement.item5'), // 交易品种
  119. align: 'left',
  120. slot: 'symbol'
  121. },
  122. {
  123. prop: 'profit',
  124. label: t('Label.Amount'), // 利润, USD
  125. slot: 'profit',
  126. align: 'right'
  127. },
  128. {
  129. prop: 'more',
  130. type: 'more',
  131. width: 20,
  132. align: 'right'
  133. },
  134. ])
  135. const orderStatusMap = computed(() => ([
  136. { value: null, text: t('Custom.PaymentHistory.All') },
  137. { value: 0, text: t('State.ToBeProcessed') },
  138. { value: 1, text: t('State.InTheProcessing') },
  139. { value: 2, text: t('State.Completed') },
  140. { value: 3, text: t('State.Refused') },
  141. ]));
  142. const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
  143. // 动态传入筛选字段配置
  144. const filterFields = computed(() => [
  145. {
  146. key: 'status', type: 'select', label: t('Custom.PaymentHistory.Status'), placeholder: t('placeholder.choose'), options: orderStatusMap.value, defaultValue: null
  147. },
  148. { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
  149. ])
  150. const searchParams = ref({})
  151. const tableRef = ref(null)
  152. const handleSearch = (params) => {
  153. Object.assign(search, params)
  154. // search.login = params.login && Number(params.login)
  155. // console.log(params.login, 12);
  156. search.platform = loginOptions.find(item => item.value == params.login)?.platform || ''
  157. nextTick(() => {
  158. tableRef.value.refreshTable()
  159. })
  160. }
  161. const handleReset = (params) => {
  162. Object.assign(search, params)
  163. search.platform = loginOptions.find(item => item.value == params.login)?.platform || ''
  164. nextTick(() => {
  165. tableRef.value.refreshTable()
  166. })
  167. }
  168. const listApi = ref(null)
  169. listApi.value = documentaryApi.followTransferList
  170. const getSymbolParts = (sym: string) => {
  171. if (!sym) return ['', '']
  172. const s = String(sym).toUpperCase()
  173. if (s.includes('/')) {
  174. const [base, quote] = s.split('/')
  175. return [base, quote]
  176. }
  177. const base = s.slice(0, 3)
  178. const quote = s.slice(3)
  179. return [base, quote]
  180. }
  181. const formatCmdName = (cmd: string) => {
  182. const v = String(cmd || '').toLowerCase()
  183. if (v.includes('sell')) return '卖出'
  184. if (v.includes('buy')) return '买入'
  185. return cmd || ''
  186. }
  187. const getCmdColorClass = (cmd: string) => {
  188. const v = String(cmd || '').toLowerCase()
  189. if (v.includes('sell')) return 'is-sell'
  190. if (v.includes('buy')) return 'is-buy'
  191. return ''
  192. }
  193. const getProfitColorClass = (profit: any) => {
  194. const n = Number(profit)
  195. if (!Number.isFinite(n) || n === 0) return ''
  196. return n > 0 ? 'is-profit' : 'is-loss'
  197. }
  198. onLoad((e) => {
  199. console.log(e, 'e')
  200. if (e.login) {
  201. // ✅ 必须转数字!你的 value 是数字类型
  202. search.login = Number(e.login)
  203. }
  204. })
  205. </script>
  206. <style scoped lang="scss">
  207. @import "@/uni.scss";
  208. .avatar {
  209. width: px2rpx(60);
  210. height: px2rpx(60);
  211. border-radius: 4px;
  212. }
  213. .content-title {
  214. display: flex;
  215. justify-content: space-between;
  216. align-items: center;
  217. font-size: px2rpx(20);
  218. font-weight: 500;
  219. .content-title-btns {
  220. margin: px2rpx(8) 0;
  221. display: flex;
  222. align-items: center;
  223. justify-content: center;
  224. gap: px2rpx(12);
  225. .btn-primary {
  226. min-width: px2rpx(120);
  227. background-color: var(--color-error);
  228. color: white;
  229. padding: 0 px2rpx(12);
  230. border: none;
  231. font-size: px2rpx(14);
  232. text-align: center;
  233. cursor: pointer;
  234. display: flex;
  235. align-items: center;
  236. justify-content: center;
  237. gap: px2rpx(8);
  238. }
  239. .btn-primary:active {
  240. background-color: #cf1322;
  241. ;
  242. }
  243. }
  244. }
  245. .operation-btn {
  246. :deep(span) {
  247. display: flex;
  248. align-items: center;
  249. justify-content: center;
  250. gap: px2rpx(4);
  251. cursor: pointer;
  252. background-color: var(--color-slate-150);
  253. padding: px2rpx(8) 0;
  254. }
  255. }
  256. .operation-btn.disabled {
  257. cursor: not-allowed;
  258. opacity: 0.5;
  259. }
  260. .symbol-cell {
  261. display: inline-flex;
  262. align-items: flex-start;
  263. gap: 0.25rem;
  264. flex-direction: column;
  265. .pair {
  266. font-weight: 600;
  267. color: var(--color-slate-900);
  268. }
  269. .desc {
  270. color: var(--color-slate-600);
  271. }
  272. }
  273. .is-sell,
  274. .is-loss {
  275. color: #eb483f;
  276. }
  277. .is-buy,
  278. .is-profit {
  279. color: #46cd7c;
  280. }
  281. .search-bar {
  282. display: flex;
  283. align-items: center;
  284. justify-content: flex-start;
  285. flex-wrap: wrap;
  286. gap: px2rpx(16);
  287. margin: px2rpx(16) 0;
  288. .cwg-combox,
  289. .uni-easyinput,
  290. .uni-date {
  291. width: px2rpx(240) !important;
  292. flex: none;
  293. }
  294. }
  295. </style>