Pay.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <div class="wechat-pay">
  3. <div class="order">
  4. <div>
  5. <div>{{$t('订单编号:')}}{{ props.order.id }}</div>
  6. <div>{{$t('订单类型:')}}{{ props.order.goods.type }}</div>
  7. </div>
  8. <div class="flex items-center">
  9. {{$t('应付金额')}}:
  10. <span style="font-size: 20px; color: #f5222d">
  11. ¥{{ props.order.amount }}</span
  12. >
  13. </div>
  14. </div>
  15. <div class="code-panel">
  16. <div>
  17. <t-radio
  18. :checked="payType === 0"
  19. :disabled="user.amount < props.order.amount"
  20. @click="
  21. payType = 0;
  22. hideQrPay();
  23. ">
  24. <h5>{{$t('余额支付')}}</h5>
  25. </t-radio>
  26. <div class="pl-3 mt-3 ml-3">
  27. {{$t('当前余额')}}:<span class="bland">¥ {{ user.amount }} 元</span>
  28. </div>
  29. </div>
  30. <div class="mt-5">
  31. <t-radio
  32. :checked="payType === 1"
  33. @click="
  34. payType = 1;
  35. showQrPay();
  36. ">
  37. <h5>{{$t('扫码支付')}}</h5>
  38. </t-radio>
  39. <div class="flex pl-3 ml-3" v-show="payType === 1">
  40. <div class="code">
  41. <img class="qrcode" :src="payQRCode.wepay" />
  42. <div class="icon">
  43. <img src="/img/wepay.png" />
  44. </div>
  45. </div>
  46. <div class="code">
  47. <div class="qrcode">
  48. <iframe
  49. :src="payQRCode.alipay"
  50. frameborder="no"
  51. scrolling="no"
  52. allowtransparency="true"
  53. />
  54. </div>
  55. <div class="icon">
  56. <img src="/img/alipay.png" />
  57. </div>
  58. </div>
  59. </div>
  60. </div>
  61. <div class="mt-5 bland">【{{$t('注意')}}】{{$t('不支持退款')}}</div>
  62. </div>
  63. <div style="padding: 20px"></div>
  64. <div class="buttons">
  65. <t-button v-if="payType === 0" @click="payByAmount" :loading="payLoading">
  66. {{$t('确认支付')}}
  67. </t-button>
  68. <t-button v-if="payType === 1" @click="getPayResult"> {{$t('支付完成')}} </t-button>
  69. </div>
  70. </div>
  71. </template>
  72. <script lang="ts" setup>
  73. import { onBeforeMount, onUnmounted, ref } from 'vue';
  74. import axios from 'axios';
  75. import QRCode from 'qrcode';
  76. import { useUser } from '@/services/user';
  77. const props = defineProps<{
  78. order: any;
  79. alipayUrl: string;
  80. codeUrl: string;
  81. }>();
  82. const emit = defineEmits(['success']);
  83. const { user, getUser } = useUser();
  84. const payType = ref(0);
  85. const payQRCode = ref<any>({});
  86. const payLoading = ref(false);
  87. let timer: any;
  88. onBeforeMount(async () => {
  89. if (user.amount >= props.order.amount) {
  90. payType.value = 0;
  91. } else {
  92. payType.value = 1;
  93. }
  94. payQRCode.value.alipay = props.alipayUrl;
  95. payQRCode.value.wepay = await QRCode.toDataURL(props.codeUrl, {
  96. margin: 0,
  97. });
  98. if (payType.value === 1) {
  99. showQrPay();
  100. }
  101. });
  102. const hideQrPay = () => {
  103. clearInterval(timer);
  104. };
  105. const showQrPay = async () => {
  106. clearInterval(timer);
  107. timer = setInterval(async () => {
  108. const success = await getPayResult();
  109. if (success) {
  110. clearInterval(timer);
  111. }
  112. }, 5000);
  113. };
  114. onUnmounted(() => {
  115. clearInterval(timer);
  116. });
  117. const getPayResult = async () => {
  118. const result: { state: number } = await axios.post('/api/order/pay/state', {
  119. id: props.order.id || props.order._id,
  120. });
  121. if (result && result.state === 8) {
  122. emit('success', true);
  123. return true;
  124. }
  125. };
  126. const payByAmount = async () => {
  127. payLoading.value = true;
  128. const result: any = await axios.post('/api/order/pay/by/amount', {
  129. id: props.order.id,
  130. });
  131. payLoading.value = false;
  132. if (result) {
  133. emit('success', true);
  134. }
  135. };
  136. </script>
  137. <style lang="postcss" scoped>
  138. .wechat-pay {
  139. .order {
  140. padding: 10px 0;
  141. line-height: 30px;
  142. display: flex;
  143. justify-content: space-between;
  144. align-items: center;
  145. }
  146. /* .pay-type {
  147. background-color: #f7f8fa;
  148. }
  149. .wepay {
  150. color: var(--color-title);
  151. margin-top: 16px;
  152. }
  153. .wepay-qrcode {
  154. width: 150px;
  155. margin-top: 24px;
  156. }
  157. .wepay-text {
  158. padding: 10px 40px;
  159. line-height: 20px;
  160. color: #ffffff;
  161. font-size: 13px;
  162. } */
  163. .code-panel {
  164. padding: 30px 40px;
  165. color: #171b27;
  166. background: #f7f8fa;
  167. margin-top: 8px;
  168. .flex > div {
  169. flex: 1;
  170. .icon {
  171. width: 100px;
  172. text-align: center;
  173. & > img {
  174. height: 28px;
  175. margin: 8px auto;
  176. }
  177. }
  178. }
  179. .code {
  180. height: 150px;
  181. }
  182. }
  183. .qrcode {
  184. width: 100px;
  185. height: 100px;
  186. margin-top: 16px;
  187. }
  188. iframe {
  189. width: 160px;
  190. height: 160px;
  191. /* margin-top: 16px; */
  192. transform: scale(0.625);
  193. transform-origin: 0 0;
  194. }
  195. .buttons {
  196. position: absolute;
  197. bottom: 32px;
  198. right: 32px;
  199. }
  200. .pl-3{
  201. padding-left: 0.75rem;
  202. }
  203. .mt-3{
  204. margin-top: 0.75rem;
  205. }
  206. .ml-3{
  207. margin-left: 0.75rem;
  208. }
  209. .mt-5{
  210. margin-top: 1.25rem;
  211. }
  212. :deep(.t-radio__label){
  213. color: #171b27;
  214. }
  215. :deep(.t-radio__input){
  216. background-color: #fff0 !important;
  217. }
  218. }
  219. </style>