feat(web-admin): add apply attachment display name helper

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-14 11:30:16 +08:00
1 parent 5d4e1e14ad
commit a564bfe733
2 files changed
+108

No files matched your search

@@ -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'])
})
})
@@ -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
})
)
}