This commit is contained in:
zlwu committed 2026-06-06 19:44:18 +08:00
1 parent dacfdc9ff6
commit f9676f8759
906 files changed
+533306 -12946

No files matched your search

+58
View File
@@ -95,6 +95,46 @@ export function fillAttachmentInfoCache(
})
}
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
@@ -102,11 +142,29 @@ export function isCrmImageFile(file: { name?: string; url?: string; ossId?: stri
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
+23
View File
@@ -0,0 +1,23 @@
/** CRM 附件上传 bizType(与原 RuoYi /resource/oss/upload 一致) */
export const CRM_UPLOAD_BIZ_TYPE = {
CONTRACT_ATTACHMENT: 'crm_contract_attachment',
CONTRACT_PRICE_LETTER: 'crm_contract_price_letter',
CONTRACT_FILE: 'crm_contract_file',
QUOTE_ATTACHMENT: 'crm_quote_attachment'
} as const
export type CrmUploadBizType = (typeof CRM_UPLOAD_BIZ_TYPE)[keyof typeof CRM_UPLOAD_BIZ_TYPE]
/** 构建门户统一上传 FormData(file + bizType,folder 可选覆盖) */
export function buildAttachmentUploadFormData(
file: File,
options: { bizType: CrmUploadBizType | string; folder?: string }
): FormData {
const formData = new FormData()
formData.append('file', file)
formData.append('bizType', options.bizType)
if (options.folder) {
formData.append('folder', options.folder)
}
return formData
}
+47
View File
@@ -0,0 +1,47 @@
/**
* 全局包装 ElTag:默认插槽无有效内容时不渲染,避免空值时出现带背景色的空 tag。
* 通过 vite Components 自定义 resolver 替换自动导入的 ElTag。
*/
import { ElTag } from 'element-plus'
import { Comment, Text, defineComponent, h, type VNode, type VNodeArrayChildren } from 'vue'
function hasVisibleDefaultContent(vnodes: VNode[] | undefined): boolean {
if (!vnodes?.length) return false
const walk = (nodes: VNodeArrayChildren | undefined): boolean => {
if (!nodes) return false
for (const node of nodes) {
if (node == null || node === false) continue
if (typeof node === 'string' || typeof node === 'number') {
if (String(node).trim()) return true
continue
}
const vnode = node as VNode
if (vnode.type === Comment) continue
if (vnode.type === Text) {
if (String(vnode.children ?? '').trim()) return true
continue
}
if (vnode.type) return true
}
return false
}
return walk(vnodes)
}
const EmptyAwareElTag = defineComponent({
name: 'ElTag',
inheritAttrs: false,
props: ElTag.props,
setup(props, { slots, attrs }) {
return () => {
if (!hasVisibleDefaultContent(slots.default?.())) {
return null
}
return h(ElTag, { ...props, ...attrs }, slots)
}
}
})
export default EmptyAwareElTag