fix:bug修改

This commit is contained in:
dzc committed 2026-07-30 20:20:38 +08:00
1 parent 0c92763537
commit 401c7c1dd6
5 files changed
+666 -94

No files matched your search

+2 -1
View File
@@ -8,7 +8,8 @@ VITE_DEV=true
VITE_BASE_URL='http://localhost:48083'
VITE_CHAT_URL='http://localhost:8080/#/home/chat'
# VITE_CHAT_URL='http://localhost:8080/#/home/chat'
VITE_CHAT_URL='http://service.htysapp.com/#/home/chat'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
VITE_UPLOAD_TYPE=server
+31 -2
View File
@@ -5,13 +5,32 @@ export interface RecentVO {
id: number // 收件地址编号
memberId: number // 用户编号
coverPic: string // 主图
describe: string // 描述
coverVideo: string // 视频
description: string // 描述
issueLong: number // 发布-经度
issueLat: number // 发布-纬度
issueProvince: string // 发布-省
issueCity: string // 发布-市
issueDistrict: string // 发布-区
issueAddress: string // 发布-详细地址
visible: boolean // 是否展示
auditStatus: number // 审核状态:0-审核中,1-审核通过,2-审核不通过
auditSource?: 'THIRD_PARTY' | 'MANUAL' | 'HISTORY' // 审核来源
auditReason?: string // 审核原因
auditTime?: string // 审核时间
auditUserId?: number // 人工审核用户编号
createTime?: string // 创建时间
}
export interface RecentAuditReqVO {
id: number
auditStatus: 1 | 2
auditReason?: string
}
export interface RecentVisibleReqVO {
id: number
visible: boolean
}
// 朋友圈 API
@@ -36,6 +55,16 @@ export const RecentApi = {
return await request.put({ url: `/member/recent/update`, data })
},
// 人工审核朋友圈
auditRecent: async (data: RecentAuditReqVO) => {
return await request.put({ url: `/member/recent/audit`, data })
},
// 更新朋友圈展示状态
updateRecentVisible: async (data: RecentVisibleReqVO) => {
return await request.put({ url: `/member/recent/visible`, data })
},
// 删除朋友圈
deleteRecent: async (id: number) => {
return await request.delete({ url: `/member/recent/delete?id=` + id })
@@ -45,4 +74,4 @@ export const RecentApi = {
exportRecent: async (params) => {
return await request.download({ url: `/member/recent/export-excel`, params })
}
}
}
+103 -43
View File
@@ -1,54 +1,101 @@
<template>
<el-card shadow="never" class="w-full h-full m-0!">
<template #header>
<slot name="header"></slot>
</template>
<el-row>
<el-col :span="3">
<PreviewImage :src="recent.coverPic" />
</el-col>
<el-col :span="21">
<el-descriptions :column="2">
<el-descriptions-item>
<template #label>
<descriptions-item-label icon="ep:document" label="内容" />
</template>
<span class="user-cell">{{ recent.describe || '未知' }}</span>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label icon="ep:location" label="发布地址" />
</template>
<div class="recent-cell">
<div>省市区:</div>
<div>
<span>{{ recent.issueProvince || '未知' }}</span>-
<span>{{ recent.issueCity || '未知' }}</span>-
<span>{{ recent.issueDistrict || '未知' }}</span>
</div>
<template>
<el-card shadow="never" class="w-full h-full m-0!">
<template #header>
<slot name="header"></slot>
</template>
<el-row>
<el-col :span="6">
<div class="media-preview">
<PreviewImage v-for="url in getMediaUrls(recent.coverPic)" :key="url" :src="url" />
<video
v-for="url in getMediaUrls(recent.coverVideo)"
:key="url"
:src="url"
class="detail-video"
controls
playsinline
preload="metadata"
></video>
<span v-if="!recent.coverPic && !recent.coverVideo">无媒体</span>
</div>
</el-col>
<el-col :span="18">
<el-descriptions :column="2">
<el-descriptions-item>
<template #label>
<descriptions-item-label icon="ep:document" label="内容" />
</template>
<span class="user-cell">{{ recent.description || '未知' }}</span>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label icon="ep:location" label="发布地址" />
</template>
<div class="recent-cell">
<div>省市区:</div>
<div>
<span>{{ recent.issueProvince || '未知' }}</span
>- <span>{{ recent.issueCity || '未知' }}</span
>-
<span>{{ recent.issueDistrict || '未知' }}</span>
</div>
<div class="recent-cell">
<div>详细地址:</div>
<div>
<span>{{ recent.issueAddress || '未知' }}</span>
</div>
</div>
<div class="recent-cell">
<div>详细地址:</div>
<div>
<span>{{ recent.issueAddress || '未知' }}</span>
</div>
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
</el-card>
</template>
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<descriptions-item-label icon="ep:circle-check" label="审核状态" />
</template>
<div class="recent-cell">
<div>状态:</div>
<div>{{ getAuditStatusText(recent.auditStatus) }}</div>
</div>
<div class="recent-cell">
<div>来源:</div>
<div>{{ getAuditSourceText(recent.auditSource) }}</div>
</div>
<div v-if="recent.auditReason" class="recent-cell">
<div>原因:</div>
<div>{{ recent.auditReason }}</div>
</div>
</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
</el-card>
</template>
<script lang="ts" setup>
type Props = {
recent: any;
recent: any
}
const props = withDefaults(defineProps<Props>(), {
withDefaults(defineProps<Props>(), {
recent: {}
})
const getMediaUrls = (content?: string) =>
content
?.split(',')
.map((url) => url.trim())
.filter(Boolean) ?? []
const getAuditStatusText = (status: number) =>
({ 0: '审核中', 1: '审核通过', 2: '审核不通过' } as Record<number, string>)[status] ||
'未知状态'
const getAuditSourceText = (source?: string) =>
(
{ THIRD_PARTY: '第三方审核', MANUAL: '人工审核', HISTORY: '历史数据' } as Record<
string,
string
>
)[source || ''] || '尚未审核'
</script>
<style lang="scss" scoped>
@@ -58,6 +105,20 @@ const props = withDefaults(defineProps<Props>(), {
align-items: center;
}
.media-preview {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.detail-video {
width: 180px;
height: 120px;
border-radius: 6px;
background: #111;
object-fit: contain;
}
.recent-cell {
display: flex;
justify-content: flex-start;
@@ -81,7 +142,6 @@ const props = withDefaults(defineProps<Props>(), {
::v-deep(.el-descriptions__table) {
tbody {
.el-descriptions__cell {
display: flex;
@@ -109,4 +169,4 @@ const props = withDefaults(defineProps<Props>(), {
}
}
}
</style>
</style>
@@ -0,0 +1,173 @@
<template>
<Dialog
v-model="dialogVisible"
title="动态详情"
width="960px"
max-height="70vh"
:scroll="true"
>
<div v-loading="detailLoading" class="recent-detail">
<el-descriptions v-if="detailData.id" :column="2" border>
<el-descriptions-item label="动态编号">{{ detailData.id }}</el-descriptions-item>
<el-descriptions-item label="用户编号">{{ detailData.memberId }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag :type="getAuditTagType(detailData.auditStatus)">
{{ getAuditStatusText(detailData.auditStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="展示状态">
<el-tag :type="isRecentVisible(detailData.visible) ? 'success' : 'info'">
{{ isRecentVisible(detailData.visible) ? '显示中' : '已下架' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="审核来源">
{{ getAuditSourceText(detailData.auditSource) }}
</el-descriptions-item>
<el-descriptions-item label="人工审核人">
{{ detailData.auditUserId || '-' }}
</el-descriptions-item>
<el-descriptions-item label="发布时间">
{{ formatTime(detailData.createTime) }}
</el-descriptions-item>
<el-descriptions-item label="审核时间">
{{ formatTime(detailData.auditTime) }}
</el-descriptions-item>
<el-descriptions-item label="动态内容" :span="2">
<div class="description-text">{{ detailData.description || '-' }}</div>
</el-descriptions-item>
<el-descriptions-item label="媒体内容" :span="2">
<div v-if="imageUrls.length || videoUrls.length" class="detail-media-list">
<PreviewImage
v-for="url in imageUrls"
:key="url"
:src="url"
:width="160"
:height="120"
/>
<video
v-for="url in videoUrls"
:key="url"
:src="url"
class="detail-video"
controls
playsinline
preload="metadata"
></video>
</div>
<span v-else>无媒体</span>
</el-descriptions-item>
<el-descriptions-item label="发布位置" :span="2">
<div>{{ locationText }}</div>
<div v-if="detailData.issueAddress" class="secondary-text">
{{ detailData.issueAddress }}
</div>
</el-descriptions-item>
<el-descriptions-item v-if="detailData.auditReason" label="审核原因" :span="2">
<span class="audit-reason-text">{{ detailData.auditReason }}</span>
</el-descriptions-item>
</el-descriptions>
<el-empty v-else-if="!detailLoading" description="暂无动态数据" />
</div>
<template #footer>
<el-button @click="dialogVisible = false">关闭</el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { RecentApi, RecentVO } from '@/api/member/recent'
import { formatDate } from '@/utils/formatTime'
defineOptions({ name: 'MemberRecentDetailDialog' })
const dialogVisible = ref(false)
const detailLoading = ref(false)
const detailData = ref<Partial<RecentVO>>({})
const splitMediaUrls = (content?: string) =>
content
?.split(',')
.map((url) => url.trim())
.filter(Boolean) ?? []
const imageUrls = computed(() => splitMediaUrls(detailData.value.coverPic))
const videoUrls = computed(() => splitMediaUrls(detailData.value.coverVideo))
const locationText = computed(() => {
const regions = [
detailData.value.issueProvince,
detailData.value.issueCity,
detailData.value.issueDistrict
].filter(Boolean)
return regions.join(' / ') || '-'
})
const isRecentVisible = (visible?: boolean) => visible !== false
const getAuditStatusText = (status?: number) =>
({ 0: '审核中', 1: '审核通过', 2: '审核不通过' } as Record<number, string>)[
status ?? -1
] || '未知状态'
const getAuditTagType = (status?: number) =>
(
{ 0: 'warning', 1: 'success', 2: 'danger' } as Record<
number,
'warning' | 'success' | 'danger'
>
)[status ?? -1] || 'info'
const getAuditSourceText = (source?: string) =>
(
{ THIRD_PARTY: '第三方审核', MANUAL: '人工审核', HISTORY: '历史数据' } as Record<
string,
string
>
)[source || ''] || '尚未审核'
const formatTime = (value?: string) => (value ? formatDate(new Date(value)) : '-')
const open = async (id: number) => {
dialogVisible.value = true
detailLoading.value = true
detailData.value = {}
try {
detailData.value = await RecentApi.getRecent(id)
} finally {
detailLoading.value = false
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.recent-detail {
min-height: 240px;
}
.description-text {
white-space: pre-wrap;
word-break: break-word;
}
.detail-media-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.detail-video {
width: 240px;
height: 160px;
border-radius: 6px;
background: var(--el-fill-color-darker);
object-fit: contain;
}
.secondary-text {
margin-top: 4px;
color: var(--el-text-color-secondary);
}
.audit-reason-text {
color: var(--el-color-danger);
white-space: pre-wrap;
word-break: break-word;
}
</style>
+357 -48
View File
@@ -1,31 +1,76 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="用户" prop="memberNickname">
<el-input
v-model="queryParams.memberNickname" placeholder="请输入用户" clearable @keyup.enter="handleQuery"
class="!w-240px" />
v-model="queryParams.memberNickname"
placeholder="请输入用户"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="审核状态" prop="auditStatus">
<el-select
v-model="queryParams.auditStatus"
placeholder="全部状态"
clearable
class="!w-160px"
>
<el-option label="审核中" :value="0" />
<el-option label="审核通过" :value="1" />
<el-option label="审核不通过" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="展示状态" prop="visible">
<el-select
v-model="queryParams.visible"
placeholder="全部状态"
clearable
class="!w-140px"
>
<el-option label="显示中" :value="true" />
<el-option label="已下架" :value="false" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-440px" />
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-440px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
<el-button v-if="false" type="primary" plain @click="openForm('create')" v-hasPermi="['member:recent:create']">
<el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button>
<el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button>
<el-button
v-if="false"
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['member:recent:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
v-if="false" type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['member:recent:export']">
v-if="false"
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['member:recent:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
@@ -34,52 +79,88 @@ v-if="false" type="success" plain @click="handleExport" :loading="exportLoading"
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
table-layout="fixed"
max-height="calc(100vh - 330px)"
scrollbar-always-on
>
<el-table-column label="审核状态" align="center" fixed="left" width="180">
<template #default="{ row }">
<div class="audit-cell">
<el-tag :type="getAuditTagType(row.auditStatus)">
{{ getAuditStatusText(row.auditStatus) }}
</el-tag>
<span class="audit-source">{{ getAuditSourceText(row.auditSource) }}</span>
<el-tooltip v-if="row.auditReason" :content="row.auditReason" placement="top">
<span class="audit-reason">{{ row.auditReason }}</span>
</el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column label="展示状态" align="center" fixed="left" width="110">
<template #default="{ row }">
<el-tag :type="isRecentVisible(row.visible) ? 'success' : 'info'">
{{ isRecentVisible(row.visible) ? '显示中' : '已下架' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="用户" align="center" width="250">
<template #default="{ row }">
<div class="flex items-center gap-2">
<PreviewImage :src="row.member.memberAvatar" />
<PreviewImage :src="row.member?.memberAvatar" />
<div class="flex flex-col items-start">
<div class="flex items-center gap-1">
<div class="table-cell">
<span>昵称:</span>
<span>{{ row.member.memberNickname }}</span>
<span>{{ row.member?.memberNickname }}</span>
</div>
<div class="table-cell">
<span>年龄:</span>
<span>{{ row.member.memberAge || '未知' }}岁</span>
<span>{{ row.member?.memberAge || '未知' }}岁</span>
</div>
</div>
<div class="flex items-center gap-1">
<div class="table-cell">
<span>性别:</span>
<dict-tag size="" :type="DICT_TYPE.SYSTEM_USER_SEX" :value="row.member.memberSex" />
<dict-tag
size=""
:type="DICT_TYPE.SYSTEM_USER_SEX"
:value="row.member?.memberSex"
/>
</div>
<div class="table-cell">
<span>职业:</span>
<span>{{ row.member.memberOcc || '未知' }}</span>
<span>{{ row.member?.memberOcc || '未知' }}</span>
</div>
</div>
<div class="flex items-center gap-1">
<div class="table-cell">
<span>身高:</span>
<span>{{ row.member.memberHeight || '未知' }}</span>
<span>{{ row.member?.memberHeight || '未知' }}</span>
</div>
<div class="table-cell">
<span>体重:</span>
<span>{{ row.member.memberWeight || '未知' }}kg</span>
<span>{{ row.member?.memberWeight || '未知' }}kg</span>
</div>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="用户标签" align="center" width="200">
<el-table-column label="用户标签" align="center" width="180">
<template #default="{ row }">
<div class="flex gap-2">
<dict-tag
v-for="v in row.memberTagList" :key="v.tagBizType" :type="DICT_TYPE.MEMBER_RECENT_TAG"
:value="v.tagBizType" />
v-for="v in row.memberTagList"
:key="v.tagBizType"
:type="DICT_TYPE.MEMBER_RECENT_TAG"
:value="v.tagBizType"
/>
</div>
</template>
</el-table-column>
@@ -98,12 +179,26 @@ v-for="v in row.memberTagList" :key="v.tagBizType" :type="DICT_TYPE.MEMBER_RECEN
</div>
</template>
</el-table-column>
<el-table-column label="图片" align="center" prop="coverPic">
<el-table-column label="媒体内容" align="center" width="240">
<template #default="{ row }">
<PreviewImage :src="row.coverPic" />
<div v-if="getMediaUrls(row.coverPic).length" class="media-list">
<PreviewImage v-for="url in getMediaUrls(row.coverPic)" :key="url" :src="url" />
</div>
<div v-if="getMediaUrls(row.coverVideo).length" class="media-list">
<video
v-for="url in getMediaUrls(row.coverVideo)"
:key="url"
:src="url"
class="audit-video"
controls
playsinline
preload="metadata"
></video>
</div>
<span v-if="!row.coverPic && !row.coverVideo" class="text-gray-400">无媒体</span>
</template>
</el-table-column>
<el-table-column label="描述" align="center" prop="describe" />
<el-table-column label="描述" align="left" prop="description" width="220" />
<el-table-column label="统计" align="center" width="200">
<template #default="{ row }">
<div class="flex flex-col items-start">
@@ -126,31 +221,83 @@ v-for="v in row.memberTagList" :key="v.tagBizType" :type="DICT_TYPE.MEMBER_RECEN
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
<el-table-column label="操作" align="center" min-width="80px">
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" fixed="right" width="330px">
<template #default="{ row }">
<el-button link type="primary" @click="openDetail(row.id)">
详情
</el-button>
<el-button
v-if="false" link type="primary" @click="openForm('update', row.id)"
v-hasPermi="['member:recent:update']">
编辑
</el-button>
<el-button link type="danger" @click="handleDelete(row.id)" v-hasPermi="['member:recent:delete']">
删除
</el-button>
<div class="row-actions">
<el-button link type="primary" @click="openUserDetail(row.memberId)"> 用户详情 </el-button>
<el-button link type="primary" @click="openRecentDetail(row.id)"> 动态详情 </el-button>
<el-button
v-if="row.auditStatus !== 1"
link
type="success"
:disabled="auditingId === row.id"
:loading="auditingId === row.id"
@click="handleAuditApprove(row.id)"
v-hasPermi="['member:recent:update']"
>
通过
</el-button>
<el-button
v-if="row.auditStatus !== 2"
link
type="warning"
:disabled="auditingId === row.id"
@click="handleAuditReject(row.id)"
v-hasPermi="['member:recent:update']"
>
驳回
</el-button>
<el-button
link
:type="isRecentVisible(row.visible) ? 'warning' : 'success'"
:disabled="visibleUpdatingId === row.id"
:loading="visibleUpdatingId === row.id"
@click="handleVisibleChange(row)"
v-hasPermi="['member:recent:update']"
>
{{ isRecentVisible(row.visible) ? '下架' : '恢复' }}
</el-button>
<el-button
v-if="false"
link
type="primary"
@click="openForm('update', row.id)"
v-hasPermi="['member:recent:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(row.id)"
v-hasPermi="['member:recent:delete']"
>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<RecentForm ref="formRef" @success="getList" />
<!-- 动态详情弹窗 -->
<RecentDetail ref="recentDetailRef" />
</template>
<script setup lang="ts">
@@ -159,6 +306,8 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { RecentApi, RecentVO } from '@/api/member/recent'
import RecentForm from './RecentForm.vue'
import RecentDetail from './cmp/recent-detail.vue'
import { ElMessageBox } from 'element-plus'
/** 朋友圈 列表 */
defineOptions({ name: 'Recent' })
@@ -173,15 +322,60 @@ const queryParams = reactive({
pageNo: 1,
pageSize: 10,
memberNickname: '',
auditStatus: undefined as number | undefined,
visible: undefined as boolean | undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
const auditingId = ref<number>()
const visibleUpdatingId = ref<number>()
const getMediaUrls = (content?: string) =>
content
?.split(',')
.map((url) => url.trim())
.filter(Boolean) ?? []
const getAuditStatusText = (status: number) => {
return (
({ 0: '审核中', 1: '审核通过', 2: '审核不通过' } as Record<number, string>)[status] ||
'未知状态'
)
}
const getAuditTagType = (status: number) => {
const tagTypes: Record<number, 'warning' | 'success' | 'danger' | 'info'> = {
0: 'warning',
1: 'success',
2: 'danger'
}
return tagTypes[status] || 'info'
}
const getAuditSourceText = (source?: string) => {
return (
(
{ THIRD_PARTY: '第三方审核', MANUAL: '人工审核', HISTORY: '历史数据' } as Record<
string,
string
>
)[source || ''] || '尚未审核'
)
}
const isRecentVisible = (visible?: boolean) => visible !== false
/** 打开会员详情 */
const { push } = useRouter()
const openDetail = (id: number) => {
push({ name: 'MemberRecentDetail', params: { id } })
const openUserDetail = (memberId: number) => {
push({ name: 'MemberUserDetail', params: { id: memberId } })
}
/** 打开动态详情 */
const recentDetailRef = ref()
const openRecentDetail = (id: number) => {
recentDetailRef.value.open(id)
}
/** 查询列表 */
@@ -224,7 +418,64 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch { }
} catch {}
}
/** 人工审核通过 */
const handleAuditApprove = async (id: number) => {
try {
await message.confirm('确认人工审核通过该动态吗?通过后将展示在动态列表中。')
auditingId.value = id
await RecentApi.auditRecent({ id, auditStatus: 1 })
message.success('审核通过')
await getList()
} catch {
} finally {
auditingId.value = undefined
}
}
/** 人工审核驳回 */
const handleAuditReject = async (id: number) => {
try {
const { value } = await ElMessageBox.prompt(
'请填写驳回原因,用户将在“我的动态”中看到该原因。',
'驳回动态',
{
confirmButtonText: '确认驳回',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入具体原因',
inputValidator: (input) => Boolean(input?.trim()) || '驳回原因不能为空'
}
)
auditingId.value = id
await RecentApi.auditRecent({ id, auditStatus: 2, auditReason: value.trim() })
message.success('已驳回')
await getList()
} catch {
} finally {
auditingId.value = undefined
}
}
/** 紧急下架或恢复动态 */
const handleVisibleChange = async (row: RecentVO) => {
const nextVisible = !isRecentVisible(row.visible)
try {
await message.confirm(
nextVisible
? '确认恢复展示该动态吗?只有审核通过的动态才会出现在公开列表中。'
: '确认紧急下架该动态吗?下架后会立即从公开列表中隐藏。'
)
visibleUpdatingId.value = row.id
await RecentApi.updateRecentVisible({ id: row.id, visible: nextVisible })
message.success(nextVisible ? '动态已恢复展示' : '动态已下架')
await getList()
} catch {
} finally {
visibleUpdatingId.value = undefined
}
}
/** 导出按钮操作 */
@@ -290,4 +541,62 @@ onMounted(() => {
color: lightblue;
}
}
</style>
.media-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
& + & {
margin-top: 8px;
}
}
.audit-video {
width: 160px;
height: 100px;
border-radius: 6px;
background: #111;
object-fit: contain;
}
.audit-cell {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.audit-source {
color: var(--el-text-color-secondary);
font-size: 12px;
}
.audit-reason {
max-width: 190px;
overflow: hidden;
color: var(--el-color-danger);
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.row-actions {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
align-items: center;
gap: 8px 4px;
width: 100%;
:deep(.el-button) {
justify-content: center;
width: 100%;
margin: 0;
}
}
:deep(.el-table .el-scrollbar__bar.is-horizontal) {
height: 10px;
}
</style>