fix:bug修改
This commit is contained in:
1 parent
0c92763537
commit
401c7c1dd6
5 files changed
+666
-94
No files matched your search
+2
-1
@@ -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
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user