/** 根据文件名或 MIME 判断是否为图片 */ export function isImageFileName(name?: string): boolean { if (!name) return false return /\.(?:png|jpe?g|gif|webp|bmp|svg|ico|tif|tiff|avif)$/i.test(name) } /** 上传接口使用的 formType */ export function getUploadFormType(file: { name?: string, type?: string }): 'image' | 'file' { const mime = file.type || '' if (mime.startsWith('image/') || isImageFileName(file.name)) return 'image' return 'file' } /** 将琉璃配置的 accept 转为 input accept 属性 */ export function buildInputAccept(accept?: string[] | string): string { if (!accept) return '*/*' const list = Array.isArray(accept) ? accept : [accept] const normalized = list .flatMap(item => String(item).split(',')) .map(item => item.trim()) .filter(Boolean) if (normalized.length === 0 || normalized.includes('*')) return '*/*' return normalized.join(',') } export interface FileTypeMeta { ext: string label: string color: string } /** 从文件名提取扩展名(小写,不含点) */ export function getFileExtension(name?: string): string { if (!name) return '' const base = name.split('?')[0].split('#')[0] const parts = base.split('.') if (parts.length < 2) return '' return (parts.pop() || '').toLowerCase() } /** 附件类型展示信息(表单小图标用) */ export function getFileTypeMeta(name?: string): FileTypeMeta { const ext = getFileExtension(name) const map: Record = { 'pdf': { label: 'PDF', color: '#e74c3c' }, 'doc': { label: 'Word', color: '#2b579a' }, 'docx': { label: 'Word', color: '#2b579a' }, 'xls': { label: 'Excel', color: '#217346' }, 'xlsx': { label: 'Excel', color: '#217346' }, 'ppt': { label: 'PPT', color: '#d24726' }, 'pptx': { label: 'PPT', color: '#d24726' }, 'txt': { label: 'TXT', color: '#607d8b' }, 'zip': { label: 'ZIP', color: '#795548' }, 'rar': { label: 'RAR', color: '#795548' }, '7z': { label: '7Z', color: '#795548' }, } const meta = map[ext] || { label: ext ? ext.toUpperCase() : '文件', color: '#909399' } return { ext: ext ? ext.toUpperCase() : 'FILE', label: meta.label, color: meta.color, } } export type PreviewFileKind = 'pdf' | 'docx' | 'doc' | 'xlsx' | 'xls' | 'image' | 'video' | 'audio' | 'text' | 'unsupported' /** 附件预览类型(vue-office / 原生标签) */ export function getPreviewFileKind(name?: string): PreviewFileKind { const ext = getFileExtension(name) if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'ico', 'tif', 'tiff', 'avif'].includes(ext)) return 'image' if (['mp4', 'webm', 'mov', 'm4v', 'avi', 'mkv', 'flv', 'rmvb'].includes(ext)) return 'video' if (['mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a', 'wma'].includes(ext)) return 'audio' if (ext === 'pdf') return 'pdf' if (ext === 'docx') return 'docx' if (ext === 'doc') return 'doc' if (ext === 'xlsx') return 'xlsx' if (ext === 'xls') return 'xls' if (['txt', 'md', 'log', 'csv', 'json', 'xml', 'yaml', 'yml', 'ini', 'conf', 'properties'].includes(ext)) return 'text' return 'unsupported' } /** 附件展示用文件名 */ export function getAttachmentDisplayName(file: { real_name?: string, name?: string, url?: string, f_downloadpath?: string }): string { return file.real_name || file.name || file.f_downloadpath?.split('/').pop() || file.url?.split('/').pop()?.split('?')[0] || '附件' } /** 附件类型中文描述(列表展示) */ export function getFileTypeLabel(name?: string): string { if (isImageFileName(name)) return '图片' const meta = getFileTypeMeta(name) const labelMap: Record = { 'PDF': 'PDF文档', 'Word': 'Word文档', 'Excel': 'Excel表格', 'PPT': 'PPT演示', 'TXT': '文本文件', 'ZIP': '压缩文件', 'RAR': '压缩文件', '7Z': '压缩文件', } return labelMap[meta.label] || (meta.ext === 'FILE' ? '文件' : `${meta.label}文件`) } /** 附件大小格式化(后端 size 单位为 MB) */ export function formatAttachmentSize(size?: number): string { if (!size) return '' if (size < 1) return `${(size * 1024).toFixed(1)} KB` return `${size.toFixed(1)} MB` } /** 判断附件是否为图片(含 url / data URL) */ export function isAttachmentImage(file: { real_name?: string, name?: string, url?: string, f_downloadpath?: string }): boolean { const name = getAttachmentDisplayName(file) if (isImageFileName(name)) return true const url = file.url || file.f_downloadpath || '' return isImageFileName(url) || /^data:image\//i.test(url) }