order-pay.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <template>
  2. <view class="container">
  3. <view v-if="loading" class="loading-wrap">
  4. <text class="loading-text">加载中...</text>
  5. </view>
  6. <scroll-view v-else scroll-y class="content">
  7. <!-- Header Section -->
  8. <view class="header-section">
  9. <text class="header-title">订单待支付</text>
  10. <text class="header-countdown" v-if="countdown > 0">
  11. 支付剩余时间: {{ formatCountdown }}
  12. </text>
  13. <text class="header-cancelled" v-else>
  14. 订单已取消
  15. </text>
  16. </view>
  17. <!-- Order Info Card -->
  18. <view class="section">
  19. <view class="info-row">
  20. <text class="info-label">订单编号</text>
  21. <text class="info-value">{{ orderNo }}</text>
  22. </view>
  23. <view class="info-row">
  24. <text class="info-label">订单金额</text>
  25. <text class="order-amount">¥{{ formatAmount(totalAmount) }}</text>
  26. </view>
  27. </view>
  28. <!-- Payment Method -->
  29. <view class="section">
  30. <text class="section-title">支付方式</text>
  31. <view class="payment-method-row">
  32. <view class="payment-method-left">
  33. <text class="payment-icon">💳</text>
  34. <text class="payment-name">微信支付</text>
  35. </view>
  36. <text class="payment-checked">✓</text>
  37. </view>
  38. </view>
  39. <!-- Pay Button -->
  40. <view class="pay-section">
  41. <button
  42. :class="['pay-btn', (paying || countdown <= 0) ? 'disabled' : '']"
  43. :disabled="paying || countdown <= 0"
  44. @click="onPay"
  45. >
  46. {{ paying ? '支付中...' : '微信支付 ¥' + formatAmount(totalAmount) }}
  47. </button>
  48. <text class="cancel-link" @click="onCancelPay">暂不支付</text>
  49. </view>
  50. </scroll-view>
  51. </view>
  52. </template>
  53. <script>
  54. import { payActivityOrder, handleActivityOrderSuccess } from '@/utils/api.js'
  55. export default {
  56. data() {
  57. return {
  58. orderNo: '',
  59. totalAmount: 0,
  60. activityId: null,
  61. countdown: 900,
  62. paying: false,
  63. loading: false,
  64. timer: null
  65. }
  66. },
  67. computed: {
  68. formatCountdown() {
  69. var min = Math.floor(this.countdown / 60)
  70. var sec = this.countdown % 60
  71. return String(min).padStart(2, '0') + ':' + String(sec).padStart(2, '0')
  72. }
  73. },
  74. onLoad(options) {
  75. if (options.orderNo) {
  76. this.orderNo = options.orderNo
  77. }
  78. if (options.totalAmount) {
  79. this.totalAmount = parseFloat(options.totalAmount)
  80. }
  81. if (options.activityId) {
  82. this.activityId = options.activityId
  83. }
  84. this.startCountdown()
  85. },
  86. onUnload() {
  87. if (this.timer) {
  88. clearInterval(this.timer)
  89. this.timer = null
  90. }
  91. },
  92. methods: {
  93. formatAmount: function(val) {
  94. var num = parseFloat(val) || 0
  95. return (num / 100).toFixed(2)
  96. },
  97. startCountdown() {
  98. var that = this
  99. this.timer = setInterval(function() {
  100. if (that.countdown > 0) {
  101. that.countdown = that.countdown - 1
  102. } else {
  103. if (that.timer) {
  104. clearInterval(that.timer)
  105. that.timer = null
  106. }
  107. }
  108. }, 1000)
  109. },
  110. onPay() {
  111. if (this.countdown <= 0) {
  112. uni.showToast({ title: '订单已取消,无法支付', icon: 'none' })
  113. return
  114. }
  115. this.paying = true
  116. var that = this
  117. payActivityOrder(this.orderNo).then(function(res) {
  118. that.paying = false
  119. if (res && res.code === 200 && res.data && res.data.wechatPayParams) {
  120. var params = res.data.wechatPayParams
  121. uni.requestPayment({
  122. timeStamp: params.timeStamp,
  123. nonceStr: params.nonceStr,
  124. package: params.package,
  125. signType: params.signType || 'RSA',
  126. paySign: params.paySign,
  127. success: function() {
  128. handleActivityOrderSuccess(that.orderNo).then(function() {
  129. uni.showToast({ title: '支付成功', icon: 'success' })
  130. setTimeout(function() {
  131. that.backToDetail(true)
  132. }, 1500)
  133. }).catch(function() {
  134. uni.showToast({ title: '支付成功', icon: 'success' })
  135. setTimeout(function() {
  136. that.backToDetail(true)
  137. }, 1500)
  138. })
  139. },
  140. fail: function() {
  141. uni.showToast({ title: '支付失败', icon: 'none' })
  142. }
  143. })
  144. } else {
  145. uni.showToast({ title: (res && res.message) || '获取支付参数失败', icon: 'none' })
  146. }
  147. }).catch(function() {
  148. that.paying = false
  149. uni.showToast({ title: '网络请求失败', icon: 'none' })
  150. })
  151. },
  152. onCancelPay() {
  153. this.backToDetail(false)
  154. },
  155. backToDetail(refresh) {
  156. if (this.activityId) {
  157. uni.redirectTo({
  158. url: '/pages/activity/activity-detail/activity-detail?id=' + this.activityId + (refresh ? '&paid=1' : '')
  159. })
  160. } else {
  161. uni.navigateBack()
  162. }
  163. }
  164. }
  165. }
  166. </script>
  167. <style scoped>
  168. .container {
  169. min-height: 100vh;
  170. background: #F5F7FA;
  171. }
  172. .loading-wrap {
  173. display: flex;
  174. align-items: center;
  175. justify-content: center;
  176. padding: 120rpx 40rpx;
  177. }
  178. .loading-text {
  179. font-size: 28rpx;
  180. color: #94A3B8;
  181. }
  182. .content {
  183. height: 100vh;
  184. box-sizing: border-box;
  185. }
  186. .header-section {
  187. background: #F97316;
  188. padding: 48rpx 32rpx;
  189. display: flex;
  190. flex-direction: column;
  191. align-items: center;
  192. }
  193. .header-title {
  194. font-size: 36rpx;
  195. font-weight: bold;
  196. color: #fff;
  197. }
  198. .header-countdown {
  199. margin-top: 12rpx;
  200. font-size: 26rpx;
  201. color: #FFEDD5;
  202. }
  203. .header-cancelled {
  204. margin-top: 12rpx;
  205. font-size: 26rpx;
  206. color: #FFEDD5;
  207. }
  208. .section {
  209. background: #fff;
  210. border-radius: 16rpx;
  211. margin: 24rpx;
  212. padding: 28rpx;
  213. }
  214. .section-title {
  215. display: block;
  216. font-size: 28rpx;
  217. font-weight: 600;
  218. color: #1E293B;
  219. margin-bottom: 20rpx;
  220. }
  221. .info-row {
  222. display: flex;
  223. flex-direction: row;
  224. justify-content: space-between;
  225. align-items: center;
  226. padding: 12rpx 0;
  227. }
  228. .info-label {
  229. font-size: 26rpx;
  230. color: #64748B;
  231. }
  232. .info-value {
  233. font-size: 26rpx;
  234. color: #1E293B;
  235. max-width: 400rpx;
  236. word-break: break-all;
  237. text-align: right;
  238. }
  239. .order-amount {
  240. font-size: 32rpx;
  241. font-weight: bold;
  242. color: #F97316;
  243. }
  244. .payment-method-row {
  245. display: flex;
  246. flex-direction: row;
  247. justify-content: space-between;
  248. align-items: center;
  249. padding: 12rpx 0;
  250. }
  251. .payment-method-left {
  252. display: flex;
  253. flex-direction: row;
  254. align-items: center;
  255. }
  256. .payment-icon {
  257. font-size: 32rpx;
  258. margin-right: 12rpx;
  259. }
  260. .payment-name {
  261. font-size: 28rpx;
  262. color: #1E293B;
  263. }
  264. .payment-checked {
  265. font-size: 28rpx;
  266. color: #10B981;
  267. }
  268. .pay-section {
  269. margin: 48rpx 24rpx;
  270. display: flex;
  271. flex-direction: column;
  272. align-items: center;
  273. }
  274. .pay-btn {
  275. width: 100%;
  276. height: 88rpx;
  277. border-radius: 44rpx;
  278. background: linear-gradient(135deg, #F97316, #FB923C);
  279. color: #fff;
  280. font-size: 30rpx;
  281. font-weight: 600;
  282. display: flex;
  283. align-items: center;
  284. justify-content: center;
  285. }
  286. .pay-btn.disabled {
  287. background: #CBD5E1;
  288. }
  289. .cancel-link {
  290. margin-top: 24rpx;
  291. font-size: 26rpx;
  292. color: #94A3B8;
  293. }
  294. </style>