admin-vue3/src/views/submit/common/detail.vue

481 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<el-drawer
v-model="dialogVisible"
title="数据详情"
size="38%"
direction="rtl"
:close-on-click-modal="true"
:close-on-press-escape="true"
:show-close="true"
>
<!-- 移除自定义 header使用默认header -->
<div class="main-container">
<!-- 基础信息 -->
<div class="group-warp">
<el-divider content-position="left">
<span class="group-name">基础信息</span>
</el-divider>
<div class="group-container">
<el-descriptions :column="2" size="default" border>
<el-descriptions-item label="标题" :span="2">
{{ detailData.title || "-" }}
</el-descriptions-item>
<el-descriptions-item label="部门">
{{ detailData.deptName || "-" }}
</el-descriptions-item>
<el-descriptions-item label="事项">
{{ detailData.reason?.name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="涉事人">
{{ detailData.personName || "-" }}
</el-descriptions-item>
<el-descriptions-item label="执行人">
{{ detailData.executorName || "-" }}
</el-descriptions-item>
<el-descriptions-item label="填报时间">
{{ formatDateTime(detailData.fillingTime) || "-" }}
</el-descriptions-item>
<el-descriptions-item label="审批人">
{{ detailData.approveUserName || "-" }}
</el-descriptions-item>
<el-descriptions-item label="审批时间">
{{ formatDateTime(detailData.approveTime) || "-" }}
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="getStatusTagType(detailData.status)">
{{ getStatusText(detailData.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ detailData.remark || "-" }}
</el-descriptions-item>
<template v-if="detailData.userIntegralRecords && detailData.userIntegralRecords.length > 0">
<el-descriptions-item
v-for="(item, index) in detailData.userIntegralRecords"
:key="index"
:label="'积分'"
>
<div class="user-score-detail">
<span>{{ item.userName || '-' }}</span>
<span v-if="item.happenedDate">
{{ ' 于 ' + item.happenedDate.slice(0, 10) }}
</span>
<span class="user-score-detail-value">
<el-tag size="small" :type="item.score > 0 ? 'success' : 'danger'">
{{ item.score >= 0 ? ('+' + item.score) : item.score }}
</el-tag>
</span>
</div>
</el-descriptions-item>
</template>
</el-descriptions>
</div>
</div>
<!-- 附件区域 -->
<div class="group-warp" v-if="detailData.imageUrls && detailData.imageUrls.length > 0">
<el-divider content-position="left">
<span class="group-name">附件</span>
</el-divider>
<div class="group-container">
<div class="image-container">
<div class="image-container-item" v-for="(url, index) in detailData.imageUrls" :key="index">
<el-image
:src="getImageSrc(url)"
fit="contain"
@click="showImage(getImageSrc(url))"
>
<template #error>
<div class="image-slot">
<el-icon>
<Picture />
</el-icon>
</div>
</template>
</el-image>
</div>
</div>
</div>
</div>
<!-- 评论区域 -->
<div class="group-warp">
<el-divider content-position="left">
<span class="group-name">评论信息</span>
</el-divider>
<div class="group-container comment-container">
<div class="b-white" v-if="showCommentInput === false">
<el-button type="primary" size="small" @click="showCommentInput = true">
<el-icon>
<Plus />
</el-icon>
我要评论
</el-button>
</div>
<div class="add-comment-form" v-if="showCommentInput">
<el-input
v-model="commentText"
type="textarea"
maxlength="1000"
:autosize="{ minRows: 3, maxRows: 6 }"
placeholder="请输入评论内容"
resize="none"
/>
<div class="dialog-footer">
<el-button @click="showCommentInput = false" size="small">
<el-icon>
<CloseBold />
</el-icon>
取消
</el-button>
<el-button type="primary" @click="addComment" :loading="commentLoading" size="small">
<el-icon>
<Select />
</el-icon>
提交
</el-button>
</div>
</div>
<div class="comment-list">
<template v-if="detailData.comments && detailData.comments.length > 0">
<el-timeline>
<el-timeline-item v-for="(comment, index) in detailData.comments" :key="index" :timestamp="formatDateTime(comment.createTime)" placement="top">
<el-card>
<div class="card-header" v-if="comment.createUserName === userId">
<el-icon class="comment-delete" color="#eb333d" @click="handleDeleteComment(index)">
<Delete />
</el-icon>
</div>
<h4 class="comment-content">{{ comment.comment }}</h4>
<p class="comment-user-info">{{ comment.createUserName + ' 提交于 ' + formatDateTime(comment.createTime) }}</p>
</el-card>
</el-timeline-item>
</el-timeline>
</template>
<template v-else>
<el-empty description="暂无评论" />
</template>
</div>
</div>
</div>
</div>
<!-- 图片预览 -->
<el-image-viewer
v-if="imageViewerVisible"
:url-list="[imageViewerUrl]"
@close="imageViewerVisible = false"
/>
</el-drawer>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getMyReportDetail, saveComment, deleteComment } from '@/api/submit/common/common'
import { useUserStore } from '@/store/modules/user'
import { Picture, Plus, CloseBold, Select, Delete } from '@element-plus/icons-vue'
import { enumsType } from '@/store/static'
// Props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
reportId: {
type: String,
default: ''
}
})
// Emits
const emit = defineEmits<{
'update:visible': [value: boolean]
'refresh': []
}>()
// 响应式数据
const dialogVisible = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val)
})
const detailData = ref({
id: '',
title: '',
deptId: '',
deptName: '',
reasonId: '',
reason: { name: '' },
personName: '',
executorName: '',
comment: '',
remark: '',
imageUrls: [] as string[],
status: '',
approveUserId: '',
approveUserName: '',
approveTime: '',
fillingTime: '',
userIntegralRecords: [] as any[],
comments: [] as any[],
depId: '',
type: '',
happenedDate: '',
approverId: '',
approverName: '',
approverTime: '',
createTime: '',
createBy: '',
createUserName: ''
})
const commentText = ref('')
const showCommentInput = ref(false)
const commentLoading = ref(false)
const imageViewerVisible = ref(false)
const imageViewerUrl = ref('')
const userStore = useUserStore()
const userId = computed(() => userStore.getUserInfo?.id)
// 监听reportId变化加载详情数据
watch(() => props.reportId, (newId) => {
if (newId && props.visible) {
loadDetailData(newId)
}
}, { immediate: true })
// 监听visible变化确保抽屉显示时重新加载数据
watch(() => props.visible, (newVisible) => {
if (newVisible && props.reportId) {
loadDetailData(props.reportId)
}
})
// 加载详情数据
const loadDetailData = async (id: string) => {
try {
console.log('开始加载详情数据reportId:', id)
const response = await getMyReportDetail(id)
console.log('接口返回数据:', response)
if (response) {
response.comments = response.comments || []
response.userIntegralRecords = response.userIntegralRecords || []
response.imageUrls = response.imageUrls || []
detailData.value = response
console.log('赋值给detailData的数据:', detailData.value)
} else {
console.error('接口返回数据格式错误:', response)
detailData.value = {
comments: [],
userIntegralRecords: [],
imageUrls: []
}
}
} catch (error) {
console.error('获取详情失败:', error)
ElMessage.error('获取详情失败')
}
}
// 获取图片URL
const getImageSrc = (url: string) => {
if (!url) return ''
if (url.startsWith('http://') || url.startsWith('https://')) {
return url
}
if (url.startsWith('/')) {
return window.location.origin + url
}
return window.location.origin + '/' + url
}
// 预览图片
const showImage = (url: string) => {
imageViewerUrl.value = getImageSrc(url)
imageViewerVisible.value = true
}
// 格式化日期时间
const formatDateTime = (dateTime: any) => {
if (!dateTime) return ''
const date = new Date(dateTime)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
// 获取状态标签类型
const getStatusTagType = (status: any) => {
const statusMap: Record<string, string> = {
'STAGING': 'warning',
'AUDIT': 'primary',
'APPROVED': 'success',
'REVIEW_FAILED': 'danger'
}
return statusMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status: any) => {
const statusItem = enumsType.reportStatus.find(item => item.value === status)
return statusItem ? statusItem.text : '-'
}
// 添加评论
const addComment = async () => {
if (!commentText.value.trim()) {
ElMessage.warning('请输入评论内容')
return
}
try {
commentLoading.value = true
await saveComment(props.reportId, {
comment: commentText.value.trim()
})
ElMessage.success('评论成功')
commentText.value = ''
showCommentInput.value = false
await loadDetailData(props.reportId)
} catch (error) {
console.error('评论失败:', error)
ElMessage.error('评论失败')
} finally {
commentLoading.value = false
}
}
// 删除评论
const handleDeleteComment = async (index: number) => {
try {
const comment = detailData.value.comments[index]
if (!comment) return
await ElMessageBox.confirm('确定删除该评论吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await deleteComment(comment.id)
ElMessage.success('删除成功')
await loadDetailData(props.reportId)
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error)
ElMessage.error('删除失败')
}
}
}
</script>
<style scoped>
.main-container {
padding: 0px 40px;
}
.group-name {
color: #A8ABB2
}
.group-warp {
padding-bottom: 10px;
}
.group-warp:last-child {
padding-bottom: 0px;
}
.group-container {
padding: 10px 15px;
}
.group-container .user-score-detail-value {
display: inline-block;
padding-left: 10px;
}
.group-container .image-container {
display: flex;
justify-content: flex-start;
align-items: center;
}
.group-container .image-container-item {
display: flex;
justify-content: center;
width: 110px;
height: 110px;
margin-right: 15px;
cursor: pointer;
}
.group-container .image-container-item .image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100px;
height: 100px;
color: var(--el-text-color-secondary);
font-size: 30px;
}
.group-container .image-container-item .image-slot .el-icon {
font-size: 30px;
}
.comment-container .b-white {
display: flex;
flex-direction: row-reverse;
}
.comment-container .comment-list {
margin-top: 10px;
max-height: 500px;
overflow-y: auto;
}
.comment-container .comment-list .card-header {
display: flex;
justify-content: flex-end;
}
.comment-container .comment-list .comment-delete {
cursor: pointer;
}
.comment-container .comment-list .comment-content {
line-height: 21px;
font-size: 14px;
}
.comment-container .comment-list .comment-user-info {
margin-top: 10px;
color: #A8ABB2;
font-size: 14px;
}
.comment-container .comment-list .el-card {
margin-bottom: 2px;
}
.comment-container .add-comment-form .dialog-footer {
display: flex;
justify-content: flex-end;
}
:deep(.el-descriptions__label){
width: 80px;
}
:deep(.el-descriptions__content){
min-width: 200px;
}
</style>