import { attachmentApi, type BxId } from '@/api/bx' import { ElMessage } from 'element-plus' export type BxAttachmentItem = { id: number | string fileName: string fileType?: string fileSize?: number previewUrl?: string _uploading?: boolean _existing?: boolean } const IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp|svg)$/i const VIDEO_EXT = /\.(mp4|webm|ogg|mov|avi)$/i const PDF_EXT = /\.pdf$/i export function isBxImageFile(file: { fileName?: string; fileType?: string }) { const name = String(file.fileName || '').toLowerCase() const type = String(file.fileType || '').toLowerCase() return type.startsWith('image/') || IMAGE_EXT.test(name) } export function isBxVideoFile(file: { fileName?: string; fileType?: string }) { const name = String(file.fileName || '').toLowerCase() const type = String(file.fileType || '').toLowerCase() return type.startsWith('video/') || VIDEO_EXT.test(name) } export function isBxPdfFile(file: { fileName?: string; fileType?: string }) { const name = String(file.fileName || '').toLowerCase() const type = String(file.fileType || '').toLowerCase() return type === 'application/pdf' || type.includes('pdf') || PDF_EXT.test(name) } export function canBxPreview(file: BxAttachmentItem) { if (file._uploading) return false return isBxImageFile(file) || isBxVideoFile(file) || isBxPdfFile(file) } export async function fillBxAttachmentPreviews( files: Array ) { const tasks = files .filter((file) => isBxImageFile(file) && !file.previewUrl) .map(async (file) => { const id = file.id ?? file.attachmentId if (id == null || id === '') return try { const res: any = await attachmentApi.preview(id) file.previewUrl = res.data?.url || '' } catch { /* preview optional */ } }) await Promise.all(tasks) } export async function loadBxAttachments(bizType: string, bizId: BxId): Promise { const res: any = await attachmentApi.listByBiz(bizType, bizId) const files: BxAttachmentItem[] = (res.data || []).map((item: any) => ({ id: item.id, fileName: item.fileName, fileType: item.fileType || '', fileSize: item.fileSize, previewUrl: '', _existing: true })) await fillBxAttachmentPreviews(files) return files } export async function downloadBxAttachment(file: { id: number | string; fileName: string }) { try { const res: any = await attachmentApi.download(file.id) const blob = new Blob([res], { type: 'application/octet-stream' }) const url = URL.createObjectURL(blob) const anchor = document.createElement('a') anchor.href = url anchor.download = file.fileName anchor.click() URL.revokeObjectURL(url) } catch (error: any) { ElMessage.error(error?.message || '下载失败') throw error } } export async function previewBxAttachment(file: BxAttachmentItem) { if (isBxVideoFile(file)) { try { const res: any = await attachmentApi.preview(file.id) return { type: 'video' as const, url: res.data?.url || '', fileName: file.fileName } } catch (error: any) { ElMessage.error(error?.message || '预览失败') throw error } } if (isBxImageFile(file)) { if (file.previewUrl) { return { type: 'image' as const, url: file.previewUrl, fileName: file.fileName } } try { const res: any = await attachmentApi.preview(file.id) file.previewUrl = res.data?.url || '' return { type: 'image' as const, url: file.previewUrl, fileName: file.fileName } } catch (error: any) { ElMessage.error(error?.message || '预览失败') throw error } } if (isBxPdfFile(file)) { try { const res: any = await attachmentApi.preview(file.id) return { type: 'pdf' as const, url: res.data?.url || '', fileName: file.fileName } } catch (error: any) { ElMessage.error(error?.message || '预览失败') throw error } } ElMessage.warning('该文件类型暂不支持在线预览,请使用下载查看') return null }