diff --git a/src/api/tjt/report/index.ts b/src/api/tjt/report/index.ts index b81ae29..011e741 100644 --- a/src/api/tjt/report/index.ts +++ b/src/api/tjt/report/index.ts @@ -14,28 +14,46 @@ export interface EmployeeOutputSummaryExportReqVO { sortType?: string } -export const exportProjectBudget = (projectId: number) => { - return request.download({ url: '/tjt/report/project-budget/export-excel', params: { projectId } }) +export interface ProjectBudgetExportReqVO { + projectId: number + year?: number } -export const exportProjectQuarterOutput = (planningId: number) => { - return request.download({ - url: '/tjt/report/project-quarter-output/export-excel', - params: { planningId } - }) +export interface ProjectQuarterOutputExportReqVO { + planningId: number + year?: number } -export const exportProjectLeadQuarterOutput = (planningId: number) => { - return request.download({ - url: '/tjt/report/project-lead-quarter-output/export-excel', - params: { planningId } - }) +export interface ProjectLeadQuarterOutputExportReqVO { + planningId: number + year?: number } -export const exportSpecialtyPersonOutput = (params: { +export interface SpecialtyPersonOutputExportReqVO { planningId: number specialtyCode: string -}) => { + year?: number +} + +export const exportProjectBudget = (params: ProjectBudgetExportReqVO) => { + return request.download({ url: '/tjt/report/project-budget/export-excel', params }) +} + +export const exportProjectQuarterOutput = (params: ProjectQuarterOutputExportReqVO) => { + return request.download({ + url: '/tjt/report/project-quarter-output/export-excel', + params + }) +} + +export const exportProjectLeadQuarterOutput = (params: ProjectLeadQuarterOutputExportReqVO) => { + return request.download({ + url: '/tjt/report/project-lead-quarter-output/export-excel', + params + }) +} + +export const exportSpecialtyPersonOutput = (params: SpecialtyPersonOutputExportReqVO) => { return request.download({ url: '/tjt/report/specialty-person-output/export-excel', params }) } diff --git a/src/config/axios/index.ts b/src/config/axios/index.ts index 07719a2..cf41105 100644 --- a/src/config/axios/index.ts +++ b/src/config/axios/index.ts @@ -14,6 +14,12 @@ const request = (option: any) => { } }) } + +export interface DownloadResponseData { + data: Blob + fileName?: string +} + export default { get: async (option: any) => { const res = await request({ method: 'GET', ...option }) @@ -35,9 +41,9 @@ export default { const res = await request({ method: 'PUT', ...option }) return res.data as unknown as T }, - download: async (option: any) => { + download: async (option: any) => { const res = await request({ method: 'GET', responseType: 'blob', ...option }) - return res as unknown as Promise + return res as T }, upload: async (option: any) => { option.headersType = 'multipart/form-data' diff --git a/src/config/axios/service.ts b/src/config/axios/service.ts index 9214bf8..521e1f9 100644 --- a/src/config/axios/service.ts +++ b/src/config/axios/service.ts @@ -20,6 +20,29 @@ import { ApiEncrypt } from '@/utils/encrypt' const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE const { result_code, base_url, request_timeout } = config +const resolveDownloadFileName = (contentDisposition?: string) => { + if (!contentDisposition) { + return undefined + } + const decodeFileName = (value: string) => { + const normalizedValue = value.trim().replace(/^"(.*)"$/, '$1').replace(/\+/g, '%20') + try { + return decodeURIComponent(normalizedValue) + } catch (error) { + return normalizedValue + } + } + const filenameStarMatch = contentDisposition.match(/filename\*\s*=\s*([^;]+)/i) + if (filenameStarMatch?.[1]) { + return decodeFileName(filenameStarMatch[1].replace(/^UTF-8''/i, '')) + } + const filenameMatch = contentDisposition.match(/filename\s*=\s*([^;]+)/i) + if (filenameMatch?.[1]) { + return decodeFileName(filenameMatch[1]) + } + return undefined +} + // 需要忽略的提示。忽略后,自动 Promise.reject('error') const ignoreMsgs = [ '无效的刷新令牌', // 刷新令牌被删除时,不用提示 @@ -141,7 +164,10 @@ service.interceptors.response.use( ) { // 注意:如果导出的响应为 json,说明可能失败了,不直接返回进行下载 if (response.data.type !== 'application/json') { - return response.data + return { + data: response.data, + fileName: resolveDownloadFileName(response.headers['content-disposition']) + } } data = await new Response(response.data).json() } diff --git a/src/utils/download.ts b/src/utils/download.ts index 32fc624..e56f693 100644 --- a/src/utils/download.ts +++ b/src/utils/download.ts @@ -1,12 +1,38 @@ -const download0 = (data: Blob, fileName: string, mineType: string) => { +export interface DownloadSource { + data: BlobPart + fileName?: string +} + +const resolveDownloadPayload = (source: BlobPart | DownloadSource, fallbackFileName?: string) => { + if ( + source && + typeof source === 'object' && + !(source instanceof Blob) && + !(source instanceof ArrayBuffer) && + 'data' in source + ) { + const resolvedSource = source as DownloadSource + return { + data: resolvedSource.data, + fileName: resolvedSource.fileName || fallbackFileName || 'download' + } + } + return { + data: source as BlobPart, + fileName: fallbackFileName || 'download' + } +} + +const download0 = (source: BlobPart | DownloadSource, fileName: string | undefined, mineType: string) => { + const payload = resolveDownloadPayload(source, fileName) // 创建 blob - const blob = new Blob([data], { type: mineType }) + const blob = new Blob([payload.data], { type: mineType }) // 创建 href 超链接,点击进行下载 window.URL = window.URL || window.webkitURL const href = URL.createObjectURL(blob) const downA = document.createElement('a') downA.href = href - downA.download = fileName + downA.download = payload.fileName downA.click() // 销毁超连接 window.URL.revokeObjectURL(href) @@ -14,27 +40,27 @@ const download0 = (data: Blob, fileName: string, mineType: string) => { const download = { // 下载 Excel 方法 - excel: (data: Blob, fileName: string) => { + excel: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'application/vnd.ms-excel') }, // 下载 Word 方法 - word: (data: Blob, fileName: string) => { + word: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'application/msword') }, // 下载 Zip 方法 - zip: (data: Blob, fileName: string) => { + zip: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'application/zip') }, // 下载 Html 方法 - html: (data: Blob, fileName: string) => { + html: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'text/html') }, // 下载 Markdown 方法 - markdown: (data: Blob, fileName: string) => { + markdown: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'text/markdown') }, // 下载 Json 方法 - json: (data: Blob, fileName: string) => { + json: (data: BlobPart | DownloadSource, fileName?: string) => { download0(data, fileName, 'application/json') }, // 下载图片(允许跨域) diff --git a/src/views/tjt/employee-year-cost-budget/index.vue b/src/views/tjt/employee-year-cost-budget/index.vue index 366cb43..5e357c3 100644 --- a/src/views/tjt/employee-year-cost-budget/index.vue +++ b/src/views/tjt/employee-year-cost-budget/index.vue @@ -173,7 +173,7 @@ import type { FormRules } from 'element-plus' import * as EmployeeApi from '@/api/tjt/employee' import * as EmployeeYearCostBudgetApi from '@/api/tjt/employeeYearCostBudget' -import { EMPLOYEE_STATUS, formatAmountText } from '@/views/tjt/shared/planning' +import { formatAmountText } from '@/views/tjt/shared/planning' defineOptions({ name: 'TjtEmployeeYearCostBudget' }) @@ -251,7 +251,6 @@ const searchEmployees = async (keyword: string) => { try { employeeOptions.value = await EmployeeApi.getEmployeeSimpleList({ keyword, - status: EMPLOYEE_STATUS.active, enabledFlag: true }) ensureEmployeeOption() diff --git a/src/views/tjt/project/ProjectForm.vue b/src/views/tjt/project/ProjectForm.vue index 4694716..a24d2cb 100644 --- a/src/views/tjt/project/ProjectForm.vue +++ b/src/views/tjt/project/ProjectForm.vue @@ -401,7 +401,6 @@ const searchEmployees = async (keyword: string) => { try { employeeOptions.value = await EmployeeApi.getEmployeeSimpleList({ keyword, - status: '在职', enabledFlag: true }) ensureEmployeeOptions(formData.value.rolePersons) diff --git a/src/views/tjt/report-budget/index.vue b/src/views/tjt/report-budget/index.vue index 8c6c785..c4516b3 100644 --- a/src/views/tjt/report-budget/index.vue +++ b/src/views/tjt/report-budget/index.vue @@ -208,6 +208,28 @@ + + + + + + + + + +