diff --git a/web-admin/src/utils/bx/attachment-display-name.test.ts b/web-admin/src/utils/bx/attachment-display-name.test.ts new file mode 100644 index 000000000..bf7e6a715 --- /dev/null +++ b/web-admin/src/utils/bx/attachment-display-name.test.ts @@ -0,0 +1,63 @@ +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { + sanitizeRemarkBaseName, + buildApplyAttachmentDisplayName, + mapApplyAttachmentDisplayNames +} from './attachment-display-name' + +describe('sanitizeRemarkBaseName', () => { + it('trims and replaces illegal chars and spaces', () => { + assert.equal(sanitizeRemarkBaseName(' 差旅/说明 A '), '差旅_说明_A') + }) + + it('truncates to 40 chars', () => { + const long = '啊'.repeat(50) + assert.equal(sanitizeRemarkBaseName(long).length, 40) + }) + + it('returns empty for blank', () => { + assert.equal(sanitizeRemarkBaseName(' '), '') + }) +}) + +describe('buildApplyAttachmentDisplayName', () => { + it('falls back to original when remark empty', () => { + assert.equal( + buildApplyAttachmentDisplayName({ remark: '', originalFileName: 'a.pdf', index1: 1 }), + 'a.pdf' + ) + }) + + it('uses remark_index + ext', () => { + assert.equal( + buildApplyAttachmentDisplayName({ + remark: '差旅说明', + originalFileName: 'photo.JPG', + index1: 2 + }), + '差旅说明_2.JPG' + ) + }) + + it('handles missing extension', () => { + assert.equal( + buildApplyAttachmentDisplayName({ + remark: '说明', + originalFileName: 'readme', + index1: 1 + }), + '说明_1' + ) + }) +}) + +describe('mapApplyAttachmentDisplayNames', () => { + it('maps list with 1-based indices', () => { + const out = mapApplyAttachmentDisplayNames('备注', [ + { fileName: 'a.pdf' }, + { fileName: 'b.jpg' } + ]) + assert.deepEqual(out, ['备注_1.pdf', '备注_2.jpg']) + }) +}) diff --git a/web-admin/src/utils/bx/attachment-display-name.ts b/web-admin/src/utils/bx/attachment-display-name.ts new file mode 100644 index 000000000..016dd57a1 --- /dev/null +++ b/web-admin/src/utils/bx/attachment-display-name.ts @@ -0,0 +1,45 @@ +const ILLEGAL = /[/\\:*?"<>|\u0000-\u001f]/g + +export function sanitizeRemarkBaseName(remark: string): string { + const cleaned = String(remark || '') + .trim() + .replace(ILLEGAL, '_') + .replace(/\s+/g, '_') + .replace(/_+/g, '_') + .replace(/^_|_$/g, '') + if (!cleaned) return '' + return cleaned.length > 40 ? cleaned.slice(0, 40) : cleaned +} + +function splitExt(fileName: string): { base: string; ext: string } { + const name = String(fileName || '') + const i = name.lastIndexOf('.') + if (i <= 0 || i === name.length - 1) return { base: name, ext: '' } + return { base: name.slice(0, i), ext: name.slice(i) } +} + +export function buildApplyAttachmentDisplayName(opts: { + remark: string + originalFileName: string + index1: number +}): string { + const original = String(opts.originalFileName || '').trim() || '申请附件' + const base = sanitizeRemarkBaseName(opts.remark) + if (!base) return original + const { ext } = splitExt(original) + const n = Math.max(1, Math.floor(Number(opts.index1) || 1)) + return `${base}_${n}${ext}` +} + +export function mapApplyAttachmentDisplayNames( + remark: string, + files: Array<{ fileName?: string | null }> +): string[] { + return (files || []).map((f, i) => + buildApplyAttachmentDisplayName({ + remark, + originalFileName: f.fileName || '', + index1: i + 1 + }) + ) +}