ActivityRegistrationReview.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <div class="registration-review admin-page">
  3. <div class="header">
  4. <h2>活动报名审核</h2>
  5. <div class="filters">
  6. <el-select v-model="activityId" placeholder="选择活动" @change="loadList" style="width: 200px;" clearable>
  7. <el-option v-for="a in activityList" :key="a.id" :label="a.title" :value="a.id" />
  8. </el-select>
  9. <el-input v-model="keyword" placeholder="搜索报名人/手机号/用户ID..." prefix-icon="el-icon-search" style="width:220px" clearable @keyup.enter.native="loadList" @clear="loadList" />
  10. <el-select v-model="statusFilter" placeholder="状态筛选" @change="loadList" style="width: 130px;" clearable>
  11. <el-option label="全部状态" value="" />
  12. <el-option label="待审核" value="pending" />
  13. <el-option label="已通过" value="approved" />
  14. <el-option label="已拒绝" value="rejected" />
  15. </el-select>
  16. </div>
  17. </div>
  18. <div class="table-scroll-wrap-sm">
  19. <el-table :max-height="tableHeight" :data="filteredList" v-loading="loading" border stripe>
  20. <el-table-column prop="id" label="ID" width="60" />
  21. <el-table-column prop="registrantName" label="报名人" width="100" />
  22. <el-table-column prop="registrantPhone" label="手机号" width="120" />
  23. <el-table-column prop="status" label="状态" width="90">
  24. <template slot-scope="{ row }">
  25. <el-tag :type="row.status === 'approved' ? 'success' : row.status === 'rejected' ? 'danger' : 'warning'">
  26. {{ row.status === 'approved' ? '已通过' : row.status === 'rejected' ? '已拒绝' : '待审核' }}
  27. </el-tag>
  28. </template>
  29. </el-table-column>
  30. <el-table-column prop="registeredAt" label="报名时间" width="160">
  31. <template slot-scope="{ row }">
  32. {{ formatTime(row.registeredAt || row.createdAt) }}
  33. </template>
  34. </el-table-column>
  35. <el-table-column label="操作" width="200" fixed="right">
  36. <template slot-scope="{ row }">
  37. <el-button v-if="row.status === 'pending'" type="success" size="mini" @click="handleApprove(row)">通过</el-button>
  38. <el-button v-if="row.status === 'pending'" type="danger" size="mini" @click="showRejectDialog(row)">拒绝</el-button>
  39. <span v-else style="color: #999;">已处理</span>
  40. </template>
  41. </el-table-column>
  42. </el-table>
  43. </div>
  44. <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
  45. <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
  46. <span slot="footer">
  47. <el-button @click="rejectDialogVisible = false">取消</el-button>
  48. <el-button type="danger" @click="handleReject">确认拒绝</el-button>
  49. </span>
  50. </el-dialog>
  51. </div>
  52. </template>
  53. <script>
  54. import { getActivityList } from '@/api/activity'
  55. import { getRegistrationList, approveRegistration, rejectRegistration } from '@/api/activity'
  56. export default {
  57. name: 'ActivityRegistrationReview',
  58. data() {
  59. return {
  60. activityList: [],
  61. list: [],
  62. loading: false,
  63. keyword: '',
  64. activityId: null,
  65. statusFilter: '',
  66. rejectDialogVisible: false,
  67. rejectReason: '',
  68. currentRow: null
  69. }
  70. },
  71. computed: {
  72. tableHeight() {
  73. return window.innerHeight - 300
  74. },
  75. filteredList() {
  76. if (!this.keyword) return this.list
  77. var kw = this.keyword.toLowerCase()
  78. return this.list.filter(function(item) {
  79. return (item.userId && String(item.userId).includes(kw)) ||
  80. (item.childId && String(item.childId).includes(kw)) ||
  81. (item.registrantName && String(item.registrantName).toLowerCase().includes(kw)) ||
  82. (item.registrantPhone && String(item.registrantPhone).includes(kw))
  83. })
  84. }
  85. },
  86. mounted() {
  87. this.loadActivities()
  88. },
  89. methods: {
  90. formatTime(t) {
  91. if (!t) return '-'
  92. return t.replace ? t.replace('T', ' ').substring(0, 19) : t
  93. },
  94. async loadActivities() {
  95. try {
  96. const res = await getActivityList({ page: 1, size: 100 })
  97. if (res.code === 200) this.activityList = (res.data && res.data.records) || res.data || []
  98. } catch (e) { console.error(e) }
  99. },
  100. async loadList() {
  101. if (!this.activityId) return this.$message.warning('请先选择活动')
  102. this.loading = true
  103. try {
  104. const params = { activityId: this.activityId }
  105. if (this.statusFilter) params.status = this.statusFilter
  106. const res = await getRegistrationList(params)
  107. if (res.code === 200) this.list = res.data || []
  108. } catch (e) { console.error(e) }
  109. this.loading = false
  110. },
  111. async handleApprove(row) {
  112. try {
  113. const res = await approveRegistration({ registrationId: row.id })
  114. if (res.code === 200) {
  115. this.$message.success('已通过')
  116. this.loadList()
  117. } else {
  118. this.$message.error(res.message || '操作失败')
  119. }
  120. } catch (e) { this.$message.error('操作失败') }
  121. },
  122. showRejectDialog(row) {
  123. this.currentRow = row
  124. this.rejectReason = ''
  125. this.rejectDialogVisible = true
  126. },
  127. async handleReject() {
  128. try {
  129. const res = await rejectRegistration({ registrationId: this.currentRow.id, rejectReason: this.rejectReason })
  130. if (res.code === 200) {
  131. this.$message.success('已拒绝')
  132. this.rejectDialogVisible = false
  133. this.loadList()
  134. } else {
  135. this.$message.error(res.message || '操作失败')
  136. }
  137. } catch (e) { this.$message.error('操作失败') }
  138. }
  139. }
  140. }
  141. </script>
  142. <style scoped>
  143. .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
  144. .header h2 { margin: 0; }
  145. .filters { display: flex; align-items: center; gap: 12px; }
  146. </style>