PaymentConfirmModal.jsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React from 'react';
  16. import { Modal, Typography, Card, Skeleton } from '@douyinfe/semi-ui';
  17. import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si';
  18. import { CreditCard } from 'lucide-react';
  19. const { Text } = Typography;
  20. const PaymentConfirmModal = ({
  21. t,
  22. open,
  23. onlineTopUp,
  24. handleCancel,
  25. confirmLoading,
  26. topUpCount,
  27. renderQuotaWithAmount,
  28. amountLoading,
  29. renderAmount,
  30. payWay,
  31. payMethods,
  32. // 新增:用于显示折扣明细
  33. amountNumber,
  34. discountRate,
  35. }) => {
  36. const hasDiscount = discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0;
  37. const originalAmount = hasDiscount ? (amountNumber / discountRate) : 0;
  38. const discountAmount = hasDiscount ? (originalAmount - amountNumber) : 0;
  39. return (
  40. <Modal
  41. title={
  42. <div className='flex items-center'>
  43. <CreditCard className='mr-2' size={18} />
  44. {t('充值确认')}
  45. </div>
  46. }
  47. visible={open}
  48. onOk={onlineTopUp}
  49. onCancel={handleCancel}
  50. maskClosable={false}
  51. size='small'
  52. centered
  53. confirmLoading={confirmLoading}
  54. >
  55. <div className='space-y-4'>
  56. <Card className='!rounded-xl !border-0 bg-slate-50 dark:bg-slate-800'>
  57. <div className='space-y-3'>
  58. <div className='flex justify-between items-center'>
  59. <Text strong className='text-slate-700 dark:text-slate-200'>
  60. {t('充值数量')}:
  61. </Text>
  62. <Text className='text-slate-900 dark:text-slate-100'>
  63. {renderQuotaWithAmount(topUpCount)}
  64. </Text>
  65. </div>
  66. <div className='flex justify-between items-center'>
  67. <Text strong className='text-slate-700 dark:text-slate-200'>
  68. {t('实付金额')}:
  69. </Text>
  70. {amountLoading ? (
  71. <Skeleton.Title style={{ width: '60px', height: '16px' }} />
  72. ) : (
  73. <div className='flex items-baseline space-x-2'>
  74. <Text strong className='font-bold' style={{ color: 'red' }}>
  75. {renderAmount()}
  76. </Text>
  77. {hasDiscount && (
  78. <Text size='small' className='text-rose-500'>
  79. {Math.round(discountRate * 100)}%
  80. </Text>
  81. )}
  82. </div>
  83. )}
  84. </div>
  85. {hasDiscount && !amountLoading && (
  86. <>
  87. <div className='flex justify-between items-center'>
  88. <Text className='text-slate-500 dark:text-slate-400'>
  89. {t('原价')}:
  90. </Text>
  91. <Text delete className='text-slate-500 dark:text-slate-400'>
  92. {`${originalAmount.toFixed(2)} ${t('元')}`}
  93. </Text>
  94. </div>
  95. <div className='flex justify-between items-center'>
  96. <Text className='text-slate-500 dark:text-slate-400'>
  97. {t('优惠')}:
  98. </Text>
  99. <Text className='text-emerald-600 dark:text-emerald-400'>
  100. {`- ${discountAmount.toFixed(2)} ${t('元')}`}
  101. </Text>
  102. </div>
  103. </>
  104. )}
  105. <div className='flex justify-between items-center'>
  106. <Text strong className='text-slate-700 dark:text-slate-200'>
  107. {t('支付方式')}:
  108. </Text>
  109. <div className='flex items-center'>
  110. {(() => {
  111. const payMethod = payMethods.find(
  112. (method) => method.type === payWay,
  113. );
  114. if (payMethod) {
  115. return (
  116. <>
  117. {payMethod.type === 'alipay' ? (
  118. <SiAlipay
  119. className='mr-2'
  120. size={16}
  121. color='#1677FF'
  122. />
  123. ) : payMethod.type === 'wxpay' ? (
  124. <SiWechat
  125. className='mr-2'
  126. size={16}
  127. color='#07C160'
  128. />
  129. ) : payMethod.type === 'stripe' ? (
  130. <SiStripe
  131. className='mr-2'
  132. size={16}
  133. color='#635BFF'
  134. />
  135. ) : (
  136. <CreditCard
  137. className='mr-2'
  138. size={16}
  139. color={
  140. payMethod.color || 'var(--semi-color-text-2)'
  141. }
  142. />
  143. )}
  144. <Text className='text-slate-900 dark:text-slate-100'>
  145. {payMethod.name}
  146. </Text>
  147. </>
  148. );
  149. } else {
  150. // 默认充值方式
  151. if (payWay === 'alipay') {
  152. return (
  153. <>
  154. <SiAlipay
  155. className='mr-2'
  156. size={16}
  157. color='#1677FF'
  158. />
  159. <Text className='text-slate-900 dark:text-slate-100'>
  160. {t('支付宝')}
  161. </Text>
  162. </>
  163. );
  164. } else if (payWay === 'stripe') {
  165. return (
  166. <>
  167. <SiStripe
  168. className='mr-2'
  169. size={16}
  170. color='#635BFF'
  171. />
  172. <Text className='text-slate-900 dark:text-slate-100'>
  173. Stripe
  174. </Text>
  175. </>
  176. );
  177. } else {
  178. return (
  179. <>
  180. <SiWechat
  181. className='mr-2'
  182. size={16}
  183. color='#07C160'
  184. />
  185. <Text className='text-slate-900 dark:text-slate-100'>
  186. {t('微信')}
  187. </Text>
  188. </>
  189. );
  190. }
  191. }
  192. })()}
  193. </div>
  194. </div>
  195. </div>
  196. </Card>
  197. </div>
  198. </Modal>
  199. );
  200. };
  201. export default PaymentConfirmModal;