fix: harden performance survey xlsx downloads

This commit is contained in:
江林铖 2026-08-06 14:28:26 +08:00
parent 2163ddb4f7
commit 52ad6aeb04
2 changed files with 65 additions and 3 deletions

View File

@ -2,7 +2,9 @@ import ExcelJS from 'exceljs'
import {
buildPerformanceSurveyDashboardWorkbook,
buildPerformanceSurveyExportTable,
choosePerformanceSurveyExportDestination,
sanitizePerformanceSurveyExportFileName,
savePerformanceSurveyDashboardWorkbook,
type PerformanceSurveyDashboardExportData,
} from '@/lib/performance-survey-dashboard-export'
@ -85,4 +87,44 @@ describe('performance survey dashboard xlsx export', () => {
expect(sanitizePerformanceSurveyExportFileName('考核:品牌/设计*主管'))
.toBe('考核_品牌_设计_主管.xlsx')
})
test('keeps the browser context when opening the native save picker', async () => {
const handle = { createWritable: jest.fn() }
const picker = jest.fn(function (this: unknown) {
expect(this).toBe(window)
return Promise.resolve(handle)
})
Object.defineProperty(window, 'showSaveFilePicker', {
configurable: true,
value: picker,
})
await expect(choosePerformanceSurveyExportDestination('考核调研.xlsx'))
.resolves.toEqual({ kind: 'file-system', handle })
expect(picker).toHaveBeenCalledWith(expect.objectContaining({
suggestedName: '考核调研.xlsx',
}))
delete (window as typeof window & { showSaveFilePicker?: unknown }).showSaveFilePicker
})
test('uses a native blob download when the save picker is unavailable', async () => {
const createObjectURL = jest.fn(() => 'blob:performance-survey-export')
const revokeObjectURL = jest.fn()
Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: createObjectURL })
Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: revokeObjectURL })
const click = jest.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {})
await savePerformanceSurveyDashboardWorkbook(
exportData,
'考核调研_品牌创意主管_2026-07.xlsx',
{ kind: 'download' },
)
expect(createObjectURL).toHaveBeenCalledWith(expect.any(Blob))
expect(click).toHaveBeenCalledTimes(1)
await new Promise((resolve) => window.setTimeout(resolve, 0))
expect(revokeObjectURL).toHaveBeenCalledWith('blob:performance-survey-export')
click.mockRestore()
})
})

View File

@ -211,7 +211,7 @@ export async function choosePerformanceSurveyExportDestination(
if (!picker) return { kind: 'download' }
try {
const handle = await picker({
const handle = await picker.call(window, {
suggestedName,
types: [{
description: 'WPS/Excel 工作簿',
@ -243,6 +243,26 @@ export async function savePerformanceSurveyDashboardWorkbook(
return
}
const { saveAs } = await import('file-saver')
saveAs(blob, fileName)
if (
typeof document === 'undefined'
|| typeof URL === 'undefined'
|| typeof URL.createObjectURL !== 'function'
) {
throw new Error('当前浏览器无法创建下载文件,请更换 Chrome 或 Edge 后重试。')
}
const objectUrl = URL.createObjectURL(blob)
const anchor = document.createElement('a')
anchor.href = objectUrl
anchor.download = fileName
anchor.rel = 'noopener'
anchor.style.display = 'none'
document.body.appendChild(anchor)
try {
anchor.click()
} finally {
anchor.remove()
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 0)
}
}