1、需要实现的效果

2、模板

需求:前端需要实现个人信息的Excel导出。我的实现方案是使用模板,通过读取模板,使用模板语法去写值。因为其余的库都需要写样式,我得保证样式和模板一致,使用这个库只需要写值进去,以及合并单元格。
3、安装插件
npm i exceljs-xlsx-template
4、导出的工具函数
javascript
import { renderXlsxTemplate, placeholderRange } from 'exceljs-xlsx-template'
import { userInfoAllStore } from '@/stores/userInfoAll'
const BASE_URL = import.meta.env.VITE_APP_BASE_API
export async function handleXlsxTemplate() {
const userInfoAll = userInfoAllStore()
console.log(userInfoAll.userInfoAll)
const xlsxFile = '/template.xlsx'
const data = [
{
...userInfoAll.userInfoAll
}
]
try {
const response = await fetch(xlsxFile)
if (!response.ok) {
alert('模板文件加载失败,请检查文件是否存在')
return
}
const arrayBuffer = await response.arrayBuffer()
renderXlsxTemplate(arrayBuffer, data, `${data[0]?.name}--个人基本信息.xlsx`, {
parseImage: true,
async beforeSave(workbook) {
const worksheet = workbook.getWorksheet('sheet1')
if (!worksheet) return
formatMergeCell(worksheet, data)
// 头像
const officialsealFile = data[0]?.profilePhoto ? BASE_URL + '/' + data[0]?.profilePhoto : ''
const officialsealRresponse = await fetch(officialsealFile)
if (!officialsealRresponse.ok) {
console.error(
`Failed to download image file, status code: ${officialsealRresponse.status}`
)
return
}
const officialsealArrayBuffer = await officialsealRresponse.arrayBuffer()
// 将图片添加到工作簿
const imageId = workbook.addImage({
buffer: officialsealArrayBuffer,
extension: 'png'
})
// 获取头像占位符位置信息
const range = placeholderRange(worksheet, '{{#officialseal}}')
if (range) {
// 插入图片到表格中
worksheet.addImage(imageId, {
tl: { col: range.start.col - 0.9, row: range.start.row - 0.9 },
ext: { width: 150, height: 184 }
})
}
}
})
} catch (error) {
console.error('Error processing Excel file:', error)
}
}
// 工具函数:合并单元格区域
const mergeCellRange = (worksheet, startRow, endRow, startCol = 'A', endCol = 'B') => {
const range = `${startCol}${startRow}:${endCol}${endRow}`
worksheet.unMergeCells(range)
worksheet.mergeCells(range)
return range
}
const formatMergeCell = (worksheet, data) => {
// 使用工具函数
const baseRow = 12
const familyMembersLength = data[0].familyMembers.length
const educationExperiencesLength = data[0].educationExperiences.length
const trainingCoursesLength = data[0].trainingCourses.length
const workExperiencesLength = data[0].workExperiences.length
const cadreAppointmentsLength = data[0].cadreAppointments.length
const hRPositionSettingsLength = data[0].hRPositionSettings.length
const centerPositionSettingsLength = data[0].centerPositionSettings.length
const professionalQualificationsLength = data[0].professionalQualifications.length
const papersLength = data[0].papers.length
const booksLength = data[0].books.length
const patentsLength = data[0].patents.length
const awardInfosLength = data[0].awardInfos.length
const techResearchsLength = data[0].techResearchs.length
const annualAssessmentsLength = data[0].annualAssessments.length
const awardsLength = data[0].awards.length
const punishmentsLength = data[0].punishments.length
// 合并家庭成员列
const familyRange = mergeCellRange(worksheet, baseRow, baseRow + familyMembersLength)
if (familyMembersLength < 2) {
const targetRow = baseRow + familyMembersLength
// 定义需要合并的列范围
const columnRanges = [
['F', 'G'],
['H', 'I'],
['J', 'K'],
['M', 'N']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并家庭成员区域: ${familyRange}`)
// 合并学习经历列
const educationStartRow = baseRow + familyMembersLength + 2
const educationRange = mergeCellRange(
worksheet,
educationStartRow,
educationStartRow + educationExperiencesLength
)
if (educationExperiencesLength < 2) {
const targetRow = educationStartRow + educationExperiencesLength
// 定义需要合并的列范围
const columnRanges = [
['D', 'E'],
['F', 'G'],
['H', 'I'],
['J', 'K'],
['L', 'M'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并学习经历区域: ${educationRange}`)
// 合并培训进修列
const trainingStartRow = educationStartRow + educationExperiencesLength + 1
const trainingRange = mergeCellRange(
worksheet,
trainingStartRow,
trainingStartRow + trainingCoursesLength
)
if (trainingCoursesLength < 2) {
const targetRow = trainingStartRow + trainingCoursesLength
// 定义需要合并的列范围
const columnRanges = [
['D', 'E'],
['H', 'I'],
['J', 'K']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并培训进修区域: ${trainingRange}`)
// 合并工作经历列
const workStartRow = trainingStartRow + trainingCoursesLength + 2
const workRange = mergeCellRange(worksheet, workStartRow, workStartRow + workExperiencesLength)
// 工作经历列--单行数据无需合并
console.log(`合并工作经历区域: ${workRange}`)
// 合并干部任免列
const postStartRow = workStartRow + workExperiencesLength + 1
const postRange = mergeCellRange(worksheet, postStartRow, postStartRow + cadreAppointmentsLength)
if (cadreAppointmentsLength < 2) {
const targetRow = postStartRow + cadreAppointmentsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'E'],
['F', 'H'],
['I', 'K'],
['M', 'N']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并干部任免列区域: ${postRange}`)
// 合并人社局岗位设置列
const postHrStartRow = postStartRow + cadreAppointmentsLength + 1
const postHrRange = mergeCellRange(
worksheet,
postHrStartRow,
postHrStartRow + hRPositionSettingsLength
)
if (hRPositionSettingsLength < 2) {
const targetRow = postHrStartRow + hRPositionSettingsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'F'],
['G', 'J'],
['K', 'N'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并人社局岗位设置列区域: ${postHrRange}`)
// 合并中心岗位设置列
const postCenterStartRow = postHrStartRow + hRPositionSettingsLength + 1
const postCenterRange = mergeCellRange(
worksheet,
postCenterStartRow,
postCenterStartRow + centerPositionSettingsLength
)
if (centerPositionSettingsLength < 2) {
const targetRow = postCenterStartRow + centerPositionSettingsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'E'],
['F', 'H'],
['I', 'K'],
['L', 'N'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并中心岗位设置列区域: ${postCenterRange}`)
// 合并职称职业资格列
const professionalQualificationStartRow = postCenterStartRow + centerPositionSettingsLength + 1
const professionalQualificationRange = mergeCellRange(
worksheet,
professionalQualificationStartRow,
professionalQualificationStartRow + professionalQualificationsLength
)
if (professionalQualificationsLength < 2) {
const targetRow = professionalQualificationStartRow + professionalQualificationsLength
// 定义需要合并的列范围
const columnRanges = [
['D', 'E'],
['F', 'G'],
['K', 'L'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并职称职业资格列区域: ${professionalQualificationRange}`)
// 合并论文列
const paperStartRow = professionalQualificationStartRow + professionalQualificationsLength + 2
const paperRange = mergeCellRange(worksheet, paperStartRow, paperStartRow + papersLength)
if (papersLength < 2) {
const targetRow = paperStartRow + papersLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['G', 'H'],
['M', 'N']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并论文列区域: ${paperRange}`)
// 合并专著列
const bookStartRow = paperStartRow + papersLength + 1
const bookRange = mergeCellRange(worksheet, bookStartRow, bookStartRow + booksLength)
if (booksLength < 2) {
const targetRow = bookStartRow + booksLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['F', 'G'],
['H', 'I'],
['J', 'K']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并专著列区域: ${bookRange}`)
// 合并专利列
const patentStartRow = bookStartRow + booksLength + 1
const patentRange = mergeCellRange(worksheet, patentStartRow, patentStartRow + patentsLength)
if (patentsLength < 2) {
const targetRow = patentStartRow + patentsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['E', 'F'],
['G', 'H'],
['M', 'N'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并专利列区域: ${patentRange}`)
// 合并业务获奖列
const businessAwardStartRow = patentStartRow + patentsLength + 1
const businessAwardRange = mergeCellRange(
worksheet,
businessAwardStartRow,
businessAwardStartRow + awardInfosLength
)
if (awardInfosLength < 2) {
const targetRow = businessAwardStartRow + awardInfosLength
// 定义需要合并的列范围
const columnRanges = [
['E', 'F']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并业务获奖列区域: ${businessAwardRange}`)
// 合并科技研发列
const techDevelopStartRow = businessAwardStartRow + awardInfosLength + 1
const techDevelopRange = mergeCellRange(
worksheet,
techDevelopStartRow,
techDevelopStartRow + techResearchsLength
)
if (techResearchsLength < 2) {
const targetRow = techDevelopStartRow + techResearchsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['F', 'G']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并科技研发列区域: ${techDevelopRange}`)
// 合并年度考核列
const annualAssessmentStartRow = techDevelopStartRow + techResearchsLength + 2
const annualAssessmentRange = mergeCellRange(
worksheet,
annualAssessmentStartRow,
annualAssessmentStartRow + annualAssessmentsLength
)
if (annualAssessmentsLength < 2) {
const targetRow = annualAssessmentStartRow + annualAssessmentsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'F'],
['G', 'J'],
['K', 'N'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并年度考核列区域: ${annualAssessmentRange}`)
// 合并奖励列
const rewardStartRow = annualAssessmentStartRow + annualAssessmentsLength + 1
const rewardRange = mergeCellRange(
worksheet,
rewardStartRow,
rewardStartRow + awardsLength
)
if (awardsLength < 2) {
const targetRow = rewardStartRow + awardsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['E', 'F'],
['G', 'H'],
['I', 'J'],
['K', 'L'],
['M', 'N']
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并奖励列区域: ${rewardRange}`)
// 合并处罚列
const penaltyStartRow = rewardStartRow + awardsLength + 1
const penaltyRange = mergeCellRange(
worksheet,
penaltyStartRow,
penaltyStartRow + punishmentsLength
)
if (punishmentsLength < 2) {
const targetRow = penaltyStartRow + punishmentsLength
// 定义需要合并的列范围
const columnRanges = [
['C', 'D'],
['E', 'F'],
['G', 'H'],
['I', 'J'],
['K', 'L'],
['M', 'N'],
]
// 循环合并每个列范围
columnRanges.forEach(([startCol, endCol]) => {
mergeCellRange(worksheet, targetRow, targetRow, startCol, endCol)
})
}
console.log(`合并处罚列区域: ${penaltyRange}`)
}
5、使用
直接调用即可,因为我这些数据都存储到了Pinia,直接取就行
javascript
<el-button link type="primary" size="large" @click="handleXlsxTemplate">
<el-icon :style="{ marginRight: '10px' }">
<Download /></el-icon>导出信息
</el-button>
6、数据结构
