BankInfoTab.vue 45 KB

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