Files
shanxia2.0/web-admin/src/utils/crm-attachment.ts
T
2026-08-08 20:43:48 +08:00

236 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* CRM 附件解析与下载 — 门户 MinIO(fileUrl 即存储键/URL,不再走 CRM /resource/oss/listByIds)
*/
import {
downloadAttachmentFile,
openAttachmentPreviewInNewTab
} from '@/utils/download-attachment'
export type CrmAttachmentRef = {
ossId: string
category?: string
}
export type CrmAttachmentFile = {
ossId: string
name: string
url: string
category?: string
}
/** 解析 attachmentFiles 字段(JSON 数组或逗号分隔 URL) */
export function parseCrmAttachmentField(
fileStr: string | null | undefined
): CrmAttachmentRef[] {
const raw = (fileStr || '').trim()
if (!raw) return []
if (raw.startsWith('[')) {
try {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed)) {
return parsed.map((item: unknown) => {
if (typeof item === 'string') {
return { ossId: item, category: 'other' }
}
const obj = item as Record<string, unknown>
return {
ossId: String(obj.ossId || obj.url || ''),
category: (obj.category as string) || 'other'
}
}).filter((a) => a.ossId)
}
} catch {
// fall through
}
}
return raw
.split(',')
.map((s) => s.trim())
.filter(Boolean)
.map((ossId) => ({ ossId, category: 'other' }))
}
/** 从 ossId/url 推导展示文件名(完整 URL 与相对路径均取最后一段) */
export function crmAttachmentDisplayName(ossId: string, index = 0): string {
const id = (ossId || '').trim()
if (!id) return `附件${index + 1}`
// 含路径分隔符时取末段(兼容 http(s) URL 与 MinIO 相对键如 crm/contract/.../uuid.pdf)
if (id.includes('/')) {
const seg = (id.split('/').pop() || '').split('?')[0]
try {
return decodeURIComponent(seg) || `附件${index + 1}`
} catch {
return seg || `附件${index + 1}`
}
}
if (id.length > 12) return `附件_${id.substring(0, 8)}`
return id
}
/** 将解析结果转为 UI 文件项(门户直接存 MinIO URL,无需 listByIds) */
export function resolveCrmAttachmentFiles(
refs: CrmAttachmentRef[]
): CrmAttachmentFile[] {
return refs.map((att, index) => {
const ossId = att.ossId
return {
ossId,
name: crmAttachmentDisplayName(ossId, index),
url: ossId,
category: att.category || 'other'
}
})
}
/** 兼容旧 loadAttachmentInfos:批量解析并写入 cache Map */
export function fillAttachmentInfoCache(
ossIds: string[],
cache: Map<string, { name: string; url: string }>
): void {
ossIds.forEach((id, index) => {
const key = (id || '').trim()
if (!key || cache.has(key)) return
cache.set(key, {
name: crmAttachmentDisplayName(key, index),
url: key
})
})
}
export type ContractAttachmentItem = {
uid: string
name: string
ossId?: string
url?: string
localPreview?: string
status: 'uploading' | 'success' | 'error'
category: 'single_seal' | 'double_seal' | 'other'
}
/** 合同附件列表 → 保存 API 用的 JSON 字符串 */
export function serializeContractAttachments(items: ContractAttachmentItem[]): string {
const payload = items
.filter((file) => file.status === 'success' && file.ossId)
.map((file) => ({
ossId: String(file.ossId),
category: file.category || 'other'
}))
return payload.length ? JSON.stringify(payload) : ''
}
/** 合同附件列表 → uploadAttachments 接口 payload */
export function mapContractAttachmentsPayload(items: ContractAttachmentItem[]) {
return items
.filter((file) => file.status === 'success' && file.ossId)
.map((file) => ({
ossId: String(file.ossId),
category: file.category || 'other'
}))
}
/** MinIO 相对路径 → 浏览器可访问 URL */
export function resolveCrmAttachmentUrl(fileUrl: string): string {
const trimmed = String(fileUrl || '').trim()
if (!trimmed) return ''
if (/^https?:\/\//i.test(trimmed)) return trimmed
const base = String(import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
return base ? `${base}/${trimmed.replace(/^\//, '')}` : trimmed
}
export function isCrmImageFile(file: { name?: string; url?: string; ossId?: string }): boolean {
const name = file.name || ''
if (/\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(name)) return true
const src = file.url || file.ossId || ''
return /\.(jpg|jpeg|png|gif|bmp|webp)(\?|$)/i.test(src)
}
export function isCrmPdfFile(file: { name?: string; url?: string; ossId?: string }): boolean {
const name = file.name || ''
if (/\.pdf$/i.test(name)) return true
const src = file.url || file.ossId || ''
return /\.pdf(\?|$)/i.test(src)
}
export function getCrmFileExt(fileName: string): string {
const ext = fileName.split('.').pop()?.toUpperCase()
return ext || 'FILE'
}
const CRM_ATTACHMENT_CATEGORY_LABELS: Record<string, string> = {
single_seal: '单章附件',
double_seal: '双章附件',
other: '其他附件'
}
/** 附件分类 → 展示文案 */
export function getCrmAttachmentCategoryLabel(category?: string): string {
return CRM_ATTACHMENT_CATEGORY_LABELS[category || 'other'] || CRM_ATTACHMENT_CATEGORY_LABELS.other
}
/** 下载 CRM 附件(fileUrl / ossId 均为 MinIO 路径或完整 URL) */
export async function downloadCrmAttachment(file: {
ossId?: string
url?: string
name?: string
fileName?: string
}) {
const fileUrl = String(file.url || file.ossId || '').trim()
if (!fileUrl) return
const fileName = String(file.fileName || file.name || '').trim() || crmAttachmentDisplayName(fileUrl)
await downloadAttachmentFile({ fileUrl, fileName }, { fullFileUrl: resolveCrmAttachmentUrl })
}
/** CRM 附件在新标签页预览(图片 HTML 内嵌 / PDF 预签名直开) */
export async function openCrmAttachmentPreviewInNewTab(file: {
ossId?: string
url?: string
name?: string
fileName?: string
}) {
const fileUrl = String(file.url || file.ossId || '').trim()
if (!fileUrl) return false
const fileName =
String(file.fileName || file.name || '').trim() || crmAttachmentDisplayName(fileUrl)
const kind = isCrmImageFile({ name: fileName, url: fileUrl, ossId: fileUrl })
? 'image'
: isCrmPdfFile({ name: fileName, url: fileUrl, ossId: fileUrl })
? 'pdf'
: 'other'
return openAttachmentPreviewInNewTab(
{ fileUrl, fileName },
{ fullFileUrl: resolveCrmAttachmentUrl, kind }
)
}
/** el-upload 回显:从 attachmentFiles 字符串构建 file-list */
export function buildUploadFileListFromField(
fileStr: string | null | undefined
): Array<Record<string, unknown>> {
const refs = parseCrmAttachmentField(fileStr)
return refs.map((att, index) => ({
uid: `existing_${index}_${Date.now()}`,
name: crmAttachmentDisplayName(att.ossId, index),
url: att.ossId,
ossId: att.ossId,
category: att.category || 'other',
status: 'success'
}))
}
/** 从 el-upload file-list 收集 attachmentFiles JSON(供保存 API) */
export function serializeUploadAttachments(
files: Array<{ status?: string; ossId?: string; url?: string; category?: string }>
): string {
const items = files
.filter((f) => f.status === 'success' && (f.ossId || f.url))
.map((f) => ({
ossId: String(f.ossId || f.url),
category: f.category || 'other'
}))
return items.length ? JSON.stringify(items) : ''
}