submit.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <div class="app-container">
  3. <!-- 支付信息 -->
  4. <el-card v-loading="loading">
  5. <el-descriptions title="支付信息" :column="3" border>
  6. <el-descriptions-item label="支付单号">{{ payOrder.id }}</el-descriptions-item>
  7. <el-descriptions-item label="商品标题">{{ payOrder.subject }}</el-descriptions-item>
  8. <el-descriptions-item label="商品内容">{{ payOrder.body }}</el-descriptions-item>
  9. <el-descriptions-item label="支付金额">¥{{ (payOrder.amount / 100.0).toFixed(2) }}</el-descriptions-item>
  10. <el-descriptions-item label="创建时间">{{ parseTime(payOrder.createTime) }}</el-descriptions-item>
  11. <el-descriptions-item label="过期时间">{{ parseTime(payOrder.expireTime) }}</el-descriptions-item>
  12. </el-descriptions>
  13. </el-card>
  14. <!-- 支付选择框 -->
  15. <el-card style="margin-top: 10px" v-loading="submitLoading" element-loading-text="提交支付中...">
  16. <!-- 支付宝 -->
  17. <el-descriptions title="选择支付宝支付">
  18. </el-descriptions>
  19. <div class="pay-channel-container">
  20. <div class="box" v-for="channel in aliPayChannels" :key="channel.code" @click="submit(channel.code)">
  21. <img :src="icons[channel.code]">
  22. <div class="title">{{ channel.name }}</div>
  23. </div>
  24. </div>
  25. <!-- 微信支付 -->
  26. <el-descriptions title="选择微信支付" style="margin-top: 20px;" />
  27. <div class="pay-channel-container">
  28. <div class="box" v-for="channel in wxPayChannels" :key="channel.code">
  29. <img :src="icons[channel.code]">
  30. <div class="title">{{ channel.name }}</div>
  31. </div>
  32. </div>
  33. <!-- 其它支付 -->
  34. <el-descriptions title="选择其它支付" style="margin-top: 20px;" />
  35. <div class="pay-channel-container">
  36. <div class="box" v-for="channel in otherPayChannels" :key="channel.code">
  37. <img :src="icons[channel.code]">
  38. <div class="title">{{ channel.name }}</div>
  39. </div>
  40. </div>
  41. </el-card>
  42. <!-- 支付二维码 -->
  43. <el-dialog :title="qrCode.title" :visible.sync="qrCode.visible" width="350px" append-to-body
  44. :close-on-press-escape="false">
  45. <qrcode-vue :value="qrCode.url" size="310" level="H" />
  46. </el-dialog>
  47. <!-- 阿里支付 -->
  48. <div ref="alipayWap" v-html="alipayHtml.value" />
  49. </div>
  50. </template>
  51. <script>
  52. import QrcodeVue from 'qrcode.vue'
  53. import { DICT_TYPE, getDictDatas } from "@/utils/dict";
  54. import { getOrder, submitOrder } from '@/api/pay/order';
  55. import { PayChannelEnum, PayOrderStatusEnum } from "@/utils/constants";
  56. export default {
  57. name: "PayOrderSubmit",
  58. components: {
  59. QrcodeVue,
  60. },
  61. data() {
  62. return {
  63. id: undefined, // 请假编号
  64. loading: false, // 支付信息的 loading
  65. payOrder: {}, // 支付信息
  66. aliPayChannels: [], // 阿里支付的渠道
  67. wxPayChannels: [], // 微信支付的渠道
  68. otherPayChannels: [], // 其它的支付渠道
  69. icons: {
  70. alipay_qr: require("@/assets/images/pay/icon/alipay_qr.svg"),
  71. alipay_app: require("@/assets/images/pay/icon/alipay_app.svg"),
  72. alipay_wap: require("@/assets/images/pay/icon/alipay_wap.svg"),
  73. alipay_pc: require("@/assets/images/pay/icon/alipay_pc.svg"),
  74. wx_app: require("@/assets/images/pay/icon/wx_app.svg"),
  75. wx_lite: require("@/assets/images/pay/icon/wx_lite.svg"),
  76. wx_pub: require("@/assets/images/pay/icon/wx_pub.svg"),
  77. mock: require("@/assets/images/pay/icon/mock.svg"),
  78. },
  79. submitLoading: false, // 提交支付的 loading
  80. qrCode: { // 支付二维码
  81. url: '',
  82. title: '',
  83. visible: false,
  84. },
  85. interval: undefined, // 定时任务,轮询是否完成支付
  86. alipayHtml: '' // 阿里支付的 HTML
  87. };
  88. },
  89. created() {
  90. this.id = this.$route.query.id;
  91. this.getDetail();
  92. this.initPayChannels();
  93. },
  94. methods: {
  95. /** 初始化支付渠道 */
  96. initPayChannels() {
  97. // 微信支付
  98. for (const dict of getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)) {
  99. const payChannel = {
  100. name: dict.label,
  101. code: dict.value
  102. }
  103. if (dict.value.indexOf('wx_') === 0) {
  104. this.wxPayChannels.push(payChannel);
  105. } else if (dict.value.indexOf('alipay_') === 0) {
  106. this.aliPayChannels.push(payChannel);
  107. } else {
  108. this.otherPayChannels.push(payChannel);
  109. }
  110. }
  111. },
  112. /** 获得支付信息 */
  113. getDetail() {
  114. // 1.1 未传递订单编号
  115. if (!this.id) {
  116. this.$message.error('未传递支付单号,无法查看对应的支付信息');
  117. this.goBackToList();
  118. return;
  119. }
  120. getOrder(this.id).then(response => {
  121. // 1.2 无法查询到支付信息
  122. if (!response.data) {
  123. this.$message.error('支付订单不存在,请检查!');
  124. this.goBackToList();
  125. return;
  126. }
  127. // 1.3 订单已支付
  128. if (response.data.status !== PayOrderStatusEnum.WAITING.status) {
  129. this.$message.error('支付订单不处于待支付状态,请检查!');
  130. this.goBackToList();
  131. return;
  132. }
  133. // 2. 可以展示
  134. this.payOrder = response.data;
  135. });
  136. },
  137. /** 提交支付 */
  138. submit(channelCode) {
  139. this.submitLoading = true
  140. submitOrder({
  141. id: this.id,
  142. channelCode: channelCode
  143. }).then(response => {
  144. const invokeResponse = response.data.invokeResponse
  145. // 不同的支付,调用不同的策略
  146. if (channelCode === PayChannelEnum.ALIPAY_QR.code) {
  147. this.submitAfterAlipayQr(invokeResponse)
  148. } else if (channelCode === PayChannelEnum.ALIPAY_PC.code
  149. || channelCode === PayChannelEnum.ALIPAY_WAP.code) {
  150. this.submitAfterAlipayPc(invokeResponse)
  151. }
  152. // 打开轮询任务
  153. this.createQueryInterval()
  154. })
  155. },
  156. /** 提交支付后(支付宝扫码支付) */
  157. submitAfterAlipayQr(invokeResponse) {
  158. this.qrCode = {
  159. title: '请使用支付宝“扫一扫”扫码支付',
  160. url: invokeResponse.qrCode,
  161. visible: true
  162. }
  163. this.submitLoading = false
  164. },
  165. /** 提交支付后(支付宝 PC 网站支付) */
  166. submitAfterAlipayPc(invokeResponse) {
  167. // 渲染支付页面
  168. this.alipayHtml = {
  169. value: invokeResponse.body,
  170. visible: true
  171. }
  172. // 防抖避免重复支付
  173. this.$nextTick(() => {
  174. // 提交支付表单
  175. // this.$refs.alipayWap.children[0].submit();
  176. // setTimeout(() => {
  177. // this.submitLoading = false
  178. // }, 1000);
  179. });
  180. },
  181. /** 轮询查询任务 */
  182. createQueryInterval() {
  183. this.interval = setInterval(() => {
  184. getOrder(this.id).then(response => {
  185. // 已支付
  186. if (response.data.status === PayOrderStatusEnum.SUCCESS.status) {
  187. this.clearQueryInterval();
  188. this.$message.success('支付成功!');
  189. this.goBackToList();
  190. }
  191. // 已取消
  192. if (response.data.status === PayOrderStatusEnum.CLOSED.status) {
  193. this.clearQueryInterval();
  194. this.$message.error('支付已关闭!');
  195. this.goBackToList();
  196. }
  197. })
  198. }, 1000 * 2)
  199. },
  200. /** 清空查询任务 */
  201. clearQueryInterval() {
  202. // 清空各种弹窗
  203. this.qrCode = {
  204. title: '',
  205. url: '',
  206. visible: false
  207. }
  208. // 清空任务
  209. clearInterval(this.interval)
  210. this.interval = undefined
  211. },
  212. /** 回到列表 **/
  213. goBackToList() {
  214. this.$tab.closePage();
  215. this.$router.go(-1);
  216. }
  217. }
  218. };
  219. </script>
  220. <style lang="scss" scoped>
  221. .pay-channel-container {
  222. display: flex;
  223. margin-top: -10px;
  224. .box {
  225. width: 130px;
  226. border: 1px solid #e6ebf5;
  227. cursor: pointer;
  228. text-align: center;
  229. padding-top: 10px;
  230. padding-bottom: 5px;
  231. margin-right: 10px;
  232. img {
  233. width: 40px;
  234. height: 40px;
  235. }
  236. .title {
  237. padding-top: 5px
  238. }
  239. }
  240. }
  241. </style>