BankInfoTab.vue 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  1. <template>
  2. <view class="user-form crm-form">
  3. <uni-row class="demo-uni-row uni-row1">
  4. <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  5. <view class="card">
  6. <view class="bank-menu card-header">
  7. <view v-for="item in bankTypes" :key="item.key" class="bank-menu-item"
  8. :class="{ active: selectedBankType === item.key }" @click="selectedBankType = item.key">
  9. <!-- <image class="bank-icon" :src="item.icon" mode="widthFix" />-->
  10. <text>{{ item.label }}</text>
  11. </view>
  12. </view>
  13. <view class="card-body">
  14. <view class="bank-content" v-if="selectedBankType === 'crypto'">
  15. <view class="d-flex flex-wrap align-items-center header-title mb-3 card-header">
  16. <view class="h4">
  17. {{ t('blockchain.item2') }}
  18. </view>
  19. <button hover-class="" type="button" class="btn btn-danger btn-shadow waves-effect" @click="addBank()"
  20. v-if="cryptoWallets.length < 2">
  21. <view class="d-flex align-items-center">
  22. <cwg-icon name="crm-plus" :size="14" color="#fff" />
  23. <text>{{ `${t('Btn.New')}${t('blockchain.item2')}` }}</text>
  24. </view>
  25. </button>
  26. </view>
  27. <view class="bank-info bg-gray bg-opacity-05 card" v-for="(item, index) in cryptoWallets" :key="item.id">
  28. <view class="bank-header card-header">
  29. <uni-row style="width: 100%;">
  30. <uni-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
  31. <view class="bank-title">
  32. <text>{{ currentBankType?.label }} {{ index + 1 }}</text>
  33. </view>
  34. </uni-col>
  35. <uni-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
  36. <view class="actions">
  37. <view class="bank-actions">
  38. <view class="action-btn bg-secondary btn-dark" v-if="item.authStatus == 0" type="primary"
  39. v-t="'State.ToCertified'" @click="doReady(item.id, item)" />
  40. <view class="action-btn bg-secondary btn-dark" v-if="item.authStatus == 0" type="primary"
  41. @click="openCardDialog(item.id, item)" v-t="'PersonalManagement.CardVerify.Title'" />
  42. <view class="action-btn btn-gray" v-if="!editingId && item.authStatus !== 1"
  43. @tap="startEdit(item)" v-t="'Btn.Editor'" />
  44. <template v-if="editingId === item.id">
  45. <view class="action-btn btn-gray" @tap="saveBank(item)" v-t="'Btn.Save'" />
  46. <view class="action-btn btn-gray" @tap="cancelEdit()" v-t="'Btn.Cancel'" />
  47. </template>
  48. <view class="action-btn btn-gray" @tap="confirmDelete(item)" v-t="'Btn.Delete'" />
  49. </view>
  50. </view>
  51. </uni-col>
  52. </uni-row>
  53. </view>
  54. <uni-forms :model="item" labelWidth="200" label-position="top">
  55. <uni-row class="card-body custom-row" :gutter="20">
  56. <uni-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  57. <uni-forms-item :label="t('blockchain.item3')">
  58. <uni-easyinput :clearable="false" v-model="item.addressName" :disabled="editingId !== item.id"
  59. :placeholder="locale == 'es' ? 'Introduzca el nombre de la red' : t('placeholder.input')" />
  60. </uni-forms-item>
  61. </uni-col>
  62. <uni-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  63. <uni-forms-item :label="t('blockchain.item4')">
  64. <uni-easyinput :clearable="false" v-model="item.address" :disabled="editingId !== item.id"
  65. :placeholder="locale == 'es' ? 'Introduzca la dirección de la billetera' : t('placeholder.input')" />
  66. </uni-forms-item>
  67. </uni-col>
  68. <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"
  69. v-if="item.cardFiles && item.cardFiles.length">
  70. <uni-forms-item :label="t('PersonalManagement.Label.CertificationPhoto')">
  71. <view class="photo-upload">
  72. <view v-for="(file, idx) in item.cardFiles" :key="idx" class="photo-item">
  73. <image class="photo-preview" :src="updateUrl + file.path" mode="aspectFill" />
  74. </view>
  75. </view>
  76. </uni-forms-item>
  77. </uni-col>
  78. <uni-col :xs="24" class="mt-3">
  79. <uni-forms-item class="checkbox-item">
  80. <uni-data-checkbox :disabled="editingId !== item.id" v-model="item.defaultBank1" multiple
  81. :localdata="hobbys" />
  82. </uni-forms-item>
  83. </uni-col>
  84. </uni-row>
  85. </uni-forms>
  86. </view>
  87. </view>
  88. <view class="bank-content" v-if="selectedBankType === 'unionpay'">
  89. <view class="d-flex flex-wrap align-items-center header-title card-header mb-3">
  90. <view class="h4">
  91. {{ t('PersonalManagement.Title.ChinaUnionPayCard') }}
  92. </view>
  93. <button hover-class="" type="button" class="btn btn-danger btn-shadow waves-effect" @click="addBank()"
  94. v-if="unionpayCards.length < 2">
  95. <view class="d-flex align-items-center">
  96. <cwg-icon name="crm-plus" :size="14" color="#fff" />
  97. <text>{{ `${t('Btn.New')}${t('PersonalManagement.Title.ChinaUnionPayCard')}` }}</text>
  98. </view>
  99. </button>
  100. </view>
  101. <view class="bank-info bg-gray bg-opacity-05 card" v-for="(item, index) in unionpayCards" :key="item.id">
  102. <view class="bank-header card-header">
  103. <uni-row style="width: 100%;">
  104. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  105. <view class="bank-title">
  106. <text>{{ currentBankType?.label }}{{ index + 1 }}</text>
  107. </view>
  108. </uni-col>
  109. <uni-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
  110. <view class="bank-actions">
  111. <view class="action-btn btn-gray" v-if="!editingId && item.authStatus !== 1"
  112. @tap="startEdit(item)" v-t="'Btn.Editor'" />
  113. <template v-if="editingId === item.id">
  114. <view class="action-btn btn-gray" @tap="saveBank(item)" v-t="'Btn.Save'" />
  115. <view class="action-btn btn-gray" @tap="cancelEdit()" v-t="'Btn.Cancel'" />
  116. </template>
  117. <view class="action-btn btn-gray" @tap="confirmDelete(item)" v-t="'Btn.Delete'" />
  118. </view>
  119. </uni-col>
  120. </uni-row>
  121. </view>
  122. <uni-forms :model="item" labelWidth="200" label-position="top">
  123. <uni-row class="card-body custom-row" :gutter="20">
  124. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  125. <uni-forms-item :label="t('PersonalManagement.Label.BankAccountName')">
  126. <uni-easyinput :clearable="false" v-model="item.bankUname" :disabled="true"
  127. :placeholder="locale == 'es' ? 'Introduzca el nombre de la red' : t('placeholder.input')" />
  128. </uni-forms-item>
  129. </uni-col>
  130. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  131. <uni-forms-item :label="t('PersonalManagement.Label.BankName')">
  132. <cwg-combox :clearable="false" :filterable="true" v-model:value="item.bankName"
  133. :options="bankOptions" :placeholder="t('placeholder.choose')"
  134. :disabled="editingId !== item.id" @change="onStateChange" />
  135. </uni-forms-item>
  136. </uni-col>
  137. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  138. <uni-forms-item :label="t('PersonalManagement.Label.BankAccount')">
  139. <uni-easyinput :clearable="false" v-model="item.bankCardNum" :disabled="editingId !== item.id"
  140. :placeholder="locale == 'es' ? 'Introduzca la dirección de la billetera' : t('placeholder.input')" />
  141. </uni-forms-item>
  142. </uni-col>
  143. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  144. <uni-forms-item :label="t('PersonalManagement.Label.AccountOpeningBranch')">
  145. <uni-easyinput :clearable="false" v-model="item.bankBranchName"
  146. :disabled="editingId !== item.id"
  147. :placeholder="locale == 'es' ? 'Introduzca la dirección de la billetera' : t('placeholder.input')" />
  148. </uni-forms-item>
  149. </uni-col>
  150. <uni-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
  151. <uni-forms-item :label="t('PersonalManagement.Label.CertificationPhoto')">
  152. <cwg-file-picker-wrapper v-model="item.bankFront" :disabled="editingId !== item.id"
  153. :delIcon="editingId === item.id" :limit="1" uploadUrl="/custom/bank/upload"
  154. :baseUrl="updateUrl" :imageWidth="120" :imageHeight="120" uploadText="点击上传" replaceText="点击替换"
  155. noImageText="暂无图片" :showPreviewDelete="editingId === item.id"
  156. @update:modelValue="(val) => handleFileUpdate(val, item, 'bankFront')" />
  157. </uni-forms-item>
  158. </uni-col>
  159. <uni-col :xs="24" class="mt-3">
  160. <uni-forms-item class="checkbox-item">
  161. <uni-data-checkbox :disabled="editingId !== item.id" v-model="item.defaultBank1" multiple
  162. :localdata="hobbys" />
  163. </uni-forms-item>
  164. </uni-col>
  165. </uni-row>
  166. </uni-forms>
  167. </view>
  168. </view>
  169. <view class="bank-content" v-if="selectedBankType === 'bank'">
  170. <view class="d-flex flex-wrap align-items-center card-header header-title mb-3">
  171. <view class="h4">
  172. {{ t('PersonalManagement.Title.BankWireTransfer') }}
  173. </view>
  174. <button hover-class="" type="button" class="btn btn-danger btn-shadow waves-effect" @click="addBank()"
  175. v-if="wireTransfers.length < 2">
  176. <view class="d-flex align-items-center">
  177. <cwg-icon name="crm-plus" :size="14" color="#fff" />
  178. <text>{{ `${t('Btn.New')}${t('PersonalManagement.Title.BankWireTransfer')}` }}</text>
  179. </view>
  180. </button>
  181. </view>
  182. <view class="bank-info bg-gray bg-opacity-05 card" v-for="(item, index) in wireTransfers" :key="item.id">
  183. <view class="bank-header card-header ">
  184. <uni-row style="width: 100%;">
  185. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  186. <view class="bank-title">
  187. <text>{{ currentBankType?.label }} {{ index + 1 }}</text>
  188. </view>
  189. </uni-col>
  190. <uni-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
  191. <view class="bank-actions">
  192. <view class="action-btn btn-gray" v-if="!editingId && item.authStatus !== 1"
  193. @tap="startEdit(item)" v-t="'Btn.Editor'" />
  194. <template v-if="editingId === item.id">
  195. <view class="action-btn btn-gray" @tap="saveBank(item)" v-t="'Btn.Save'" />
  196. <view class="action-btn btn-gray" @tap="cancelEdit()" v-t="'Btn.Cancel'" />
  197. </template>
  198. <view class="action-btn btn-gray" @tap="confirmDelete(item)" v-t="'Btn.Delete'" />
  199. </view>
  200. </uni-col>
  201. </uni-row>
  202. </view>
  203. <uni-forms :model="item" labelWidth="200" label-position="top">
  204. <uni-row class="card-body custom-row" :gutter="20">
  205. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  206. <uni-forms-item :label="t('PersonalManagement.Label.BankAccountName')">
  207. <uni-easyinput :clearable="false" v-model="item.bankUname" :disabled="true"
  208. :placeholder="locale == 'es' ? 'Introduzca el nombre de la red' : t('placeholder.input')" />
  209. </uni-forms-item>
  210. </uni-col>
  211. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  212. <uni-forms-item :label="t('PersonalManagement.Label.BankAccount')">
  213. <uni-easyinput :clearable="false" v-model="item.bankCardNum" :disabled="editingId !== item.id"
  214. :placeholder="locale == 'es' ? 'Introduzca la dirección de la billetera' : t('placeholder.input')" />
  215. </uni-forms-item>
  216. </uni-col>
  217. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  218. <uni-forms-item :label="t('PersonalManagement.Label.BankName')">
  219. <uni-easyinput :clearable="false" v-model="item.bankName" :disabled="editingId !== item.id"
  220. :placeholder="locale == 'es' ? 'Introduzca el nombre del banco' : t('placeholder.input')" />
  221. </uni-forms-item>
  222. </uni-col>
  223. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  224. <uni-forms-item :label="t('PersonalManagement.Label.BankAddress')">
  225. <uni-easyinput :clearable="false" v-model="item.bankAddr"
  226. :placeholder="locale == 'es' ? 'Introduzca la dirección del banco' : t('placeholder.input')"
  227. :disabled="editingId !== item.id" />
  228. </uni-forms-item>
  229. </uni-col>
  230. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  231. <uni-forms-item :label="t('PersonalManagement.Label.SwiftBIC')">
  232. <uni-easyinput :clearable="false" v-model="item.swiftCode"
  233. :placeholder="locale == 'es' ? 'Introduzca el SWIFT/BIC' : t('placeholder.input')"
  234. :disabled="editingId !== item.id" />
  235. </uni-forms-item>
  236. </uni-col>
  237. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  238. <uni-forms-item :label="t('PersonalManagement.Label.BankCode')">
  239. <uni-easyinput :clearable="false" v-model="item.bankCode"
  240. :placeholder="locale == 'es' ? 'Introduzca el código del banco' : t('placeholder.input')"
  241. :disabled="editingId !== item.id" />
  242. </uni-forms-item>
  243. </uni-col>
  244. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  245. <uni-forms-item :label="locale == 'es' ? 'Número de sucursal (opcional)' : 'Account Agency NO'">
  246. <uni-easyinput :clearable="false" v-model="item.agencyNo"
  247. :placeholder="locale == 'es' ? 'Introduzca el número de sucursal' : t('placeholder.input')"
  248. :disabled="editingId !== item.id" />
  249. </uni-forms-item>
  250. </uni-col>
  251. <uni-col :xs="24" class="mt-3">
  252. <uni-forms-item class="checkbox-item">
  253. <uni-data-checkbox :disabled="editingId !== item.id" v-model="item.defaultBank1" multiple
  254. :localdata="hobbys" />
  255. </uni-forms-item>
  256. </uni-col>
  257. </uni-row>
  258. </uni-forms>
  259. </view>
  260. </view>
  261. <view class="bank-content" v-if="selectedBankType === 'credit'">
  262. <view class="d-flex flex-wrap align-items-center header-title card-header mb-3">
  263. <view class="h4">
  264. {{ t('PersonalManagement.Label.CreditCard') }}
  265. </view>
  266. <button hover-class="" type="button" class="btn btn-danger btn-shadow waves-effect" @click="addBank()"
  267. v-if="creditCards.length < 2">
  268. <view class="d-flex align-items-center">
  269. <cwg-icon name="crm-plus" :size="14" color="#fff" />
  270. <text>{{ `${t('Btn.New')}${t('PersonalManagement.Label.CreditCard')}` }}</text>
  271. </view>
  272. </button>
  273. </view>
  274. <view class="bank-info bg-gray bg-opacity-05 card" v-for="(item, index) in creditCards" :key="item.id">
  275. <view class="bank-header card-header">
  276. <uni-row style="width: 100%;">
  277. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  278. <view class="bank-title">
  279. <text>{{ currentBankType?.label }} {{ index + 1 }}</text>
  280. </view>
  281. </uni-col>
  282. <uni-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
  283. <view class="bank-actions">
  284. <view class="action-btn btn-gray" v-if="!editingId && item.authStatus !== 1"
  285. @tap="startEdit(item)" v-t="'Btn.Editor'" />
  286. <template v-if="editingId === item.id">
  287. <view class="action-btn btn-gray" @tap="saveBank(item)" v-t="'Btn.Save'" />
  288. <view class="action-btn btn-gray" @tap="cancelEdit()" v-t="'Btn.Cancel'" />
  289. </template>
  290. <view class="action-btn btn-gray" @tap="confirmDelete(item)" v-t="'Btn.Delete'" />
  291. </view>
  292. </uni-col>
  293. </uni-row>
  294. </view>
  295. <uni-forms :model="item" labelWidth="200" label-position="top">
  296. <uni-row class="card-body custom-row" :gutter="20">
  297. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  298. <uni-forms-item :label="t('PersonalManagement.Label.CreditCardAccountName')">
  299. <uni-easyinput :clearable="false" v-model="item.bankUname" :disabled="true"
  300. :placeholder="t('placeholder.input')" />
  301. </uni-forms-item>
  302. </uni-col>
  303. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  304. <uni-forms-item :label="t('PersonalManagement.Label.CreditCardAccount')">
  305. <uni-easyinput :clearable="false" v-model="item.bankCardNum" :disabled="editingId !== item.id"
  306. :placeholder="locale == 'es' ? 'Introduzca el número de tarjeta' : t('placeholder.input')" />
  307. </uni-forms-item>
  308. </uni-col>
  309. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  310. <uni-forms-item :label="t('PersonalManagement.Label.ExpirationYear')">
  311. <uni-easyinput :clearable="false" v-model="item.expiryYearMonth"
  312. :disabled="editingId !== item.id"
  313. :placeholder="locale == 'es' ? 'Introduzca MM/AA' : t('placeholder.input')" />
  314. </uni-forms-item>
  315. </uni-col>
  316. <uni-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
  317. <uni-forms-item :label="t('CVV')">
  318. <uni-easyinput :clearable="false" v-model="item.cvv" :disabled="editingId !== item.id"
  319. :placeholder="locale == 'es' ? 'Introduzca el CVV' : t('placeholder.input')" />
  320. </uni-forms-item>
  321. </uni-col>
  322. <uni-col :xs="24" class="mt-3">
  323. <uni-forms-item class="checkbox-item">
  324. <uni-data-checkbox :disabled="editingId !== item.id" v-model="item.defaultBank1" multiple
  325. :localdata="hobbys" />
  326. </uni-forms-item>
  327. </uni-col>
  328. </uni-row>
  329. </uni-forms>
  330. </view>
  331. </view>
  332. </view>
  333. </view>
  334. </uni-col>
  335. </uni-row>
  336. </view>
  337. <!-- 删除确认弹窗 -->
  338. <uni-popup ref="deletePopup" type="dialog">
  339. <uni-popup-dialog :title="t('Msg.SystemPrompt')" :content="t('Msg.Delete')" @confirm="deleteBank"
  340. @close="closeDeletePopup" :cancelText="t('Btn.Cancel')" :confirmText="t('Btn.Confirm')" />
  341. </uni-popup>
  342. <!-- 新增银行弹窗 -->
  343. <add-bank-dialog ref="addBankDialogRef" @success="addSuccess" />
  344. <!-- kyc认证弹窗 -->
  345. <kyc-auth-dialog ref="kycDialogRef" />
  346. <!-- 证件认证弹窗 -->
  347. <card-auth-dialog ref="cardDialogRef" />
  348. <cwg-global-popup />
  349. </template>
  350. <script setup lang="ts">
  351. import { computed, ref, onMounted } from 'vue'
  352. import { useI18n } from 'vue-i18n'
  353. import { personalApi } from '@/service/personal'
  354. import KycAuthDialog from './KycAuthDialog.vue'
  355. import CardAuthDialog from './CardAuthDialog.vue'
  356. import AddBankDialog from '@/components/AddBankDialog.vue'
  357. import config from '@/config'
  358. import { BankType } from './bank'
  359. import { usePopup } from '@/hooks/usePopup'
  360. const { t, locale } = useI18n()
  361. const { toast } = usePopup()
  362. interface BankListType {
  363. key: string;
  364. label: string;
  365. icon: string;
  366. }
  367. interface BankInfo {
  368. id?: string;
  369. type: number;
  370. defaultBank: boolean;
  371. approveStatus?: number;
  372. authStatus?: number;
  373. blockchainName: string;
  374. walletAddress: string;
  375. photos?: string[];
  376. expiryYearMonth?: string;
  377. expiryYear?: string;
  378. expiryMonth?: string;
  379. [key: string]: any;
  380. }
  381. const selectedBankType = ref<string>('crypto')
  382. const bankTypes = computed<BankListType[]>(() => [
  383. { key: 'crypto', label: t('blockchain.item2'), icon: '/static/images/info/bank-info_1.png' },
  384. {
  385. key: 'unionpay',
  386. label: t('PersonalManagement.Title.ChinaUnionPayCard'),
  387. icon: '/static/images/info/bank-info_2.png',
  388. },
  389. { key: 'bank', label: t('PersonalManagement.Title.BankWireTransfer'), icon: '/static/images/info/bank-info_3.png' },
  390. { key: 'credit', label: t('PersonalManagement.Label.CreditCard'), icon: '/static/images/info/bank-info_4.png' },
  391. ])
  392. const currentBankType = computed(() => bankTypes.value.find((item: BankListType) => item.key === selectedBankType.value))
  393. const hobbys = computed(() => {
  394. switch (selectedBankType.value) {
  395. case 'crypto':
  396. return [{ value: 1, text: t('blockchain.item8') }]
  397. case 'unionpay':
  398. return [{ value: 1, text: t('PersonalManagement.Title.DefaultBank') }]
  399. case 'bank':
  400. return [{ value: 1, text: t('PersonalManagement.Title.DefaultWire') }]
  401. case 'credit':
  402. return [{ value: 1, text: t('PersonalManagement.Title.DefaultCredit') }]
  403. }
  404. })
  405. // 状态
  406. const updateUrl = config.Host05
  407. const editingId = ref(null)
  408. const bankList = ref([])
  409. const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value))
  410. const getLangName = (item: any) => (isZh.value ? item.name : item.enName)
  411. const createOptions = (list: any[], valueKey = 'code') => {
  412. return list.map((item) => ({
  413. text: getLangName(item),
  414. value: getLangName(item),
  415. }))
  416. }
  417. const bankOptions = computed(() => createOptions(bankList.value, 'name'))
  418. // 银行数据
  419. const bankData = ref({
  420. [BankType.CRYPTO]: [],
  421. [BankType.UNIONPAY]: [],
  422. [BankType.WIRE_TRANSFER]: [],
  423. [BankType.CREDIT_CARD]: [],
  424. })
  425. // 计算属性 - 各类型银行列表
  426. const cryptoWallets = computed(() => bankData.value[BankType.CRYPTO] || [])
  427. const unionpayCards = computed(() => bankData.value[BankType.UNIONPAY] || [])
  428. const wireTransfers = computed(() => bankData.value[BankType.WIRE_TRANSFER] || [])
  429. const creditCards = computed(() => bankData.value[BankType.CREDIT_CARD] || [])
  430. // 弹出层ref
  431. const deletePopup = ref(null)
  432. const deleteTarget = ref(null)
  433. // 开始编辑
  434. const startEdit = (item) => {
  435. editingId.value = item.id
  436. }
  437. // 取消编辑
  438. const cancelEdit = () => {
  439. editingId.value = null
  440. getBankInfo() // 重新加载数据
  441. }
  442. // 保存银行信息
  443. const saveBank = async (item) => {
  444. if (selectedBankType.value === 'crypto' && item.approveStatus == 1) {
  445. toast(t('PersonalManagement.BlockchainWalletReviewing'))
  446. return
  447. }
  448. try {
  449. const params = { ...item }
  450. params.defaultBank = params.defaultBank1[0] ? 1 : 0
  451. if (params.type === BankType.CREDIT_CARD && params.expiryYearMonth) {
  452. const [year, month] = params.expiryYearMonth.split('/')
  453. params.expiryYear = year
  454. params.expiryMonth = month
  455. }
  456. const res = await personalApi.customBankUpdate(params)
  457. if (res.code === 200) {
  458. uni.hideLoading()
  459. uni.showToast({
  460. title: t('Msg.Success'),
  461. icon: 'success',
  462. })
  463. editingId.value = null
  464. getBankInfo()
  465. } else {
  466. throw new Error(res.msg)
  467. }
  468. } catch (error) {
  469. uni.hideLoading()
  470. uni.showToast({
  471. title: error.msg || t('Msg.Fail'),
  472. icon: 'none',
  473. })
  474. }
  475. }
  476. // 删除
  477. const confirmDelete = (item) => {
  478. if (selectedBankType.value === 'crypto' && item.approveStatus == 1) {
  479. toast(t('PersonalManagement.BlockchainWalletReviewing'))
  480. return
  481. }
  482. deleteTarget.value = item
  483. deletePopup.value.open()
  484. }
  485. // 关闭删除弹窗
  486. const closeDeletePopup = () => {
  487. deletePopup.value.close()
  488. deleteTarget.value = null
  489. }
  490. // 执行删除
  491. const deleteBank = async () => {
  492. if (!deleteTarget.value) return
  493. try {
  494. const res = await personalApi.customBankDelete({
  495. ids: [deleteTarget.value.id],
  496. })
  497. if (res.code === 200) {
  498. uni.hideLoading()
  499. uni.showToast({
  500. title: t('Msg.DeleteSuccess'),
  501. icon: 'success',
  502. })
  503. deletePopup.value.close()
  504. deleteTarget.value = null
  505. getBankInfo()
  506. } else {
  507. uni.showToast({
  508. title: res.msg,
  509. icon: 'none',
  510. })
  511. }
  512. } catch (error) {
  513. uni.hideLoading()
  514. uni.showToast({
  515. title: error.msg,
  516. icon: 'none',
  517. })
  518. deletePopup.value.close()
  519. }
  520. }
  521. // 新增银行信息
  522. const addBankDialogRef = ref(null)
  523. function addBank() {
  524. switch (selectedBankType.value) {
  525. case 'crypto':
  526. openAddCrypto()
  527. break
  528. case 'unionpay':
  529. openAddUnionpay()
  530. break
  531. case 'bank':
  532. openAddBank()
  533. break
  534. case 'credit':
  535. openAddCredit()
  536. break
  537. }
  538. }
  539. function openAddCrypto() {
  540. const wallets = cryptoWallets.value || []
  541. // 1️⃣ 没有钱包
  542. if (wallets.length === 0) {
  543. addBankDialogRef.value?.open(4)
  544. return
  545. }
  546. // 2️⃣ 是否存在未认证钱包
  547. const hasUnAuth = wallets.some(
  548. item => item.authStatus === 0 || item.approveStatus === 1,
  549. )
  550. if (hasUnAuth) {
  551. toast(t('PersonalManagement.BlockchainWalletNotCertified'))
  552. return
  553. }
  554. // 3️⃣ 是否达到上限
  555. if (wallets.length >= 2) {
  556. toast(t('blockchain.item9'))
  557. return
  558. }
  559. // 4️⃣ 正常打开
  560. addBankDialogRef.value?.open(4)
  561. }
  562. function openAddUnionpay() {
  563. const wallets = unionpayCards.value || []
  564. if (wallets.length === 0) {
  565. addBankDialogRef.value?.open(1)
  566. return
  567. }
  568. if (wallets.length >= 2) {
  569. toast(t('Msg.UnionPayCARDS'))
  570. return
  571. }
  572. addBankDialogRef.value?.open(1)
  573. }
  574. function openAddBank() {
  575. const wallets = wireTransfers.value || []
  576. if (wallets.length === 0) {
  577. addBankDialogRef.value?.open(2)
  578. return
  579. }
  580. if (wallets.length >= 2) {
  581. toast(t('Msg.WireTransfers'))
  582. return
  583. }
  584. addBankDialogRef.value?.open(2)
  585. }
  586. function openAddCredit() {
  587. const wallets = creditCards.value || []
  588. if (wallets.length === 0) {
  589. addBankDialogRef.value?.open(3)
  590. return
  591. }
  592. addBankDialogRef.value?.open(3)
  593. }
  594. // 新增银行信息成功回调
  595. const addSuccess = (e) => {
  596. if (selectedBankType.value === 'crypto') {
  597. openKycDialog(e)
  598. }
  599. getBankInfo()
  600. }
  601. // kyc认证和证件认证
  602. const kycDialogRef = ref(null)
  603. const cardDialogRef = ref(null)
  604. // kyc认证
  605. const doReady = (bankId, item) => {
  606. if (item.approveStatus == 1) {
  607. toast(t('PersonalManagement.BlockchainWalletReviewing'))
  608. return
  609. }
  610. if (item.authStatus == 1) {
  611. toast(t('PersonalManagement.BlockchainWalletNotCertified'))
  612. return
  613. }
  614. openKycDialog(bankId)
  615. }
  616. // 打开kyc认证弹窗
  617. function openKycDialog(e) {
  618. kycDialogRef.value?.open(e)
  619. }
  620. // 打开证件认证弹窗
  621. function openCardDialog(e, item) {
  622. if (item.approveStatus == 1) {
  623. toast(t('PersonalManagement.BlockchainWalletReviewing'))
  624. return
  625. }
  626. if (item.authStatus == 1) {
  627. toast(t('PersonalManagement.BlockchainWalletNotCertified'))
  628. return
  629. }
  630. cardDialogRef.value?.open(e, item)
  631. }
  632. // 掩码卡号
  633. const maskCardNumber = (num) => {
  634. if (!num) return '--'
  635. if (num.length <= 4) return num
  636. return '**** **** **** ' + num.slice(-4)
  637. }
  638. // 获取银行列表
  639. const getBankList = async () => {
  640. const res = await personalApi.BankList({})
  641. if (res.code === 200) {
  642. bankList.value = res.data
  643. }
  644. }
  645. // 文件更新处理
  646. const handleFileUpdate = (newValue, item, field) => {
  647. item[field] = newValue
  648. }
  649. // 或者如果你需要在上传成功后做其他操作
  650. const handleUploadComplete = (result, item, field) => {
  651. if (result.success) {
  652. console.log('上传完成:', result.paths)
  653. // 可以在这里做其他操作,比如保存到服务器
  654. }
  655. }
  656. // 获取银行信息
  657. const getBankInfo = async () => {
  658. try {
  659. const res = await personalApi.customBankList({})
  660. if (res.code === 200) {
  661. // 清空数据
  662. bankData.value = {
  663. [BankType.CRYPTO]: [],
  664. [BankType.UNIONPAY]: [],
  665. [BankType.WIRE_TRANSFER]: [],
  666. [BankType.CREDIT_CARD]: [],
  667. }
  668. // 分类数据
  669. res.data.forEach(item => {
  670. item.defaultBank1 = item.defaultBank == 1 ? [1] : []
  671. if (item.type === BankType.UNIONPAY) {
  672. bankData.value[BankType.UNIONPAY].push(item)
  673. } else if (item.type === BankType.WIRE_TRANSFER) {
  674. bankData.value[BankType.WIRE_TRANSFER].push(item)
  675. } else if (item.type === BankType.CREDIT_CARD) {
  676. item.expiryYearMonth = item.expiryYear && item.expiryMonth
  677. ? `${item.expiryYear}/${item.expiryMonth}`
  678. : ''
  679. bankData.value[BankType.CREDIT_CARD].push(item)
  680. } else if (item.type === BankType.CRYPTO) {
  681. bankData.value[BankType.CRYPTO].push(item)
  682. }
  683. })
  684. }
  685. } catch (error) {
  686. uni.showToast({
  687. title: error.msg || t('Msg.Fail'),
  688. icon: 'none',
  689. })
  690. }
  691. }
  692. // 获取银行信息
  693. onMounted(() => {
  694. getBankList()
  695. getBankInfo()
  696. })
  697. </script>
  698. <style scoped lang="scss">
  699. @import "@/uni.scss";
  700. .btn {
  701. margin: 0;
  702. }
  703. .user-form {
  704. margin-top: px2rpx(30);
  705. }
  706. :deep(.uni-row1) {
  707. .uni-col {}
  708. .uni-forms-item {
  709. min-height: px2rpx(79);
  710. margin-bottom: px2rpx(10);
  711. }
  712. .uni-easyinput__content {
  713. //background-color: var(--bs-secondary-bg) !important;
  714. }
  715. }
  716. .bank-menu {
  717. // background: #fff;
  718. display: flex;
  719. flex-wrap: wrap;
  720. overflow: hidden;
  721. &.card-header {
  722. padding: px2rpx(10) px2rpx(20);
  723. }
  724. .bank-menu-item {
  725. flex: 1;
  726. min-width: px2rpx(260);
  727. display: flex;
  728. align-items: center;
  729. justify-content: center;
  730. //gap: px2rpx(12);
  731. padding: 0 px2rpx(16);
  732. cursor: pointer;
  733. font-size: px2rpx(16);
  734. font-weight: bold;
  735. color: var(--bs-emphasis-color);
  736. transition: all 0.3s;
  737. height: px2rpx(36);
  738. border-radius: px2rpx(8);
  739. .bank-icon {
  740. width: px2rpx(50);
  741. }
  742. &.active {
  743. background: #f5f5f5;
  744. color: #000;
  745. border-radius: px2rpx(8);
  746. &:hover {
  747. background: var(--bs-link-hover-color-rgb);
  748. color: #333;
  749. }
  750. }
  751. &:hover {
  752. background: #f5f5f5;
  753. color: #333;
  754. }
  755. &.active:hover {
  756. background: #f5f5f5;
  757. }
  758. }
  759. }
  760. .bank-content {
  761. //border-radius: px2rpx(8);
  762. .bank-info {
  763. margin-bottom: px2rpx(20);
  764. .bank-header {
  765. display: flex;
  766. justify-content: space-between;
  767. align-items: center;
  768. //border-bottom: 1px solid #f3f4f6;
  769. .bank-title {
  770. display: flex;
  771. align-items: center;
  772. font-size: px2rpx(20);
  773. font-weight: 600;
  774. line-height: px2rpx(32);
  775. color: #1f2937;
  776. //margin-bottom: px2rpx(20);
  777. }
  778. .actions {
  779. display: flex;
  780. flex-direction: column;
  781. }
  782. @media screen and (max-width: 992px) {
  783. .bank-actions {
  784. justify-content: flex-start !important;
  785. }
  786. }
  787. .bank-actions {
  788. display: flex;
  789. gap: px2rpx(20);
  790. align-items: center;
  791. flex-wrap: wrap;
  792. //margin-bottom: px2rpx(10);
  793. justify-content: flex-end;
  794. .action-btn {
  795. padding: px2rpx(8) px2rpx(16);
  796. border: 1px solid #d1d5db;
  797. border-radius: px2rpx(4);
  798. font-size: px2rpx(14);
  799. cursor: pointer;
  800. background: #fff;
  801. transition: all 0.3s;
  802. &:hover {
  803. background: #f3f4f6;
  804. }
  805. &.delete {
  806. background: var(--bs-btn-bg);
  807. color: #fff;
  808. border-color: var(--bs-btn-bg);
  809. &:hover {
  810. background: var(--bs-btn-hover-bg);
  811. }
  812. }
  813. }
  814. }
  815. }
  816. }
  817. .photo-upload {
  818. display: flex;
  819. flex-wrap: wrap;
  820. gap: px2rpx(16);
  821. .photo-item {
  822. width: px2rpx(120);
  823. height: px2rpx(120);
  824. border-radius: px2rpx(8);
  825. overflow: hidden;
  826. background: #f3f4f6;
  827. .photo-preview {
  828. width: 100%;
  829. height: 100%;
  830. }
  831. }
  832. }
  833. .header-title {
  834. justify-content: space-between;
  835. padding: 0 0 px2rpx(20) 0;
  836. }
  837. .add-wallet-btn {
  838. height: px2rpx(40);
  839. background: var(--bs-secondary);
  840. color: #fff;
  841. border-radius: px2rpx(8);
  842. padding: 0 px2rpx(20);
  843. display: flex;
  844. align-items: center;
  845. justify-content: center;
  846. gap: px2rpx(8);
  847. font-size: px2rpx(16);
  848. font-weight: 600;
  849. cursor: pointer;
  850. transition: all 0.3s;
  851. &:hover {
  852. background: #d11920;
  853. }
  854. }
  855. .bankFront {
  856. width: 100%;
  857. // 上传包装器
  858. .upload-wrapper {
  859. :deep(.uni-file-picker) {
  860. .file-picker__box {
  861. border: none;
  862. background: transparent;
  863. }
  864. // 文件列表容器
  865. .file-picker__box-list {
  866. display: flex;
  867. gap: px2rpx(16);
  868. flex-wrap: wrap;
  869. // 已上传的文件项
  870. .file-picker__box-item {
  871. width: px2rpx(300) !important;
  872. height: px2rpx(300) !important;
  873. margin: 0;
  874. border: 1px solid #e5e7eb;
  875. border-radius: px2rpx(8);
  876. overflow: hidden;
  877. position: relative;
  878. // 图片预览
  879. .file-picker__box-item-image {
  880. width: 100%;
  881. height: 100%;
  882. object-fit: cover;
  883. }
  884. // 删除按钮
  885. .file-picker__box-item-close {
  886. position: absolute;
  887. top: px2rpx(4);
  888. right: px2rpx(4);
  889. width: px2rpx(24);
  890. height: px2rpx(24);
  891. background: rgba(0, 0, 0, 0.5);
  892. border-radius: 50%;
  893. display: flex;
  894. align-items: center;
  895. justify-content: center;
  896. &::before {
  897. content: '×';
  898. color: var(--bs-emphasis-color);
  899. font-size: px2rpx(28);
  900. line-height: 1;
  901. }
  902. .close-icon {
  903. display: none;
  904. }
  905. }
  906. }
  907. }
  908. // 上传按钮
  909. .file-picker__box-add {
  910. width: px2rpx(300) !important;
  911. height: px2rpx(300) !important;
  912. margin: 0;
  913. border: 2rpx dashed #d1d5db;
  914. border-radius: px2rpx(8);
  915. background: #f9fafb;
  916. transition: all 0.3s;
  917. &:hover {
  918. border-color: #ea2027;
  919. background: #fef2f2;
  920. }
  921. // 自定义上传按钮内容
  922. .custom-upload-btn {
  923. width: 100%;
  924. height: 100%;
  925. display: flex;
  926. flex-direction: column;
  927. align-items: center;
  928. justify-content: center;
  929. .plus {
  930. font-size: px2rpx(48);
  931. color: #9ca3af;
  932. line-height: 1;
  933. margin-bottom: px2rpx(8);
  934. }
  935. .tip {
  936. font-size: px2rpx(24);
  937. color: #6b7280;
  938. }
  939. }
  940. // 隐藏默认的加号
  941. .add-icon {
  942. display: none;
  943. }
  944. }
  945. // 上传进度
  946. .file-picker__box-progress {
  947. width: px2rpx(300);
  948. height: px2rpx(300);
  949. border-radius: px2rpx(8);
  950. background: rgba(0, 0, 0, 0.5);
  951. color: var(--bs-emphasis-color);
  952. }
  953. }
  954. }
  955. // 图片预览
  956. .image-preview {
  957. .preview-image {
  958. width: px2rpx(300);
  959. height: px2rpx(300);
  960. border-radius: px2rpx(8);
  961. object-fit: cover;
  962. cursor: pointer;
  963. border: 1px solid #e5e7eb;
  964. }
  965. .no-image {
  966. width: px2rpx(300);
  967. height: px2rpx(300);
  968. display: flex;
  969. align-items: center;
  970. justify-content: center;
  971. background: #f5f5f5;
  972. border-radius: px2rpx(8);
  973. color: var(--bs-heading-color);
  974. font-size: px2rpx(24);
  975. border: 1px solid #e5e7eb;
  976. }
  977. }
  978. }
  979. }
  980. .custom-row {
  981. gap: px2rpx(20); // 使用 gap 替代 gutter
  982. :deep(.cwg-combox) {
  983. .disabled-text {
  984. height: 100%;
  985. }
  986. height: 3rem;
  987. }
  988. }
  989. </style>