481 lines
13 KiB
Vue
481 lines
13 KiB
Vue
<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> |