global-order.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. <template>
  2. <cwg-page-wrapper>
  3. <view class="page">
  4. <DynamicForm ref="globalFormRef" :global-form="globalForm" :fields="globalCurrenciesField" :step2="step2"
  5. type="1">
  6. <template #top>
  7. <cwg-input :label="t('global.t2')" v-model:value="globalForm.deductionAccount" fkey="deductionAccount"
  8. type="select" :columns="loginOptionsSelect" @change="changeLogin" :placeholder="t('placeholder.choose')" />
  9. </template>
  10. <template #bottom>
  11. <view class="form-section">
  12. <view class="form-item">
  13. <cwg-input :label="t('global.t6')" v-model:value="globalForm.receiver" fkey="receiver" type="select"
  14. :columns="receiverUserListSelect" @change="selectReceiver" :placeholder="t('global.t7')" />
  15. </view>
  16. <view class="form-item">
  17. <cwg-input :label="t('global.p1')" v-model:value="globalForm.payoutCurrency" fkey="payoutCurrency"
  18. type="select" :columns="currencyListSelect" @change="handlePayoutCurrencyChange"
  19. :disabled="!currencyList.length" :placeholder="t('global.p1')" />
  20. </view>
  21. <view class="form-item">
  22. <cwg-input :label="t('global.p2')" v-model:value="globalForm.payType" fkey="payType" type="select"
  23. :columns="transferTypeListSelect" @change="handlePayTypeChange"
  24. :disabled="!globalForm.payoutCurrency || !transferTypeList.length" :placeholder="t('global.p2')" />
  25. </view>
  26. <view class="form-item" v-if="payoutMethodListSelect.length">
  27. <cwg-input :label="t('global.p3')" v-model:value="globalForm.payMethod" fkey="payMethod" type="select"
  28. :columns="payoutMethodListSelect" @change="handlePayMethodChange"
  29. :disabled="!globalForm.payType || !payoutMethodList.length" :placeholder="t('global.p3')" />
  30. </view>
  31. </view>
  32. </template>
  33. <template #transferAmount v-if="globalCurrenciesField.length">
  34. <view class="form-section">
  35. <view class="form-item">
  36. <text class="form-label">
  37. {{ t("card.Form.f55") }}
  38. <text class="quota-tip" v-if="exchangeRateData && exchangeRateData.maxQuota">
  39. ({{ t("global.GlobalOrder.quoteTip") }} {{ exchangeRateData.minQuota }} - {{ exchangeRateData.maxQuota
  40. }} USD)
  41. </text>
  42. </text>
  43. <cwg-input v-model:value="globalForm.amount" fkey="amount" type="number"
  44. :placeholder="t('global.placeholder.p4')" @change="handleAmountChange" />
  45. <text class="form-error" v-if="form__error">{{ form__error }}</text>
  46. <text class="fee-text" v-if="feeNum">
  47. {{ `${t("global.GlobalOrder.fee")}:${feeNum}` }} USD
  48. </text>
  49. </view>
  50. <view class="form-item">
  51. <cwg-input
  52. :label="t('global.fieldName.' + 'transferAmount' + '.fieldTitle') + ' (' + globalForm.payoutCurrency + ')'"
  53. v-model:value="globalForm.transferAmount" fkey="transferAmount" :disabled="true"
  54. :placeholder="t('global.fieldName.' + 'transferAmount' + '.fieldDescription')" />
  55. </view>
  56. </view>
  57. </template>
  58. <template #tips>
  59. <view class="tips-section">
  60. <view class="tips">
  61. <text class="title">{{ t('global.Tips') }}</text>
  62. <text>{{ t('global.Tips1') }}</text>
  63. <text>{{ t('global.Tips2') }}</text>
  64. <text>{{ t('global.Tips3') }}</text>
  65. <text>{{ t('global.Tips4') }}</text>
  66. </view>
  67. </view>
  68. </template>
  69. <template #submit>
  70. <view class="submit-section cwg-button">
  71. <u-button type="primary" @click="saveDlobal" class="submit-btn prev-btn">
  72. <text v-if="$i18n.locale === 'es'">Enviar solicitud</text>
  73. <text v-else>{{ t('Btn.Submit') }}</text>
  74. </u-button>
  75. </view>
  76. </template>
  77. </DynamicForm>
  78. </view>
  79. </cwg-page-wrapper>
  80. </template>
  81. <script setup lang="ts">
  82. import { ref, onMounted, watch, computed, nextTick } from "vue";
  83. import _ from "lodash";
  84. import DynamicForm from "./components/DynamicForm.vue";
  85. import { showToast } from "@/utils/toast";
  86. import { onLoad } from '@dcloudio/uni-app'
  87. import useRouter from "@/hooks/useRouter";
  88. import { useI18n } from "vue-i18n";
  89. import { ucardApi } from "@/api/ucard";
  90. import useCardStore from "@/stores/use-card-store";
  91. const cardStore = useCardStore();
  92. import useUserStore from "@/stores/use-user-store";
  93. const userStore = useUserStore();
  94. const { t } = useI18n();
  95. const router = useRouter();
  96. const currency = ref()
  97. onLoad((options) => {
  98. currency.value = options.currency
  99. })
  100. // Reactive variables
  101. const globalForm = ref({
  102. transferAmount: null,
  103. payoutCurrency: undefined,
  104. payType: undefined,
  105. payMethod: undefined,
  106. deductionAccount: '',
  107. amount: undefined,
  108. receiver: undefined,
  109. // Other fields go here
  110. });
  111. const businessForm = ref({});
  112. const feeNum = ref(0);
  113. const exchangeRateData = ref({});
  114. const form__error = ref('');
  115. const loginOptions = ref([]);
  116. const globalCurrenciesDropdown = ref([]);
  117. const receiverUserList = ref([]);
  118. const step2 = ref(false);
  119. const currencyList = ref([]);
  120. const globalCurrenciesField = ref([]);
  121. const globalFormRef = ref();
  122. // Computed properties for select lists
  123. const loginOptionsSelect = computed(() => {
  124. return loginOptions.value
  125. });
  126. const receiverUserListSelect = computed(() => {
  127. return receiverUserList.value.map((item: any) => ({
  128. text: `${item.receiverFirstName} ${item.receiverLastName} / ${item.receiverBankAccountNumber}`,
  129. value: item.id
  130. }));
  131. });
  132. const currencyListSelect = computed(() => {
  133. return currencyList.value.map((item: any) => ({
  134. text: item.payoutCurrency,
  135. value: item.payoutCurrency
  136. }));
  137. });
  138. const transferTypeListSelect = computed(() => {
  139. return transferTypeList.value.map((item: any) => ({
  140. text: item.transferType,
  141. value: item.transferTypeId
  142. }));
  143. });
  144. const payoutMethodListSelect = computed(() => {
  145. return payoutMethodList.value.map((item: any) => ({
  146. text: item.payoutMethodValue,
  147. value: item.payoutMethodId
  148. }));
  149. });
  150. const transferTypeList = computed(() => {
  151. return currencyList.value.find((item) => item.payoutCurrency === globalForm.value.payoutCurrency)?.types || [];
  152. });
  153. const payoutMethodList = computed(() => {
  154. return transferTypeList.value.find((item) => item.transferTypeId === globalForm.value.payType)?.methods || [];
  155. });
  156. const currentBalance = computed(() => {
  157. if (!globalForm.value.deductionAccount || !loginOptions.value.length) return 0;
  158. const target = loginOptions.value.find((item) => item.value === globalForm.value.deductionAccount);
  159. return target ? Number(target.balance) || 0 : 0;
  160. });
  161. const amountDisabled = computed(() => {
  162. const { payoutCurrency, payType, payMethod } = globalForm.value;
  163. return !payoutCurrency || !payType || !payMethod;
  164. });
  165. // Methods
  166. const changeLogin = (value: any) => {
  167. globalForm.value.deductionAccount = value.value;
  168. if (value.value) {
  169. step2.value = true;
  170. }
  171. };
  172. const handlePayoutCurrencyChange = (value: any) => {
  173. globalForm.value.payoutCurrency = value.value;
  174. selectOption1(1);
  175. };
  176. const handlePayTypeChange = (value: any) => {
  177. globalForm.value.payType = value.value;
  178. selectOption1(2);
  179. };
  180. const handlePayMethodChange = (value: any) => {
  181. globalForm.value.payMethod = value.value;
  182. selectOption1(3);
  183. };
  184. const handleAmountChange = (value: any) => {
  185. console.log(value, 'value');
  186. globalForm.value.amount = value.value;
  187. globalExchangeRate();
  188. };
  189. async function globalExchangeRate() {
  190. feeNum.value = 0
  191. const amount = Number(globalForm.value.amount)
  192. console.log(amount, globalForm.value.amount);
  193. // 校验金额是否填写
  194. if (!amount) {
  195. const msg = t("global.validator.v15")
  196. // pigeon.MessageError(msg)
  197. form__error.value = msg
  198. return
  199. }
  200. const { usedQuota, yearTransferAmountQuota, maxQuota, minQuota } = exchangeRateData.value
  201. // 金额格式校验
  202. if (!/^(0|([1-9][0-9]*))(\.[\d]{1,2})?$/.test(amount)) {
  203. const msg = t("global.validator.v15")
  204. // pigeon.MessageError(msg)
  205. form__error.value = msg
  206. return
  207. }
  208. // 最小 / 最大额度校验
  209. if (amount < minQuota || amount > maxQuota) {
  210. const msg = t("global.validator.v14", { minQuota, maxQuota })
  211. // pigeon.MessageError(msg)
  212. form__error.value = msg
  213. return
  214. }
  215. // 余额校验
  216. if (currentBalance.value && amount > currentBalance.value) {
  217. const msg = t("global.validator.balanceTip", {
  218. balance: currentBalance.value,
  219. })
  220. // pigeon.MessageError(msg)
  221. form__error.value = msg
  222. return
  223. }
  224. // 年度额度校验
  225. if (yearTransferAmountQuota) {
  226. const add = _.add(usedQuota, amount)
  227. if (add > yearTransferAmountQuota) {
  228. const msg = t("global.GlobalOrder.rateTip")
  229. // pigeon.MessageError(msg)
  230. form__error.value = msg
  231. return
  232. }
  233. }
  234. form__error.value = ''
  235. // 调接口
  236. const res = await ucardApi.globalExchangeRate({
  237. amount,
  238. uniqueId: businessForm.value.uniqueId,
  239. country: businessForm.value.fieldData.country,
  240. payoutCurrency: businessForm.value.fieldData.payoutCurrency,
  241. transferTypeId: businessForm.value.fieldData.transferTypeId,
  242. payoutMethodId: businessForm.value.fieldData.payoutMethodId,
  243. })
  244. console.log(res, 121212121);
  245. if (res.code === 200) {
  246. businessForm.value.exchangeRate = res.data
  247. form__error.value = ''
  248. // 手续费 (deductionFee ÷ exchangeRate)
  249. feeNum.value = divideNum(
  250. res.data.deductionFee,
  251. businessForm.value.fieldData.exchangeRate
  252. )
  253. // 回显转账金额
  254. globalForm.value.transferAmount = res.data.transferAmount
  255. console.log(globalForm.value, 121212);
  256. } else {
  257. // pigeon.MessageError(res.msg)
  258. }
  259. }
  260. const divideNum = (quote, fee) => {
  261. return _.floor(_.divide(quote, fee), 2);
  262. };
  263. const backActivity = () => {
  264. // router.push({ path: "/card/index" });
  265. };
  266. const selectOption1 = async (type: number) => {
  267. if (type === 1 || type === 2) {
  268. const list1 = transferTypeList.value;
  269. if (list1.length === 1) {
  270. globalForm.value.payType = list1[0].transferTypeId;
  271. } else {
  272. globalForm.value.payType = undefined;
  273. }
  274. const list2 = list1[0]?.methods || [];
  275. if (list2.length === 1) {
  276. globalForm.value.payMethod = list2[0].payoutMethodId;
  277. } else {
  278. globalForm.value.payMethod = undefined;
  279. }
  280. }
  281. globalForm.value.amount = undefined;
  282. feeNum.value = undefined;
  283. fetchExchangeRateDebounced();
  284. };
  285. // 防抖函数,300ms 内多次触发只执行最后一次
  286. const fetchExchangeRateDebounced = _.debounce(async () => {
  287. const { payoutCurrency, payType, payMethod } = globalForm.value;
  288. if (!payoutCurrency || !payType || !payMethod) return;
  289. const row = globalCurrenciesDropdown.value.filter(
  290. (currency: any) =>
  291. currency.payoutCurrency === payoutCurrency &&
  292. currency.transferTypeId === payType &&
  293. currency.payoutMethodId === payMethod
  294. );
  295. if (!row[0]) return;
  296. businessForm.value = { ...businessForm.value, fieldData: row[0] };
  297. getGlobalCurrenciesField(row[0]);
  298. try {
  299. const res = await ucardApi.globalExchangeRate({
  300. uniqueId: businessForm.value.uniqueId,
  301. country: row[0].country,
  302. payoutCurrency: row[0].payoutCurrency,
  303. transferTypeId: row[0].transferTypeId,
  304. payoutMethodId: row[0].payoutMethodId,
  305. });
  306. if (res.code === 200) {
  307. exchangeRateData.value = res.data;
  308. }
  309. } catch (error) {
  310. console.error("Error fetching exchange rate:", error);
  311. }
  312. }, 500); // 300ms 可根据需要调整
  313. const getGlobalCurrenciesField = async (row: any) => {
  314. try {
  315. const res = await ucardApi.globalCurrenciesField(row);
  316. if (res.code === 200) {
  317. setData(res.data || []);
  318. }
  319. } catch (error) {
  320. console.error("Error fetching currencies field:", error);
  321. }
  322. };
  323. const getSenderData = () => {
  324. const { idType, idNumber } = businessForm.value;
  325. const senderIdType =
  326. idType === "PASSPORT"
  327. ? "1"
  328. : idType === "GOVERNMENT_ISSUED_ID_CARD"
  329. ? "2"
  330. : "";
  331. const senderIdNumber =
  332. idType === "PASSPORT"
  333. ? idNumber
  334. : idType === "GOVERNMENT_ISSUED_ID_CARD"
  335. ? idNumber
  336. : "";
  337. return {
  338. senderFirstName: businessForm.value.firstName,
  339. senderLastName: businessForm.value.lastName,
  340. senderGender: businessForm.value.gender,
  341. senderIdType,
  342. senderIdNumber,
  343. senderNationality: businessForm.value.nationality,
  344. senderIdIssueCountry: businessForm.value.senderIdIssueCountry,
  345. senderDateOfBirth: businessForm.value.birthday,
  346. senderCountry: businessForm.value.country,
  347. senderState: businessForm.value.senderState,
  348. senderRegion: businessForm.value.senderRegion,
  349. senderCity: businessForm.value.townEnName,
  350. senderAddress: businessForm.value.address,
  351. senderZipCode: businessForm.value.postCode,
  352. senderMobileAreaCode: businessForm.value.areaCode,
  353. senderMobileNumber: businessForm.value.mobile,
  354. senderEmail: businessForm.value.email,
  355. senderOccupation: businessForm.value.occupation,
  356. transferType: businessForm.value.fieldData.transferTypeId,
  357. PayoutMethod: businessForm.value.fieldData.payoutMethodId,
  358. };
  359. };
  360. // Function to merge dynamic fields and sender data
  361. const setData = async (data: any) => {
  362. const senderData = getSenderData();
  363. const mergedFields = await Promise.all(
  364. data.map(async (field: any) => {
  365. const { fieldName } = field;
  366. const key = Object.keys(senderData).find(
  367. (k) => k.toLowerCase() === field.fieldName.toLowerCase()
  368. );
  369. const fixedValue = key ? senderData[key] : field.fixedValue;
  370. if (field.fieldName === "receiverBankCity") {
  371. try {
  372. const res = await ucardApi.globalQueryBankCities({
  373. payoutCurrency: businessForm.value.fieldData.payoutCurrency,
  374. country: businessForm.value.fieldData.country,
  375. });
  376. if (res.code === 200 && Array.isArray(res.data)) {
  377. field.availableDtos = res.data.map((item: any) => ({
  378. value: item.bankCitiesValue,
  379. valueId: item.bankCitiesKey,
  380. }));
  381. }
  382. } catch (e) {
  383. console.error("Error loading receiverBankCity data:", e);
  384. }
  385. }
  386. // Handle select fields
  387. if (field.fieldType === "select") {
  388. let rValue = globalForm.value[fieldName];
  389. const label = field.availableDtos?.find(
  390. (item: any) => item.valueId === rValue
  391. )?.value ?? null;
  392. globalForm.value[fieldName + "Value"] = label ?? fixedValue;
  393. }
  394. return { ...field, fixedValue };
  395. })
  396. );
  397. globalCurrenciesField.value = mergedFields;
  398. };
  399. // 选择的收款人
  400. const selectReceiver = (value: any) => {
  401. globalForm.value.receiver = value.value;
  402. const data = receiverUserList.value.find((item: any) => item.id === value.value);
  403. if (data) {
  404. globalForm.value = { ...globalForm.value, ...data };
  405. }
  406. nextTick(() => {
  407. // Validation can be triggered here if needed
  408. });
  409. };
  410. // 获取币种列表
  411. const getGlobalCurrenciesDropdown = () => {
  412. const res = cardStore.currencyList;
  413. globalCurrenciesDropdown.value = res || [];
  414. const data = _.cloneDeep(res);
  415. const payoutCurrencies = [...new Set(data.map((item: any) => item.payoutCurrency))];
  416. const array = payoutCurrencies.map((item) => {
  417. let list = data.filter((item2: any) => item === item2.payoutCurrency);
  418. let transferTypes = [...new Set(list.map((item: any) => item.transferTypeValue))];
  419. const types = transferTypes.map((type) => {
  420. const cur = list.find((item2: any) => item2.transferTypeValue === type);
  421. let list2 = list
  422. .filter((item2: any) => item2.transferTypeValue === type)
  423. .map((item2: any) => ({
  424. payoutMethodId: item2.payoutMethodId,
  425. payoutMethodValue: item2.payoutMethodValue,
  426. }));
  427. return { transferType: type, transferTypeId: cur.transferTypeId, methods: list2 };
  428. });
  429. return { payoutCurrency: item, types };
  430. });
  431. currencyList.value = array;
  432. globalForm.value.payoutCurrency = currency.value
  433. };
  434. // 提交订单
  435. const saveDlobal = async () => {
  436. const isValid = await globalFormRef.value.validateForm();
  437. console.log(isValid);
  438. if (!isValid) {
  439. return;
  440. }
  441. try {
  442. if (!globalForm.value.deductionAccount) {
  443. showToast(t('placeholder.choose'));
  444. return;
  445. }
  446. const [cardNumber, deductionAccountType] = globalForm.value.deductionAccount.split(",");
  447. const otherData = {};
  448. globalCurrenciesField.value
  449. .map((item) => ({
  450. fieldName: item.fieldName,
  451. fieldType: item.fieldType,
  452. }))
  453. .forEach((item) => {
  454. otherData[item.fieldName] = globalForm.value[item.fieldName];
  455. if (item.fieldType === "select") {
  456. const key = item.fieldName + 'Value';
  457. otherData[key] = globalForm.value[key];
  458. }
  459. });
  460. const params = {
  461. ...otherData,
  462. cardNumber,
  463. amount: globalForm.value.amount,
  464. deductionAccountType,
  465. uniqueId: businessForm.value.uniqueId,
  466. ...businessForm.value.exchangeRate,
  467. };
  468. const res = await ucardApi.globalOrdersCreate({ ...params });
  469. if (res.code === 200) {
  470. showToast(res.msg);
  471. backActivity();
  472. } else {
  473. showToast(res.msg);
  474. }
  475. } catch (error: any) {
  476. console.error("Error submitting order:", error);
  477. showToast(error.message || t('common.error'));
  478. }
  479. };
  480. // 收款人list
  481. const globalReceiverUserList = async ({ uniqueId }: any) => {
  482. try {
  483. const res = await ucardApi.globalReceiverUserList({ uniqueId });
  484. if (res.code === 200) {
  485. receiverUserList.value = res.data;
  486. } else {
  487. showToast(res.msg);
  488. }
  489. } catch (error) {
  490. console.error("Error fetching receiver list:", error);
  491. showToast(t('common.error'));
  492. }
  493. };
  494. // 付款账户下拉
  495. const getAccountDropdown = async () => {
  496. try {
  497. const res = await ucardApi.cardAccountDropdown();
  498. if (res.code === 200) {
  499. loginOptions.value = res.data.map((item: any) => {
  500. if (item.type === "1") {
  501. item.text = `${t("global.GlobalOrder.cardNo")} - ${item.cardNumber} ${t("global.GlobalOrder.bal")}: ${item.balance}`;
  502. } else {
  503. item.text = `${t("global.GlobalOrder.bagBal")}: ${item.balance}`;
  504. }
  505. item.value = `${item.cardNumber},${item.type}`;
  506. item.disabled = item.balance === 0;
  507. return item;
  508. });
  509. } else {
  510. showToast(res.msg);
  511. }
  512. } catch (error) {
  513. console.error("Error fetching account dropdown:", error);
  514. showToast(t('common.error'));
  515. }
  516. };
  517. onMounted(async () => {
  518. businessForm.value = userStore.userInfo;
  519. await getAccountDropdown();
  520. getGlobalCurrenciesDropdown();
  521. globalReceiverUserList(businessForm.value);
  522. });
  523. </script>
  524. <style lang="scss" scoped>
  525. @import "@/uni.scss";
  526. .form-section {
  527. margin-bottom: px2rpx(20);
  528. }
  529. .section-title {
  530. font-size: px2rpx(16);
  531. font-weight: bold;
  532. margin-bottom: px2rpx(15);
  533. display: block;
  534. }
  535. .form-item {
  536. margin-bottom: px2rpx(15);
  537. }
  538. .form-label {
  539. font-size: var(--font-size-16);
  540. line-height: px2rpx(24);
  541. letter-spacing: px2rpx(1);
  542. color: #474747;
  543. margin-bottom: px2rpx(4);
  544. overflow: hidden;
  545. text-overflow: ellipsis;
  546. white-space: nowrap;
  547. display: flex;
  548. align-items: center;
  549. .required-mark {
  550. color: red;
  551. }
  552. }
  553. .quota-tip {
  554. font-size: px2rpx(12);
  555. color: #999;
  556. margin-left: px2rpx(5);
  557. }
  558. .form-error {
  559. color: #ff0000;
  560. font-size: px2rpx(12);
  561. margin-top: px2rpx(5);
  562. display: block;
  563. }
  564. .fee-text {
  565. font-size: px2rpx(12);
  566. color: #666;
  567. margin-top: px2rpx(5);
  568. display: block;
  569. }
  570. .tips-section {
  571. margin: px2rpx(20) 0;
  572. }
  573. .tips {
  574. background-color: #f5f5f5;
  575. padding: px2rpx(15);
  576. border-radius: px2rpx(8);
  577. }
  578. .tips .title {
  579. font-weight: bold;
  580. margin-bottom: px2rpx(16);
  581. display: block;
  582. }
  583. .tips text {
  584. display: block;
  585. margin-bottom: px2rpx(10);
  586. font-size: px2rpx(13);
  587. color: #666;
  588. line-height: px2rpx(20);
  589. }
  590. .submit-section {
  591. margin: px2rpx(20) 0;
  592. }
  593. .submit-btn {
  594. width: 100%;
  595. }
  596. </style>