| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- <template>
- <div class="reply-container">
- <!-- 搜索表单 -->
- <div class="mb10">
- <SearchForm :searchFields="searchItems" :maxFirstRowItems="3" @search="handleSearch" @reset="handleReset" />
- </div>
- <!-- 操作按钮 -->
- <div class="mb10">
- <div class="table-operations">
- <a-button type="primary" danger :disabled="!hasSelected" @click="handleBatchDelete">
- 批量删除
- </a-button>
- <span style="margin-left: 8px" v-if="hasSelected">
- 已选择 {{ selectedRowKeys.length }} 项
- </span>
- </div>
- </div>
- <!-- 数据列表 -->
- <div class="card-wrap">
- <DataList ref="dataListRef" :api="(params) => commentApi.getListByPage(params)" :deleteApi="commentApi.deleteComment"
- :columns="columns" :rowSelection="rowSelection" @action="onAction">
- <template #content="{ record }">
- <div class="content-cell">
- {{ record.content.length > 50 ? record.content.substring(0, 50) + '...' : record.content }}
- </div>
- </template>
- <template #user="{ record }">
- <div v-if="record.user" class="flex items-center space-x-2" style="display: flex;align-items: center;">
- <a-avatar :size="32" :src="record.user.avatar ? getImageUrl(record.user.avatar) : undefined">
- {{ record.user.nickname ? record.user.nickname.charAt(0) : (record.user.username ?
- record.user.username.charAt(0) : 'U') }}
- </a-avatar>
- <div style="margin-left: 5px;">{{ record.user.nickname || record.user.username }}</div>
- </div>
- <div v-else class="text-gray-400">用户已删除</div>
- </template>
- <template #post="{ record }">
- <div v-if="record.post">
- <div class="max-w-xs truncate" :title="record.post.content">{{ record.post.content || '无内容' }}
- </div>
- </div>
- <div v-else class="text-gray-400">帖子已删除</div>
- </template>
- <template #status="{ record }">
- <a-tag :color="record.status === 1 ? 'green' : 'red'">
- {{ record.status === 1 ? '正常' : '禁用' }}
- </a-tag>
- </template>
- <template #created_at="{ record }">
- {{ formatDate(record.created_at * 1000) }}
- </template>
- </DataList>
- </div>
- <!-- 评论详情弹窗 -->
- <a-modal v-model:open="detailModalVisible" title="评论详情" :footer="null" width="600px">
- <div v-if="currentComment">
- <a-descriptions :column="1" bordered>
- <a-descriptions-item label="评论内容">
- <div class="whitespace-pre-wrap">{{ currentComment.content }}</div>
- </a-descriptions-item>
- <a-descriptions-item label="评论用户">
- <div v-if="currentComment.user">
- {{ currentComment.user.nickname || currentComment.user.username }}
- <span class="text-gray-500">(ID: {{ currentComment.user.id }})</span>
- </div>
- <span v-else class="text-gray-400">用户已删除</span>
- </a-descriptions-item>
- <a-descriptions-item label="所属帖子">
- <div v-if="currentComment.post">
- <div class="whitespace-pre-wrap">{{ currentComment.post.content || '无内容' }}</div>
- <span class="text-gray-500">(ID: {{ currentComment.post.id }})</span>
- </div>
- <span v-else class="text-gray-400">帖子已删除</span>
- </a-descriptions-item>
- <a-descriptions-item label="回复用户" v-if="currentComment.reply_user">
- <div v-if="currentComment.replyUser">
- {{ currentComment.replyUser.nickname || currentComment.replyUser.username }}
- <span class="text-gray-500">(ID: {{ currentComment.replyUser.id }})</span>
- </div>
- <span v-else class="text-gray-400">用户已删除</span>
- </a-descriptions-item>
- <a-descriptions-item label="类型">
- <a-tag :color="currentComment.type === 1 ? 'blue' : 'orange'">
- {{ currentComment.type === 1 ? '评论' : '回复' }}
- </a-tag>
- </a-descriptions-item>
- <a-descriptions-item label="状态">
- <a-tag :color="currentComment.status === 1 ? 'green' : 'red'">
- {{ currentComment.status === 1 ? '正常' : '禁用' }}
- </a-tag>
- </a-descriptions-item>
- <a-descriptions-item label="点赞数">{{ currentComment.thumb_num || 0 }}</a-descriptions-item>
- <a-descriptions-item label="创建时间">{{ currentComment.created_at }}</a-descriptions-item>
- <a-descriptions-item label="更新时间">{{ currentComment.updated_at }}</a-descriptions-item>
- </a-descriptions>
- </div>
- </a-modal>
- <!-- 编辑评论弹窗 -->
- <a-modal v-model:open="editModalVisible" title="编辑评论" @ok="handleSave"
- @cancel="handleCancel" width="600px">
- <a-form :model="editForm" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
- <a-form-item label="评论内容" :rules="[{ required: true, message: '请输入评论内容' }]">
- <a-textarea v-model:value="editForm.content" :rows="4" placeholder="请输入评论内容" />
- </a-form-item>
- <a-form-item label="状态">
- <a-select v-model:value="editForm.status" placeholder="请选择状态">
- <a-select-option :value="1">正常</a-select-option>
- <a-select-option :value="0">禁用</a-select-option>
- </a-select>
- </a-form-item>
- </a-form>
- </a-modal>
- </div>
- </template>
- <script setup>
- import { notifyError } from '@/utils/request.js'
- import { ref, reactive, computed } from 'vue'
- import { message, Modal } from 'ant-design-vue'
- import DataList from '@/components/DataList.vue'
- import SearchForm from '@/components/SearchForm.vue'
- import * as commentApi from '@/api/comment'
- import { getImageUrl } from '@/utils/utils'
- import { formatDate } from '@/utils/date'
- // 响应式数据
- const dataListRef = ref()
- const selectedRowKeys = ref([])
- const detailModalVisible = ref(false)
- const editModalVisible = ref(false)
- const currentComment = ref(null)
- // 搜索表单配置
- const searchItems = ref([
- {
- label: '评论内容',
- prop: 'content',
- type: 'input',
- placeholder: '请输入评论内容'
- },
- {
- label: '状态',
- prop: 'status',
- type: 'select',
- placeholder: '请选择状态',
- options: [
- { label: '正常', value: 1 },
- { label: '禁用', value: 0 }
- ]
- },
- {
- label: '创建时间',
- prop: 'date_range',
- type: 'daterange',
- placeholder: ['开始日期', '结束日期']
- }
- ])
- // 编辑表单
- const editForm = reactive({
- id: null,
- content: '',
- status: 1
- })
- // 计算属性
- const hasSelected = computed(() => selectedRowKeys.value.length > 0)
- // 行选择配置
- const rowSelection = {
- selectedRowKeys: selectedRowKeys,
- onChange: (keys) => {
- selectedRowKeys.value = keys
- }
- }
- // 表格列配置
- const columns = [
- {
- title: '评论内容',
- dataIndex: 'content',
- width: 200,
- slot: true
- },
- {
- title: '用户',
- dataIndex: 'user',
- width: 150,
- slot: true
- },
- {
- title: '帖子',
- dataIndex: 'post',
- width: 200,
- slot: true
- },
- {
- title: '状态',
- dataIndex: 'status',
- width: 100,
- slot: true
- },
- {
- title: '点赞数',
- dataIndex: 'thumb_num',
- width: 80
- },
- {
- title: '创建时间',
- dataIndex: 'created_at',
- width: 180,
- slot: true,
- sorter: true
- },
- {
- title: '操作',
- key: 'action',
- width: 200,
- type: 'action',
- options: [
- { title: '查看', key: 'view' },
- { title: '编辑', key: 'edit' },
- { title: '删除', key: 'delete', type: 'delete' }
- ]
- }
- ]
- // 搜索处理
- const handleSearch = (searchParams) => {
- dataListRef.value.search(searchParams)
- }
- // 重置处理
- const handleReset = () => {
- dataListRef.value.reset()
- }
- // DataList操作处理
- const onAction = (action, record) => {
- if (action.key === 'view') {
- showDetailModal(record)
- } else if (action.key === 'edit') {
- showEditModal(record)
- }
- }
- // 显示详情弹窗
- const showDetailModal = (record) => {
- currentComment.value = record
- detailModalVisible.value = true
- }
- // 显示编辑弹窗
- const showEditModal = (record) => {
- Object.assign(editForm, {
- id: record.id,
- content: record.content,
- status: record.status
- })
- editModalVisible.value = true
- }
- // 保存
- const handleSave = async () => {
- try {
- await commentApi.updateComment(editForm)
- message.success('更新评论成功')
- editModalVisible.value = false
- dataListRef.value.refreshData()
- } catch (error) {
- notifyError(error, '更新评论失败')
- }
- }
- // 取消
- const handleCancel = () => {
- editModalVisible.value = false
- }
- // 切换状态
- const toggleStatus = async (record) => {
- try {
- const newStatus = record.status === 1 ? 0 : 1
- await commentApi.updateCommentStatus({ id: record.id, status: newStatus })
- message.success(newStatus === 1 ? '启用成功' : '禁用成功')
- dataListRef.value.refreshData()
- } catch (error) {
- notifyError(error, '状态更新失败')
- }
- }
- // 删除单个
- const handleDelete = (record) => {
- Modal.confirm({
- title: '确认删除',
- content: '确定要删除这条评论吗?',
- onOk: async () => {
- try {
- await commentApi.deleteComment({ id: record.id })
- message.success('删除成功')
- dataListRef.value.refreshData()
- } catch (error) {
- notifyError(error, '删除失败')
- }
- }
- })
- }
- // 批量删除
- const handleBatchDelete = () => {
- Modal.confirm({
- title: '确认批量删除',
- content: `确定要删除选中的 ${selectedRowKeys.value.length} 条评论吗?`,
- onOk: async () => {
- try {
- await commentApi.batchDeleteComments({ ids: selectedRowKeys.value })
- message.success('批量删除成功')
- selectedRowKeys.value = []
- dataListRef.value.refreshData()
- } catch (error) {
- notifyError(error, '批量删除失败')
- }
- }
- })
- }
- </script>
- <style scoped>
- .reply-container {
- padding: 20px;
- }
- .search-form {
- background: #fff;
- padding: 20px;
- margin-bottom: 20px;
- border-radius: 6px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- }
- .operation-buttons {
- margin-bottom: 20px;
- }
- .operation-buttons .ant-btn {
- margin-right: 8px;
- }
- .content-cell {
- max-width: 200px;
- word-break: break-all;
- }
- .text-gray-500 {
- color: #6b7280;
- }
- .text-gray-400 {
- color: #9ca3af;
- }
- .text-xs {
- font-size: 0.75rem;
- }
- .whitespace-pre-wrap {
- white-space: pre-wrap;
- }
- </style>
|