|
|
@@ -0,0 +1,840 @@
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ :title="'事件详情 - ' + (detailData.eventTitle || '')"
|
|
|
+ :visible.sync="dialogVisible"
|
|
|
+ width="900px"
|
|
|
+ @close="handleClose"
|
|
|
+ >
|
|
|
+ <el-tabs v-model="activeTab">
|
|
|
+ <!-- 事件详情页签 -->
|
|
|
+ <el-tab-pane label="事件详情" name="detail">
|
|
|
+ <div class="detail-content">
|
|
|
+ <!-- 基本信息卡片 -->
|
|
|
+ <el-card class="detail-card" shadow="never">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="card-title">基本信息</span>
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" size="medium">
|
|
|
+ <el-descriptions-item label="事件标识">
|
|
|
+ <span class="text-value">{{ detailData.eventId || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="事件标题">
|
|
|
+ <span class="text-value">{{ detailData.eventTitle || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="事件状态">
|
|
|
+ <el-tag :type="getStatusType(detailData.eventStatus)" size="small">
|
|
|
+ {{ detailData.eventStatusLabel || '--' }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="所属行业">
|
|
|
+ <span class="text-value">{{ detailData.industryLabel || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="事件大类">
|
|
|
+ <span class="text-value">{{ detailData.eventTypeDlLabel || detailData.eventTypeDl || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="事件小类">
|
|
|
+ <span class="text-value">{{ detailData.eventTypeXlLabel || detailData.eventTypeXl || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="是否催办">
|
|
|
+ <el-tag :type="detailData.expediteStatus === '1' ? 'danger' : 'info'" size="small">
|
|
|
+ {{ detailData.expediteStatus === '1' ? '已催办' : '未催办' }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="是否督办">
|
|
|
+ <el-tag :type="detailData.superviseStatus === '1' ? 'warning' : 'info'" size="small">
|
|
|
+ {{ detailData.superviseStatus === '1' ? '已督办' : '未督办' }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 位置信息卡片 -->
|
|
|
+ <el-card class="detail-card" shadow="never">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="card-title">位置信息</span>
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" size="medium">
|
|
|
+ <el-descriptions-item label="经度">
|
|
|
+ <span class="text-value">{{ detailData.longitude || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="纬度">
|
|
|
+ <span class="text-value">{{ detailData.latitude || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+<!-- <el-descriptions-item label="事件来源">
|
|
|
+ <span class="text-value">{{ getEventSourceLabel(detailData.eventSource) }}</span>
|
|
|
+ </el-descriptions-item>-->
|
|
|
+
|
|
|
+ <el-descriptions-item label="业务ID">
|
|
|
+ <span class="text-value">{{ detailData.busId || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 时间信息卡片 -->
|
|
|
+ <el-card class="detail-card" shadow="never">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="card-title">时间信息</span>
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" size="medium">
|
|
|
+ <el-descriptions-item label="上报时间">
|
|
|
+ <span class="text-value time-value">{{ formatFullTime(detailData.createTime) }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="上报人">
|
|
|
+ <span class="text-value">{{ detailData.createName || detailData.createBy || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="更新时间">
|
|
|
+ <span class="text-value time-value">{{ formatFullTime(detailData.updateTime) || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+
|
|
|
+ <el-descriptions-item label="更新人">
|
|
|
+ <span class="text-value">{{ detailData.updateName || detailData.updateBy || '--' }}</span>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 事件描述卡片 -->
|
|
|
+ <el-card class="detail-card" shadow="never">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="card-title">事件描述</span>
|
|
|
+ </div>
|
|
|
+ <div class="event-description">
|
|
|
+ <pre>{{ detailData.eventDescription || '暂无描述' }}</pre>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 备注信息 -->
|
|
|
+ <el-card v-if="detailData.remark" class="detail-card" shadow="never">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="card-title">备注</span>
|
|
|
+ </div>
|
|
|
+ <div class="remark-content">
|
|
|
+ {{ detailData.remark }}
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 事件日志页签 -->
|
|
|
+ <el-tab-pane label="事件日志" name="log">
|
|
|
+ <div class="log-content">
|
|
|
+ <el-timeline v-if="eventLogData.length > 0">
|
|
|
+ <el-timeline-item
|
|
|
+ v-for="(log, index) in sortedEventLogs"
|
|
|
+ :key="log.logId || index"
|
|
|
+ :timestamp="formatTime(log.createTime)"
|
|
|
+ :type="getLogType(log)"
|
|
|
+ :color="getLogColor(log)"
|
|
|
+ :icon="getLogIcon(log)"
|
|
|
+ placement="top"
|
|
|
+ size="large"
|
|
|
+ >
|
|
|
+ <el-card class="log-card" shadow="hover">
|
|
|
+ <!-- 日志头部 -->
|
|
|
+ <div class="log-header">
|
|
|
+ <div class="log-user">
|
|
|
+ <el-avatar size="small" :src="getUserAvatar(log)" class="user-avatar">
|
|
|
+ {{ getAvatarText(log.createName || log.createBy) }}
|
|
|
+ </el-avatar>
|
|
|
+ <span class="user-name">{{ log.createName || log.createBy || '未知用户' }}</span>
|
|
|
+ <el-tag
|
|
|
+ v-if="getLogTag(log)"
|
|
|
+ :type="getLogTagType(log)"
|
|
|
+ size="mini"
|
|
|
+ class="log-tag"
|
|
|
+ >
|
|
|
+ {{ getLogTag(log) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <span class="log-time">{{ formatFullTime(log.createTime) }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 日志内容 -->
|
|
|
+ <div class="log-body">
|
|
|
+ <div class="log-text">{{ log.logContent || '--' }}</div>
|
|
|
+
|
|
|
+ <!-- 图片附件 -->
|
|
|
+ <div v-if="hasImages(log)" class="image-section">
|
|
|
+ <div class="section-title">
|
|
|
+ <i class="el-icon-picture"></i>
|
|
|
+ <span>图片附件</span>
|
|
|
+ </div>
|
|
|
+ <div class="image-grid">
|
|
|
+ <div
|
|
|
+ v-for="(attach, imgIndex) in getImages(log)"
|
|
|
+ :key="imgIndex"
|
|
|
+ class="image-item"
|
|
|
+ @click="handlePreviewImage(log, imgIndex)"
|
|
|
+ >
|
|
|
+ <el-image
|
|
|
+ :src="getImageUrl(attach)"
|
|
|
+ fit="cover"
|
|
|
+ class="preview-image"
|
|
|
+ :preview-src-list="getImagePreviewList(log)"
|
|
|
+ >
|
|
|
+ <div slot="error" class="image-error">
|
|
|
+ <i class="el-icon-picture-outline"></i>
|
|
|
+ </div>
|
|
|
+ <div slot="placeholder" class="image-loading">
|
|
|
+ <i class="el-icon-loading"></i>
|
|
|
+ </div>
|
|
|
+ </el-image>
|
|
|
+ <div class="image-toolbar" v-if="getImageCount(log) > 1">
|
|
|
+ <span class="image-index">{{ imgIndex + 1 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="image-count" v-if="getImageCount(log) > 1">
|
|
|
+ 共 {{ getImageCount(log) }} 张图片
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 其他附件(如果有的话) -->
|
|
|
+ <div v-if="hasOtherAttachments(log)" class="attachment-section">
|
|
|
+ <div class="section-title">
|
|
|
+ <i class="el-icon-document"></i>
|
|
|
+ <span>其他附件</span>
|
|
|
+ </div>
|
|
|
+ <div class="attachment-list">
|
|
|
+ <div
|
|
|
+ v-for="(attach, attachIndex) in getOtherAttachments(log)"
|
|
|
+ :key="attachIndex"
|
|
|
+ class="attachment-item"
|
|
|
+ @click="handleDownloadAttach(attach)"
|
|
|
+ >
|
|
|
+ <i class="el-icon-document attachment-icon"></i>
|
|
|
+ <span class="attachment-name">{{ getFileName(attach) }}</span>
|
|
|
+ <i class="el-icon-download download-icon"></i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 日志底部 -->
|
|
|
+ <div class="log-footer">
|
|
|
+ <span class="log-id">日志标识: {{ log.logId }}</span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <div v-else class="empty-log">
|
|
|
+ <el-empty
|
|
|
+ description="暂无事件日志"
|
|
|
+ image="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
|
|
|
+ >
|
|
|
+ <el-button type="primary" size="small" @click="activeTab = 'detail'">查看事件详情</el-button>
|
|
|
+ </el-empty>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
+ <!-- 操作按钮 -->
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="dialogVisible = false">关 闭</el-button>
|
|
|
+<!-- <el-button
|
|
|
+ type="primary"
|
|
|
+ v-if="hasImagesInLogs"
|
|
|
+ @click="handleExportImages"
|
|
|
+ >
|
|
|
+ 导出所有图片
|
|
|
+ </el-button>-->
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'EventDetailDialog',
|
|
|
+
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ dialogVisible: false,
|
|
|
+ activeTab: 'detail',
|
|
|
+ detailData: {},
|
|
|
+ eventLogData: [],
|
|
|
+
|
|
|
+ // 事件来源映射
|
|
|
+ eventSourceMap: {
|
|
|
+ 'life_line_test': '生命线测试',
|
|
|
+ 'life_line_system': '生命线系统',
|
|
|
+ 'manual_report': '人工上报',
|
|
|
+ 'ai_detection': 'AI检测',
|
|
|
+ 'iot_device': '物联网设备'
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ // 按创建时间倒序排序的事件日志
|
|
|
+ sortedEventLogs() {
|
|
|
+ return [...this.eventLogData].sort((a, b) => {
|
|
|
+ return new Date(b.createTime) - new Date(a.createTime)
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 检查日志中是否有图片
|
|
|
+ hasImagesInLogs() {
|
|
|
+ return this.eventLogData.some(log => this.hasImages(log))
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ // 打开对话框
|
|
|
+ open(data) {
|
|
|
+ if (data && data.data) {
|
|
|
+ this.detailData = data.data.detail || {}
|
|
|
+ this.eventLogData = data.data.eventLog || []
|
|
|
+ this.dialogVisible = true
|
|
|
+ this.activeTab = 'detail'
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 关闭对话框
|
|
|
+ handleClose() {
|
|
|
+ this.detailData = {}
|
|
|
+ this.eventLogData = []
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化时间(时间轴用)
|
|
|
+ formatTime(time) {
|
|
|
+ if (!time) return '--'
|
|
|
+ const date = new Date(time)
|
|
|
+ const now = new Date()
|
|
|
+ const diff = now - date
|
|
|
+
|
|
|
+ // 今天
|
|
|
+ if (date.toDateString() === now.toDateString()) {
|
|
|
+ return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 昨天
|
|
|
+ const yesterday = new Date(now)
|
|
|
+ yesterday.setDate(yesterday.getDate() - 1)
|
|
|
+ if (date.toDateString() === yesterday.toDateString()) {
|
|
|
+ return '昨天 ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 一周内
|
|
|
+ if (diff < 7 * 24 * 60 * 60 * 1000) {
|
|
|
+ const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
|
|
+ return days[date.getDay()] + ' ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更早时间
|
|
|
+ return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化完整时间
|
|
|
+ formatFullTime(time) {
|
|
|
+ if (!time) return '--'
|
|
|
+ const date = new Date(time)
|
|
|
+ return date.toLocaleString('zh-CN', {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit',
|
|
|
+ hour: '2-digit',
|
|
|
+ minute: '2-digit',
|
|
|
+ second: '2-digit'
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取事件来源标签
|
|
|
+ getEventSourceLabel(source) {
|
|
|
+ return this.eventSourceMap[source] || source || '--'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取事件状态标签类型
|
|
|
+ getStatusType(status) {
|
|
|
+ const map = {
|
|
|
+ 'event_report': 'primary', // 上报 - 蓝色
|
|
|
+ 'event_processing': 'warning', // 处理中 - 橙色
|
|
|
+ 'event_completed': 'success', // 已完成 - 绿色
|
|
|
+ 'event_closed': 'info', // 已关闭 - 灰色
|
|
|
+ 'event_cancelled': 'danger' // 已取消 - 红色
|
|
|
+ }
|
|
|
+ return map[status] || ''
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志图标
|
|
|
+ getLogIcon(log) {
|
|
|
+ const content = log.logContent || ''
|
|
|
+ if (content.includes('催办')) return 'el-icon-warning'
|
|
|
+ if (content.includes('督办')) return 'el-icon-s-flag'
|
|
|
+ if (content.includes('上报')) return 'el-icon-upload'
|
|
|
+ if (content.includes('完成')) return 'el-icon-check'
|
|
|
+ return 'el-icon-chat-line-round'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志类型(用于时间轴)
|
|
|
+ getLogType(log) {
|
|
|
+ const content = log.logContent || ''
|
|
|
+ if (content.includes('催办')) return 'danger'
|
|
|
+ if (content.includes('督办')) return 'warning'
|
|
|
+ if (content.includes('上报')) return 'primary'
|
|
|
+ if (content.includes('完成')) return 'success'
|
|
|
+ return ''
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志颜色
|
|
|
+ getLogColor(log) {
|
|
|
+ const content = log.logContent || ''
|
|
|
+ if (content.includes('催办')) return '#f56c6c' // 红色
|
|
|
+ if (content.includes('督办')) return '#e6a23c' // 橙色
|
|
|
+ if (content.includes('上报')) return '#409eff' // 蓝色
|
|
|
+ if (content.includes('完成')) return '#67c23a' // 绿色
|
|
|
+ return '#909399' // 灰色
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志标签
|
|
|
+ getLogTag(log) {
|
|
|
+ const content = log.logContent || ''
|
|
|
+ if (content.includes('催办')) return '催办'
|
|
|
+ if (content.includes('督办')) return '督办'
|
|
|
+ if (content.includes('上报')) return '上报'
|
|
|
+ if (content.includes('完成')) return '完成'
|
|
|
+ if (content.includes('处理')) return '处理中'
|
|
|
+ if (content.includes('转办')) return '转办'
|
|
|
+ return null
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志标签类型
|
|
|
+ getLogTagType(log) {
|
|
|
+ const content = log.logContent || ''
|
|
|
+ if (content.includes('催办')) return 'danger'
|
|
|
+ if (content.includes('督办')) return 'warning'
|
|
|
+ if (content.includes('上报')) return 'primary'
|
|
|
+ if (content.includes('完成')) return 'success'
|
|
|
+ return 'info'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取用户头像
|
|
|
+ getUserAvatar(log) {
|
|
|
+ // 这里可以根据用户信息返回头像URL
|
|
|
+ return ''
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取头像文本
|
|
|
+ getAvatarText(name) {
|
|
|
+ if (!name) return '?'
|
|
|
+ if (name.length >= 2) {
|
|
|
+ return name.substring(name.length - 2)
|
|
|
+ }
|
|
|
+ return name.charAt(0)
|
|
|
+ },
|
|
|
+
|
|
|
+ // 检查日志是否有图片
|
|
|
+ hasImages(log) {
|
|
|
+ const attachments = log.logAttachList || []
|
|
|
+ return attachments.some(item => this.isImage(item))
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取日志中的图片列表
|
|
|
+ getImages(log) {
|
|
|
+ const attachments = log.logAttachList || []
|
|
|
+ return attachments.filter(item => this.isImage(item))
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取图片数量
|
|
|
+ getImageCount(log) {
|
|
|
+ return this.getImages(log).length
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取图片预览列表
|
|
|
+ getImagePreviewList(log) {
|
|
|
+ return this.getImages(log).map(item => this.getImageUrl(item))
|
|
|
+ },
|
|
|
+
|
|
|
+ // 检查是否有其他附件
|
|
|
+ hasOtherAttachments(log) {
|
|
|
+ const attachments = log.logAttachList || []
|
|
|
+ return attachments.some(item => !this.isImage(item))
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取其他附件列表
|
|
|
+ getOtherAttachments(log) {
|
|
|
+ const attachments = log.logAttachList || []
|
|
|
+ return attachments.filter(item => !this.isImage(item))
|
|
|
+ },
|
|
|
+
|
|
|
+ // 判断是否为图片
|
|
|
+ isImage(attachment) {
|
|
|
+ if (!attachment) return false
|
|
|
+
|
|
|
+ // 根据你的数据结构,判断attachPath是否是图片
|
|
|
+ const path = attachment.attachPath || ''
|
|
|
+ const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
|
|
|
+
|
|
|
+ return imageExtensions.some(ext =>
|
|
|
+ path.toLowerCase().endsWith(ext)
|
|
|
+ )
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取图片URL
|
|
|
+ getImageUrl(attachment) {
|
|
|
+ if (!attachment) return ''
|
|
|
+ return attachment.attachPath || ''
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取文件名
|
|
|
+ getFileName(attachment) {
|
|
|
+ if (!attachment) return '未知文件'
|
|
|
+
|
|
|
+ // 优先使用fileName
|
|
|
+ if (attachment.fileName) {
|
|
|
+ return attachment.fileName
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从attachPath中提取文件名
|
|
|
+ if (attachment.attachPath) {
|
|
|
+ const path = attachment.attachPath
|
|
|
+ const parts = path.split('/')
|
|
|
+ return parts[parts.length - 1]
|
|
|
+ }
|
|
|
+
|
|
|
+ return '未知文件'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 预览图片
|
|
|
+ handlePreviewImage(log, index) {
|
|
|
+ const images = this.getImages(log)
|
|
|
+ if (images[index]) {
|
|
|
+ // el-image会自动处理预览,这里不需要额外操作
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下载附件
|
|
|
+ handleDownloadAttach(attach) {
|
|
|
+ const url = this.getImageUrl(attach) || attach.attachPath
|
|
|
+ const fileName = this.getFileName(attach)
|
|
|
+
|
|
|
+ if (url) {
|
|
|
+ // 创建下载链接
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = url
|
|
|
+ link.download = fileName
|
|
|
+ link.target = '_blank'
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ } else {
|
|
|
+ this.$message.warning('无法获取附件链接')
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 导出所有图片
|
|
|
+ handleExportImages() {
|
|
|
+ const allImages = []
|
|
|
+
|
|
|
+ this.eventLogData.forEach(log => {
|
|
|
+ const images = this.getImages(log)
|
|
|
+ images.forEach(img => {
|
|
|
+ allImages.push({
|
|
|
+ url: this.getImageUrl(img),
|
|
|
+ name: this.getFileName(img),
|
|
|
+ logTime: log.createTime,
|
|
|
+ userName: log.createName || log.createBy
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ if (allImages.length === 0) {
|
|
|
+ this.$message.warning('没有找到可导出的图片')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 这里可以实现批量下载逻辑
|
|
|
+ this.$message.success(`找到 ${allImages.length} 张图片,导出功能开发中`)
|
|
|
+ console.log('待导出的图片列表:', allImages)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.detail-content {
|
|
|
+ max-height: 500px;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding-right: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-card {
|
|
|
+ margin-bottom: 15px;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-card:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.card-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.text-value {
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+.time-value {
|
|
|
+ color: #909399;
|
|
|
+ font-family: monospace;
|
|
|
+}
|
|
|
+
|
|
|
+.event-description {
|
|
|
+ padding: 5px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.event-description pre {
|
|
|
+ margin: 0;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-word;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #606266;
|
|
|
+ font-family: inherit;
|
|
|
+}
|
|
|
+
|
|
|
+.remark-content {
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日志样式 */
|
|
|
+.log-content {
|
|
|
+ max-height: 500px;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding-right: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.log-card {
|
|
|
+ margin: 10px 0;
|
|
|
+ border: none;
|
|
|
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
|
|
|
+}
|
|
|
+
|
|
|
+.log-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.log-user {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.user-avatar {
|
|
|
+ background-color: #409eff;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.user-name {
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.log-tag {
|
|
|
+ margin-left: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.log-time {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.log-body {
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.log-text {
|
|
|
+ color: #606266;
|
|
|
+ line-height: 1.6;
|
|
|
+ margin-bottom: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 图片部分 */
|
|
|
+.image-section {
|
|
|
+ margin-top: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title i {
|
|
|
+ color: #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+.image-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.image-item {
|
|
|
+ position: relative;
|
|
|
+ height: 100px;
|
|
|
+ border-radius: 6px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: transform 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.image-item:hover {
|
|
|
+ transform: scale(1.03);
|
|
|
+}
|
|
|
+
|
|
|
+.preview-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.image-error,
|
|
|
+.image-loading {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.image-toolbar {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 5px;
|
|
|
+ right: 5px;
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ color: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 2px 8px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.image-count {
|
|
|
+ margin-top: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 附件部分 */
|
|
|
+.attachment-section {
|
|
|
+ margin-top: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-item:hover {
|
|
|
+ background-color: #e9ecef;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-icon {
|
|
|
+ color: #409eff;
|
|
|
+ margin-right: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-name {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.download-icon {
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.log-footer {
|
|
|
+ padding-top: 10px;
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.log-id {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #b0b0b0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 空状态 */
|
|
|
+.empty-log {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ height: 300px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 页脚 */
|
|
|
+.dialog-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 滚动条美化 */
|
|
|
+.detail-content::-webkit-scrollbar,
|
|
|
+.log-content::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-content::-webkit-scrollbar-track,
|
|
|
+.log-content::-webkit-scrollbar-track {
|
|
|
+ background: #f1f1f1;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-content::-webkit-scrollbar-thumb,
|
|
|
+.log-content::-webkit-scrollbar-thumb {
|
|
|
+ background: #c1c1c1;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-content::-webkit-scrollbar-thumb:hover,
|
|
|
+.log-content::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #a8a8a8;
|
|
|
+}
|
|
|
+</style>
|