CommentReview.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <template>
  2. <div class="comment-review admin-page">
  3. <div class="header">
  4. <h2>评论审核</h2>
  5. <div class="filters">
  6. <el-select v-model="listQuery.status" @change="loadList" placeholder="审核状态" style="width:140px" clearable>
  7. <el-option label="全部" value="" />
  8. <el-option label="待审核" value="pending" />
  9. <el-option label="已通过" value="approved" />
  10. <el-option label="已驳回" value="rejected" />
  11. </el-select>
  12. <el-input v-model="listQuery.articleId" placeholder="文章ID" style="width:160px" clearable @keyup.enter.native="loadList" />
  13. <el-button type="primary" @click="loadList">查询</el-button>
  14. </div>
  15. </div>
  16. <el-table :data="list" v-loading="loading" border stripe>
  17. <el-table-column prop="id" label="ID" width="60" />
  18. <el-table-column label="用户" width="120">
  19. <template slot-scope="{ row }">
  20. <span>{{ row.userName || '未知' }}</span>
  21. </template>
  22. </el-table-column>
  23. <el-table-column label="文章" min-width="200">
  24. <template slot-scope="{ row }">
  25. <span style="font-size:13px">{{ row.articleTitle || ('文章#' + row.articleId) }}</span>
  26. </template>
  27. </el-table-column>
  28. <el-table-column label="评论内容" min-width="300">
  29. <template slot-scope="{ row }">
  30. <span v-if="row.parentId" style="color:#999">[回复] </span>{{ row.content }}
  31. </template>
  32. </el-table-column>
  33. <el-table-column label="状态" width="100">
  34. <template slot-scope="{ row }">
  35. <el-tag :type="row.status === 'approved' ? 'success' : row.status === 'rejected' ? 'danger' : 'warning'">
  36. {{ { pending: '待审核', approved: '已通过', rejected: '已驳回' }[row.status] }}
  37. </el-tag>
  38. </template>
  39. </el-table-column>
  40. <el-table-column prop="createdAt" label="时间" width="160" />
  41. <el-table-column label="操作" width="200" fixed="right">
  42. <template slot-scope="{ row }">
  43. <el-button size="mini" type="success" @click="audit(row.id, 'approved')" :disabled="row.status !== 'pending'">通过</el-button>
  44. <el-button size="mini" type="danger" @click="handleReject(row)" :disabled="row.status !== 'pending'">驳回</el-button>
  45. </template>
  46. </el-table-column>
  47. </el-table>
  48. <el-pagination
  49. v-if="total > 0"
  50. @current-change="handlePageChange"
  51. :current-page="listQuery.page"
  52. :page-size="listQuery.size"
  53. layout="total, prev, pager, next"
  54. :total="total"
  55. style="margin-top:20px;text-align:right"
  56. />
  57. <el-dialog title="驳回原因" :visible.sync="rejectDialogVisible" width="400px">
  58. <el-input type="textarea" v-model="rejectReason" :rows="3" placeholder="请输入驳回原因" />
  59. <span slot="footer">
  60. <el-button @click="rejectDialogVisible = false">取消</el-button>
  61. <el-button type="danger" @click="confirmReject">确认驳回</el-button>
  62. </span>
  63. </el-dialog>
  64. </div>
  65. </template>
  66. <script>
  67. import { adminCommentList } from '@/api/article'
  68. import request from '@/utils/request'
  69. export default {
  70. data() {
  71. return {
  72. list: [],
  73. loading: false,
  74. total: 0,
  75. listQuery: { status: 'pending', articleId: '', page: 1, size: 20 },
  76. rejectDialogVisible: false,
  77. rejectTargetId: null,
  78. rejectReason: ''
  79. }
  80. },
  81. mounted() { this.loadList() },
  82. methods: {
  83. async loadList() {
  84. this.loading = true
  85. try {
  86. var params = { page: this.listQuery.page, size: this.listQuery.size }
  87. if (this.listQuery.status) params.status = this.listQuery.status
  88. if (this.listQuery.articleId) params.articleId = parseInt(this.listQuery.articleId)
  89. var res = await adminCommentList(params)
  90. if (res.code === 200) {
  91. this.list = res.data.records || []
  92. this.total = res.data.total || 0
  93. } else {
  94. this.$message.error(res.message || '加载失败')
  95. }
  96. } catch (e) {
  97. this.$message.error('网络错误')
  98. } finally { this.loading = false }
  99. },
  100. handlePageChange(page) {
  101. this.listQuery.page = page
  102. this.loadList()
  103. },
  104. async audit(id, status) {
  105. var res = await request({ url: '/api/articles/comments/audit', method: 'post', data: { commentId: id, status: status } })
  106. if (res.code === 200) {
  107. this.$message.success(status === 'approved' ? '已通过' : '已驳回')
  108. this.loadList()
  109. } else {
  110. this.$message.error(res.message || '操作失败')
  111. }
  112. },
  113. handleReject(row) {
  114. this.rejectTargetId = row.id
  115. this.rejectReason = ''
  116. this.rejectDialogVisible = true
  117. },
  118. async confirmReject() {
  119. if (!this.rejectReason.trim()) {
  120. this.$message.warning('请填写驳回原因')
  121. return
  122. }
  123. this.rejectDialogVisible = false
  124. await this.audit(this.rejectTargetId, 'rejected')
  125. }
  126. }
  127. }
  128. </script>
  129. <style scoped>
  130. .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
  131. .filters { display: flex; gap: 10px; align-items: center; }
  132. </style>