<script setup> import { fileFormatSize } from '@/utils/strings' import { ref, computed } from 'vue' import { useUploadsStore } from '@/store' import pptText from '@/assets/image/ppt-text.png' import excelText from '@/assets/image/excel-text.png' import wordText from '@/assets/image/word-text.png' import pdfText from '@/assets/image/pdf-text.png' import fileText from '@/assets/image/file-text.png' // 定义组件属性 const props = defineProps({ // 文件的额外信息 extra: { type: Object, required: true }, // 聊天记录数据 data: { type: Object, required: true }, // 是否使用最大宽度 maxWidth: { type: Boolean, default: false } }) const uploadsStore = useUploadsStore() const isPlaying = ref(false) // 文件类型配置 const fileTypes = { PDF: { icon: pdfText, color: '#DE4E4E', type: 'PDF' }, PPT: { icon: pptText, color: '#B74B2B', type: 'PPT' }, EXCEL: { icon: excelText, color: '#3C7F4B', type: 'EXCEL' }, WORD: { icon: wordText, color: '#2750B2', type: 'WORD' }, DEFAULT: { icon: fileText, color: '#747474', type: '文件' } } // Excel文件扩展名映射 const EXCEL_EXTENSIONS = ['XLS', 'XLSX', 'CSV'] // Word文件扩展名映射 const WORD_EXTENSIONS = ['DOC', 'DOCX', 'RTF', 'DOT', 'DOTX'] // PPT文件扩展名映射 const PPT_EXTENSIONS = ['PPT', 'PPTX', 'PPS', 'PPSX'] // 获取文件类型信息 const fileInfo = computed(() => { const extension = getFileExtension(props.extra.name) if (EXCEL_EXTENSIONS.includes(extension)) { return fileTypes.EXCEL } if (WORD_EXTENSIONS.includes(extension)) { return fileTypes.WORD } if (PPT_EXTENSIONS.includes(extension)) { return fileTypes.PPT } return fileTypes[extension] || fileTypes.DEFAULT }) // 获取文件扩展名 function getFileExtension(filename) { const parts = filename.split('.') return parts.length > 1 ? parts.pop().toUpperCase() : '' } // 切换播放状态 const togglePlay = () => { isPlaying.value = !isPlaying.value if (props.extra.is_uploading && props.extra.upload_id) { const action = isPlaying.value ? 'resumeUpload' : 'pauseUpload' uploadsStore[action](props.extra.upload_id) } } // 计算SVG圆环进度条的参数 const radius = 9 const circumference = computed(() => 2 * Math.PI * radius) const strokeDashoffset = computed(() => circumference.value * (1 - (props.extra.percentage || 0) / 100) ) // 处理文件点击事件 const handleClick = () => { console.log('props.extra', props.extra); window.open( `http://localhost:5500/?url=${props.extra.path}`, '_blank', 'width=800,height=600,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no' ); } </script> <template> <div class="file-message" @click="handleClick"> <!-- 文件头部信息 --> <div class="file-header"> <!-- 文件名 --> <div class="file-name">{{ extra.name }}</div> <!-- 文件图标区域 --> <div class="file-icon-container"> <img class="file-icon" :src="fileInfo.icon" alt="文件图标"> <!-- 上传进度圆环 - 上传状态 --> <div v-if="extra.is_uploading" class="progress-overlay"> <div class="circle-progress-container" @click="togglePlay"> <svg class="circle-progress" width="20" height="20" viewBox="0 0 20 20"> <!-- 底色圆环 --> <circle cx="10" cy="10" r="9" fill="transparent" stroke="#EEEEEE" stroke-width="2" /> <!-- 进度圆环 --> <circle cx="10" cy="10" r="9" fill="transparent" :stroke="fileInfo.color" stroke-width="2" :stroke-dasharray="circumference" :stroke-dashoffset="strokeDashoffset" transform="rotate(-90 10 10)" class="progress-circle" /> <!-- 暂停/播放图标 --> <g v-if="isPlaying" class="pause-icon"> <rect x="7" y="5" width="2" height="10" :fill="fileInfo.color" /> <rect x="11" y="5" width="2" height="10" :fill="fileInfo.color" /> </g> <g v-else class="play-icon"> <rect x="6" y="6" width="8" height="8" :fill="fileInfo.color" /> </g> </svg> </div> </div> </div> </div> <!-- 文件大小信息 --> <div class="file-size">{{ fileFormatSize(extra.size) }}</div> </div> </template> <style lang="less" scoped> .file-message { width: 243px; background-color: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); padding: 0 14px; cursor: pointer; } .file-header { display: flex; padding: 14px 5px 14px 0; justify-content: space-between; width: 100%; border-bottom: 1px solid #EEEEEE; } .file-name { height: 50px; color: #1A1A1A; font-size: 14px; word-break: break-word; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .file-icon-container { position: relative; } .file-icon { width: 48px; height: 48px; } .progress-overlay { background-color: #fff; position: absolute; top: 6px; left: 11px; width: 30px; height: 30px; display: flex; justify-content: center; align-items: center; } .file-size { color: #747474; font-size: 12px; padding: 5px 0 11px; } .circle-progress-container { width: 20px; height: 20px; position: relative; cursor: pointer; } .circle-progress { transform: rotate(-90deg); transform-origin: center; } .progress-circle { transition: stroke-dashoffset 0.3s ease; } .pause-icon, .play-icon { transform-origin: center; } .pause-icon { transform: rotate(90deg); } </style>