detail.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <template>
  2. <view class="form-page">
  3. <view class="section-card" v-if="detail">
  4. <text class="section-title">团报详情</text>
  5. <view class="info-row">
  6. <text class="info-label">团报单号</text>
  7. <text class="info-value">{{ detail.groupNo }}</text>
  8. </view>
  9. <view class="info-row">
  10. <text class="info-label">班次</text>
  11. <text class="info-value">{{ detail.className || ('班次' + detail.classId) }}</text>
  12. </view>
  13. <view class="info-row">
  14. <text class="info-label">人数</text>
  15. <text class="info-value">{{ detail.memberCount }} 人</text>
  16. </view>
  17. <view class="info-row">
  18. <text class="info-label">单价</text>
  19. <text class="info-value">¥{{ fenToYuan(detail.unitAmount) }}</text>
  20. </view>
  21. <view class="info-row">
  22. <text class="info-label">总金额</text>
  23. <text class="info-value amount">¥{{ fenToYuan(detail.totalAmount) }}</text>
  24. </view>
  25. <view class="info-row" v-if="detail.invoiceTitle">
  26. <text class="info-label">发票抬头</text>
  27. <text class="info-value">{{ detail.invoiceTitle }}({{ detail.invoiceType }})</text>
  28. </view>
  29. <view class="info-row" v-if="detail.payTime">
  30. <text class="info-label">支付时间</text>
  31. <text class="info-value">{{ formatDateTime(detail.payTime) }}</text>
  32. </view>
  33. <view class="info-row">
  34. <text class="info-label">状态</text>
  35. <text class="info-value" :class="detail.status">{{ statusLabel(detail.status) }}</text>
  36. </view>
  37. <view v-if="detail.status === 'pending'" class="pay-area">
  38. <button class="pay-btn" :loading="paying" :disabled="paying" @click="handlePay">立即支付(¥{{ fenToYuan(detail.totalAmount) }})</button>
  39. <view class="pay-tip">测试环境将模拟支付成功,支付后为每位成员生成报名单</view>
  40. </view>
  41. <view v-else-if="detail.status === 'paid'" class="paid-tip">✓ 已支付,全部成员报名单已生成</view>
  42. </view>
  43. <view class="section-card" v-if="members">
  44. <text class="section-title">成员名单</text>
  45. <view class="member-item" v-for="m in members" :key="m.id">
  46. <view class="member-info">
  47. <text class="member-name">{{ m.name }}</text>
  48. <text class="member-phone">{{ m.phone }}</text>
  49. </view>
  50. <text class="member-status" :class="m.status">{{ m.status === 'paid' ? '已入账' : '待支付' }}</text>
  51. </view>
  52. </view>
  53. </view>
  54. </template>
  55. <script>
  56. import { getGroupOrderDetail, payGroupOrder } from '@/utils/api.js'
  57. import { fenToYuan, formatDateTime } from '@/utils/format.js'
  58. import { guardPage } from '@/utils/guard.js'
  59. export default {
  60. data() {
  61. return {
  62. id: '',
  63. detail: null,
  64. members: [],
  65. paying: false
  66. }
  67. },
  68. onLoad(options) {
  69. if (!guardPage()) return
  70. this.id = options && options.id ? options.id : ''
  71. if (!this.id) {
  72. uni.showToast({ title: '缺少团报单ID', icon: 'none' })
  73. setTimeout(function() {
  74. uni.navigateBack({ delta: 1 })
  75. }, 1500)
  76. return
  77. }
  78. this.loadDetail()
  79. },
  80. methods: {
  81. fenToYuan: fenToYuan,
  82. formatDateTime: formatDateTime,
  83. loadDetail() {
  84. var self = this
  85. getGroupOrderDetail({ id: self.id }).then(function(resp) {
  86. var data = resp.data || {}
  87. self.detail = data
  88. self.members = data.members || []
  89. }).catch(function(err) {
  90. uni.showToast({ title: (err && err.message) || '加载失败', icon: 'none' })
  91. })
  92. },
  93. statusLabel(status) {
  94. var map = { pending: '待支付', paid: '已支付', closed: '已关闭' }
  95. return map[status] || status || '未知'
  96. },
  97. handlePay() {
  98. var self = this
  99. if (self.paying) return
  100. self.paying = true
  101. uni.showLoading({ title: '团报支付中…' })
  102. payGroupOrder({ id: self.id }).then(function(resp) {
  103. var data = resp.data || {}
  104. uni.hideLoading()
  105. if (data.status === 'paid') {
  106. uni.showToast({ title: '支付成功', icon: 'success' })
  107. setTimeout(function() {
  108. self.loadDetail()
  109. }, 1000)
  110. } else {
  111. uni.showToast({ title: '订单已创建,请稍候查询', icon: 'none' })
  112. }
  113. }).catch(function(err) {
  114. uni.hideLoading()
  115. uni.showToast({ title: (err && err.message) || '支付失败', icon: 'none' })
  116. }).finally(function() {
  117. self.paying = false
  118. })
  119. }
  120. }
  121. }
  122. </script>
  123. <style scoped>
  124. .form-page { min-height: 100vh; background: #F5F5F5; padding: 32rpx; }
  125. .section-card { background: #FFF; border-radius: 16rpx; padding: 32rpx; margin-bottom: 24rpx; }
  126. .section-title { display: block; font-size: 32rpx; font-weight: 700; color: #2A2326; margin-bottom: 16rpx; border-left: 8rpx solid #FC7A57; padding-left: 16rpx; }
  127. .info-row { display: flex; align-items: center; justify-content: space-between; padding: 14rpx 0; border-bottom: 1rpx solid #F8FAFC; }
  128. .info-label { font-size: 26rpx; color: #5A4F4B; }
  129. .info-value { font-size: 28rpx; color: #2A2326; font-weight: 500; }
  130. .info-value.amount { color: #FC7A57; font-weight: 700; font-size: 32rpx; }
  131. .info-value.pending { color: #FC7A57; }
  132. .info-value.paid { color: #22C55E; }
  133. .info-value.closed { color: #94877F; }
  134. .pay-area { margin-top: 32rpx; }
  135. .pay-btn { width: 100%; height: 88rpx; line-height: 88rpx; background: #FC7A57; color: #FFF; font-size: 30rpx; font-weight: 600; border-radius: 44rpx; border: none; }
  136. .pay-btn:active { opacity: 0.85; }
  137. .pay-tip { font-size: 24rpx; color: #94877F; text-align: center; margin-top: 16rpx; }
  138. .paid-tip { font-size: 26rpx; color: #22C55E; text-align: center; padding: 16rpx 0; }
  139. .member-item { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0; border-bottom: 1rpx solid #F1F5F9; }
  140. .member-item:last-child { border-bottom: none; }
  141. .member-info { display: flex; align-items: center; }
  142. .member-name { font-size: 28rpx; color: #2A2326; font-weight: 500; margin-right: 24rpx; }
  143. .member-phone { font-size: 26rpx; color: #5A4F4B; }
  144. .member-status { font-size: 24rpx; font-weight: 600; }
  145. .member-status.paid { color: #22C55E; }
  146. .member-status.pending { color: #FC7A57; }
  147. </style>