DynamicList.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <template>
  2. <div class="dynamic-list-container">
  3. <h2 class="page-title">动态管理</h2>
  4. <el-card shadow="never" class="toolbar-card">
  5. <el-row :gutter="20">
  6. <el-col :span="8">
  7. <el-space>
  8. <el-button icon="Refresh" @click="loadList">刷新</el-button>
  9. </el-space>
  10. </el-col>
  11. <el-col :span="16">
  12. <el-space style="justify-content: flex-end; width: 100%;">
  13. <el-select v-model="filters.auditStatus" placeholder="审核状态" clearable style="width: 120px" @change="loadList">
  14. <el-option label="全部" :value="undefined" />
  15. <el-option label="待审核" :value="0" />
  16. <el-option label="已通过" :value="1" />
  17. <el-option label="未通过" :value="2" />
  18. </el-select>
  19. </el-space>
  20. </el-col>
  21. </el-row>
  22. </el-card>
  23. <el-card shadow="never" class="table-card">
  24. <el-table v-loading="loading" :data="list" stripe>
  25. <el-table-column type="index" label="序号" width="60" />
  26. <el-table-column label="封面" width="120">
  27. <template #default="{ row }">
  28. <el-image
  29. v-if="row.coverUrl"
  30. :src="formatCover(row.coverUrl)"
  31. :preview-src-list="[formatCover(row.coverUrl)]"
  32. fit="cover"
  33. style="width:80px;height:80px;border-radius:6px;"
  34. preview-teleported
  35. hide-on-click-modal
  36. />
  37. <div v-else class="cover-placeholder">无图</div>
  38. </template>
  39. </el-table-column>
  40. <el-table-column prop="userId" label="用户ID" width="80" />
  41. <el-table-column prop="userNickname" label="用户昵称" width="120" />
  42. <el-table-column prop="content" label="动态内容" min-width="300" show-overflow-tooltip />
  43. <el-table-column prop="likeCount" label="点赞数" width="80" />
  44. <el-table-column prop="commentCount" label="评论数" width="80" />
  45. <el-table-column label="举报" width="90">
  46. <template #default="{ row }">
  47. <el-tag v-if="row.isReported" type="danger" size="small">已举报</el-tag>
  48. <el-tag v-else type="info" size="small" effect="plain">正常</el-tag>
  49. </template>
  50. </el-table-column>
  51. <el-table-column prop="auditStatus" label="审核状态" width="100">
  52. <template #default="{ row }">
  53. <el-tag :type="getAuditStatusType(row.auditStatus)" size="small">
  54. {{ getAuditStatusText(row.auditStatus) }}
  55. </el-tag>
  56. </template>
  57. </el-table-column>
  58. <el-table-column prop="createTime" label="发布时间" width="180" />
  59. <el-table-column label="操作" width="280" fixed="right">
  60. <template #default="{ row }">
  61. <el-button type="primary" size="small" link @click="goDetail(row)">
  62. 查看
  63. </el-button>
  64. <el-button v-if="row.auditStatus === 0" type="success" size="small" link @click="handleAudit(row, 1)">
  65. 通过
  66. </el-button>
  67. <el-button v-if="row.auditStatus === 0" type="warning" size="small" link @click="handleAudit(row, 2)">
  68. 拒绝
  69. </el-button>
  70. <el-button v-if="row.isReported" type="warning" size="small" link @click="openReports(row)">
  71. 查看举报
  72. </el-button>
  73. <el-button type="danger" size="small" link @click="handleDelete(row)">删除</el-button>
  74. </template>
  75. </el-table-column>
  76. </el-table>
  77. <div class="pagination-container">
  78. <el-pagination
  79. v-model:current-page="currentPage"
  80. v-model:page-size="pageSize"
  81. :total="total"
  82. layout="total, sizes, prev, pager, next"
  83. @size-change="loadList"
  84. @current-change="loadList"
  85. />
  86. </div>
  87. </el-card>
  88. <!-- 举报记录弹窗 -->
  89. <el-dialog v-model="reportDialogVisible" title="举报记录" width="720px">
  90. <el-table :data="reports" size="small">
  91. <el-table-column prop="reportId" label="ID" width="80" />
  92. <el-table-column prop="reportType" label="类型" width="100" />
  93. <el-table-column prop="description" label="描述" min-width="260" />
  94. <el-table-column prop="status" label="状态" width="100">
  95. <template #default="{ row }">
  96. <el-tag :type="row.status===0?'danger':row.status===2?'success':'warning'">
  97. {{ row.status===0?'待处理':row.status===1?'处理中':row.status===2?'已处理':'已驳回' }}
  98. </el-tag>
  99. </template>
  100. </el-table-column>
  101. <el-table-column prop="createdAt" label="时间" width="170" />
  102. </el-table>
  103. </el-dialog>
  104. </div>
  105. </template>
  106. <script setup>
  107. import { ref, reactive, onMounted } from 'vue'
  108. import { useRouter } from 'vue-router'
  109. import { ElMessage, ElMessageBox } from 'element-plus'
  110. import request from '@/utils/request'
  111. import { API_ENDPOINTS } from '@/config/api'
  112. const router = useRouter()
  113. const loading = ref(false)
  114. const currentPage = ref(1)
  115. const pageSize = ref(10)
  116. const total = ref(0)
  117. const list = ref([])
  118. const filters = reactive({
  119. auditStatus: null
  120. })
  121. const loadList = async () => {
  122. loading.value = true
  123. try {
  124. const response = await request.get(API_ENDPOINTS.DYNAMIC_LIST, {
  125. params: { page: currentPage.value, pageSize: pageSize.value, ...filters }
  126. })
  127. if (response.code === 200) {
  128. list.value = response.data.list || response.data || []
  129. total.value = response.data.total || list.value.length
  130. }
  131. } catch (error) {
  132. console.error('加载失败:', error)
  133. ElMessage.error('加载失败')
  134. } finally {
  135. loading.value = false
  136. }
  137. }
  138. // 封面拼接(本地开发通过Vite代理走8083,生产留空)
  139. const BASE = import.meta.env.DEV ? 'http://localhost:8083' : ''
  140. const defaultCover = '/images/placeholder.png'
  141. const formatCover = (url) => {
  142. if (!url) return defaultCover
  143. return url.startsWith('http') ? url : `${BASE}${url}`
  144. }
  145. const getAuditStatusText = (status) => {
  146. const texts = { 0: '待审核', 1: '已通过', 2: '未通过' }
  147. return texts[status] || '未知'
  148. }
  149. const getAuditStatusType = (status) => {
  150. const types = { 0: 'warning', 1: 'success', 2: 'danger' }
  151. return types[status] || ''
  152. }
  153. const goDetail = (row) => {
  154. if (!row?.dynamicId) return
  155. router.push(`/dynamic/detail/${row.dynamicId}`)
  156. }
  157. const handleAudit = async (row, auditStatus) => {
  158. try {
  159. const response = await request.post(`${API_ENDPOINTS.DYNAMIC_AUDIT}/${row.dynamicId}`, { auditStatus })
  160. if (response.code === 200) {
  161. ElMessage.success('审核成功')
  162. loadList()
  163. }
  164. } catch (error) {
  165. console.error('审核失败:', error)
  166. }
  167. }
  168. const handleDelete = async (row) => {
  169. try {
  170. await ElMessageBox.confirm('确定要删除这条动态吗?', '提示', { type: 'warning' })
  171. const response = await request.delete(`${API_ENDPOINTS.DYNAMIC_DELETE}/${row.dynamicId}`)
  172. if (response.code === 200) {
  173. ElMessage.success('删除成功')
  174. loadList()
  175. }
  176. } catch (error) {
  177. if (error !== 'cancel') console.error('删除失败:', error)
  178. }
  179. }
  180. // 举报查看
  181. const reports = ref([])
  182. const reportDialogVisible = ref(false)
  183. const openReports = async (row) => {
  184. try {
  185. const res = await request.get(API_ENDPOINTS.REPORT_LIST, { params: { dynamicId: row.dynamicId, page: 1, pageSize: 50 } })
  186. if (res.code === 200) {
  187. reports.value = res.data.list || []
  188. reportDialogVisible.value = true
  189. }
  190. } catch (e) {
  191. console.error('获取举报列表失败', e)
  192. }
  193. }
  194. onMounted(() => loadList())
  195. </script>
  196. <style scoped>
  197. .dynamic-list-container { padding: 20px; }
  198. .page-title { font-size: 24px; font-weight: 600; color: #303133; margin: 0 0 20px 0; }
  199. .toolbar-card { margin-bottom: 20px; }
  200. .table-card { margin-top: 20px; }
  201. .pagination-container { display: flex; justify-content: flex-end; margin-top: 20px; }
  202. .cover-placeholder{ width:80px;height:80px;border-radius:6px;background:#f5f7fa;color:#909399;display:flex;align-items:center;justify-content:center;font-size:12px; }
  203. </style>