From 4a111e05f9b8fa441e280e099aad21537146a498 Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Tue, 25 Mar 2025 13:28:59 +0800 Subject: [PATCH] add --- .../app/main/staffpage/stafftable/page.tsx | 287 ++++++++++-------- 1 file changed, 165 insertions(+), 122 deletions(-) diff --git a/apps/web/src/app/main/staffpage/stafftable/page.tsx b/apps/web/src/app/main/staffpage/stafftable/page.tsx index 252aec0..5b8bb17 100644 --- a/apps/web/src/app/main/staffpage/stafftable/page.tsx +++ b/apps/web/src/app/main/staffpage/stafftable/page.tsx @@ -29,7 +29,7 @@ function getAreaName(codes: string[], level?: number): string { // 修改表头提取工具函数 function extractHeaders(columns: (ColDef | ColGroupDef)[]): string[] { const result: string[] = []; - + const extractHeadersRecursive = (cols: (ColDef | ColGroupDef)[]) => { cols.forEach(col => { if ('children' in col && col.children) { @@ -39,7 +39,7 @@ function extractHeaders(columns: (ColDef | ColGroupDef)[]): string[] { } }); }; - + extractHeadersRecursive(columns); return result; } @@ -56,6 +56,7 @@ export default function StaffTable() { const [defaultFileName] = useState(`员工数据_${new Date().toISOString().slice(0, 10)}`); const [paginationEnabled, setPaginationEnabled] = useState(true); const [importVisible, setImportVisible] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); const handleConfirm = async () => { setFileNameVisible(true); @@ -165,11 +166,11 @@ export default function StaffTable() { field: 'type', headerName: '人员类型', }, - { field: 'officerId', headerName: '警号'}, + { field: 'officerId', headerName: '警号' }, { field: 'phoneNumber', headerName: '手机号' }, - { field: 'age', headerName: '年龄'}, + { field: 'age', headerName: '年龄' }, { - field: 'sex', headerName: '性别', + field: 'sex', headerName: '性别', cellRenderer: (params: any) => { switch (params.value) { case true: @@ -182,32 +183,32 @@ export default function StaffTable() { } }, - { field: 'bloodType', headerName: '血型',}, + { field: 'bloodType', headerName: '血型', }, { field: 'birthplace', headerName: '籍贯', valueFormatter: (params) => params.value ? getAreaName(params.value.split('/')) : '', }, - { field: 'source', headerName: '来源'}, + { field: 'source', headerName: '来源' }, ] }, { headerName: '政治信息', children: [ - { field: 'politicalStatus', headerName: '政治面貌', }, - { field: 'partyPosition', headerName: '党内职务', } + { field: 'politicalStatus', headerName: '政治面貌', }, + { field: 'partyPosition', headerName: '党内职务', } ] }, { headerName: '职务信息', children: [ - { field: 'department.name', headerName: '所属部门', }, - { field: 'rank', headerName: '衔职级别', }, + { field: 'department.name', headerName: '所属部门', }, + { field: 'rank', headerName: '衔职级别', }, { - field: 'rankDate', headerName: '衔职时间', + field: 'rankDate', headerName: '衔职时间', valueFormatter: (params: any) => params.value ? new Date(params.value).toLocaleDateString() : '' }, - { field: 'proxyPosition', headerName: '代理职务', }, + { field: 'proxyPosition', headerName: '代理职务', }, { field: 'post', headerName: '岗位', } ] }, @@ -222,17 +223,17 @@ export default function StaffTable() { field: 'seniority', headerName: '工龄认定时间', valueFormatter: (params: any) => params.value ? new Date(params.value).toLocaleDateString() : '' }, - { field: 'sourceType', headerName: '来源类型', }, + { field: 'sourceType', headerName: '来源类型', }, { - field: 'isReentry', headerName: '是否二次入职', + field: 'isReentry', headerName: '是否二次入职', cellRenderer: (params: any) => params.value ? '是' : '否' }, { - field: 'isExtended', headerName: '是否延期服役', + field: 'isExtended', headerName: '是否延期服役', cellRenderer: (params: any) => params.value ? '是' : '否' }, { - field: 'currentPositionDate', headerName: '现岗位开始时间', + field: 'currentPositionDate', headerName: '现岗位开始时间', valueFormatter: (params: any) => params.value ? new Date(params.value).toLocaleDateString() : '' } ] @@ -240,10 +241,10 @@ export default function StaffTable() { { headerName: '教育背景', children: [ - { field: 'education', headerName: '学历', }, + { field: 'education', headerName: '学历', }, { field: 'educationType', headerName: '学历形式', }, { - field: 'isGraduated', headerName: '是否毕业', + field: 'isGraduated', headerName: '是否毕业', cellRenderer: (params: any) => params.value ? '是' : '否' }, { field: 'major', headerName: '专业', }, @@ -253,9 +254,9 @@ export default function StaffTable() { { headerName: '培训信息', children: [ - { field: 'trainType', headerName: '培训类型', }, - { field: 'trainInstitute', headerName: '培训机构', }, - { field: 'trainMajor', headerName: '培训专业', }, + { field: 'trainType', headerName: '培训类型', }, + { field: 'trainInstitute', headerName: '培训机构', }, + { field: 'trainMajor', headerName: '培训专业', }, { field: 'hasTrain', headerName: '是否参加培训', cellRenderer: (params: any) => params.value ? '是' : '否' @@ -265,10 +266,10 @@ export default function StaffTable() { { headerName: '鉴定信息', children: [ - { field: 'certRank', headerName: '鉴定等级', }, - { field: 'certWork', headerName: '鉴定工种', }, + { field: 'certRank', headerName: '鉴定等级', }, + { field: 'certWork', headerName: '鉴定工种', }, { - field: 'hasCert', headerName: '是否参加鉴定', + field: 'hasCert', headerName: '是否参加鉴定', cellRenderer: (params: any) => params.value ? '是' : '否' } ] @@ -279,7 +280,7 @@ export default function StaffTable() { { field: 'equipment', headerName: '操作维护装备', - + cellRenderer: (params: any) => (
(
(
(
{ const headerNames = extractHeaders(columnDefs); - - // 创建一个空白行对象,键为列名,值为空字符串 + + // 创建示例数据行 + let exampleRow: Record = {}; + + // 检查是否有选中行 + if (selectedRows.length > 0) { + // 使用第一条选中的记录作为模板数据 + const templateData = selectedRows[0]; + + // 使用选中的员工数据填充示例行 + exampleRow = headerNames.reduce((obj, header) => { + // 查找表头对应的字段名 + let fieldName = ''; + let found = false; + + // 遍历列定义,查找匹配的字段 + columnDefs.forEach(colDef => { + if ('children' in colDef && colDef.children) { + colDef.children.forEach(childCol => { + if ('field' in childCol && childCol.headerName === header) { + fieldName = childCol.field; + found = true; + } + }); + } else if ('field' in colDef && colDef.headerName === header) { + fieldName = colDef.field; + found = true; + } + }); + // 如果找到了字段名,从模板数据中获取值 + if (found && fieldName && templateData[fieldName] !== undefined) { + // 根据字段类型处理值 + switch (fieldName) { + // 日期类型字段 - 格式化为字符串 + case 'hireDate': + case 'seniority': + case 'rankDate': + case 'currentPositionDate': + obj[header] = templateData[fieldName] ? + new Date(templateData[fieldName]).toISOString().split('T')[0] : + ''; + break; + // 布尔类型字段 - 转为"是"或"否" + case 'sex': + obj[header] = templateData[fieldName] === true ? '男' : + templateData[fieldName] === false ? '女' : ''; + break; + case 'enabled': + case 'isReentry': + case 'isExtended': + case 'isGraduated': + case 'hasTrain': + case 'hasCert': + obj[header] = templateData[fieldName] ? '是' : '否'; + break; + + // 其他字段 - 直接使用值 + default: + obj[header] = templateData[fieldName] !== null && + templateData[fieldName] !== undefined ? + String(templateData[fieldName]) : ''; + } + } else { + // 如果没有找到对应的值,设为空字符串 + obj[header] = ''; + } + + return obj; + }, {} as Record); + } else { + // 如果没有选中行,使用默认示例数据 + exampleRow = headerNames.reduce((obj, header) => { + // 根据不同的表头设置不同的示例值 + switch (header) { + case '姓名': obj[header] = '张三'; break; + case '身份证号': obj[header] = '110101199001011234'; break; + case '人员类型': obj[header] = '在职'; break; + case '警号': obj[header] = '012345'; break; + case '手机号': obj[header] = '13800138000'; break; + case '年龄': obj[header] = '30'; break; + case '性别': obj[header] = '男'; break; + // ... 其他默认示例值保持不变 + default: obj[header] = ''; break; + } + return obj; + }, {} as Record); + } + + // 创建空白行供用户填写 const emptyRow = headerNames.reduce((obj, header) => { obj[header] = ''; return obj; }, {} as Record); - - // 创建示例数据行 - const exampleRow = headerNames.reduce((obj, header) => { - // 根据不同的表头设置不同的示例值 - switch(header) { - // 基本信息示例 - case '姓名': obj[header] = '张三'; break; - case '身份证号': obj[header] = '110101199001011234'; break; - case '人员类型': obj[header] = '在职'; break; - case '警号': obj[header] = '012345'; break; - case '手机号': obj[header] = '13800138000'; break; - case '年龄': obj[header] = '30'; break; - case '性别': obj[header] = '男'; break; - case '血型': obj[header] = 'A型'; break; - case '籍贯': obj[header] = '北京市海淀区'; break; - case '来源': obj[header] = '社会招聘'; break; - - // 政治信息示例 - case '政治面貌': obj[header] = '党员'; break; - case '党内职务': obj[header] = '支部书记'; break; - - // 职务信息示例 - case '所属部门': obj[header] = '技术部'; break; - case '衔职级别': obj[header] = '三级警司'; break; - case '衔职时间': obj[header] = '2020-01-01'; break; - case '代理职务': obj[header] = '技术组长'; break; - case '岗位': obj[header] = '技术开发'; break; - - // 入职信息示例 - case '入职时间': obj[header] = '2015-07-01'; break; - case '工龄认定时间': obj[header] = '2015-07-01'; break; - case '来源类型': obj[header] = '招聘'; break; - case '是否二次入职': obj[header] = '否'; break; - case '是否延期服役': obj[header] = '否'; break; - case '现岗位开始时间': obj[header] = '2018-05-01'; break; - - // 教育背景示例 - case '学历': obj[header] = '本科'; break; - case '学历形式': obj[header] = '全日制'; break; - case '是否毕业': obj[header] = '是'; break; - case '专业': obj[header] = '计算机科学与技术'; break; - case '外语能力': obj[header] = '英语四级'; break; - - // 培训信息示例 - case '培训类型': obj[header] = '专业技能'; break; - case '培训机构': obj[header] = '公安大学'; break; - case '培训专业': obj[header] = '网络安全'; break; - case '是否参加培训': obj[header] = '是'; break; - - // 鉴定信息示例 - case '鉴定等级': obj[header] = '高级'; break; - case '鉴定工种': obj[header] = '信息安全'; break; - case '是否参加鉴定': obj[header] = '是'; break; - - // 工作信息示例 - case '操作维护装备': obj[header] = '服务器,网络设备,安全设备'; break; - case '演训任务经历': obj[header] = '2019年网络安全演习,2020年数据恢复演练'; break; - case '奖励信息': obj[header] = '2018年度优秀员工,2020年技术创新奖'; break; - case '处分信息': obj[header] = ''; break; - - default: obj[header] = `示例${header}`; break; - } - return obj; - }, {} as Record); - - // 创建工作簿和工作表,包含示例行和空白行 + + // 创建工作簿和工作表 const wb = utils.book_new(); - const ws = utils.json_to_sheet([exampleRow, emptyRow], { header: headerNames }); - + const ws = utils.json_to_sheet([exampleRow], { header: headerNames }); + // 设置列宽 const colWidth = headerNames.map(() => ({ wch: 20 })); ws['!cols'] = colWidth; - - // 添加一些样式表示示例数据行 - // XLSX.js 不直接支持样式,但我们可以添加注释 - const note = { t: 's', v: '以上为示例数据,请在下方行填写实际数据' }; - ws['A3'] = note; - + + // 在第二行添加提示文字 + const rowIdx = 2; // 第二行索引 + const cellRef = utils.encode_cell({ r: rowIdx, c: 0 }); // A3单元格 + + const tipText = selectedRows.length > 0 + ? '以上为选中人员数据,请在下方行填写实际数据' + : '以上为示例数据,请在下方行填写实际数据'; + + ws[cellRef] = { t: 's', v: tipText }; + + // 手动添加空白行 + utils.sheet_add_json(ws, [emptyRow], { skipHeader: true, origin: rowIdx + 1 }); + + // 合并提示文字单元格 + if (!ws['!merges']) ws['!merges'] = []; + ws['!merges'].push({ + s: { r: rowIdx, c: 0 }, // 起始单元格 A3 + e: { r: rowIdx, c: Math.min(5, headerNames.length - 1) } // 结束单元格,跨越多列 + }); + utils.book_append_sheet(wb, ws, "员工模板"); writeFile(wb, `员工数据模板_${new Date().toISOString().slice(0, 10)}.xlsx`); }; @@ -447,14 +486,14 @@ export default function StaffTable() { message.error(`导入失败: ${error.message}`); } }); - + // 处理Excel导入数据 const handleImportData = (excelData: any[]) => { // 转换Excel数据为后端接受的格式 const staffData = excelData.map(row => { // 创建一个标准的员工对象 const staff: any = {}; - + // 遍历列定义,匹配Excel中的数据 columnDefs.forEach(colDef => { if ('children' in colDef && colDef.children) { @@ -485,19 +524,19 @@ export default function StaffTable() { } } }); - + return staff; }); - + // 调用后端API保存数据 if (staffData.length > 0) { // 逐个创建员工记录 staffData.forEach(staff => { - createManyMutation.mutate({ + createManyMutation.mutate({ data: staff }); }); - + message.success(`已提交${staffData.length}条员工数据导入请求`); } else { message.warning('没有可导入的有效数据'); @@ -510,7 +549,7 @@ export default function StaffTable() { if (value === null || value === undefined || value === '') { return; } - + // 根据字段类型分别处理 switch (field) { // 字符串字段 - 确保转为字符串 @@ -548,7 +587,7 @@ export default function StaffTable() { case 'deptId': staff[field] = String(value); break; - + // 布尔字段 - 转为布尔值 case 'sex': staff[field] = value === '男' ? true : value === '女' ? false : null; @@ -565,7 +604,7 @@ export default function StaffTable() { staff[field] = Boolean(value); } break; - + // 数值字段 - 转为数字 case 'age': staff[field] = parseInt(value, 10); @@ -573,7 +612,7 @@ export default function StaffTable() { case 'order': staff[field] = parseFloat(value); break; - + // 日期字段 - 转为日期格式 case 'rankDate': case 'hireDate': @@ -583,20 +622,20 @@ export default function StaffTable() { try { // Excel日期可能以不同格式导出 let dateValue = value; - + // 如果是Excel序列号格式的日期 if (typeof value === 'number') { // Excel日期是从1900年1月1日开始的天数 // 需要转换为JavaScript日期 const excelEpoch = new Date(1899, 11, 30); dateValue = new Date(excelEpoch.getTime() + value * 86400000); - } + } // 如果是字符串格式的日期 else if (typeof value === 'string') { // 尝试解析常见日期格式 dateValue = new Date(value); } - + // 检查日期是否有效 if (dateValue instanceof Date && !isNaN(dateValue.getTime())) { staff[field] = dateValue.toISOString(); @@ -607,7 +646,7 @@ export default function StaffTable() { console.error(`日期转换错误 (${field}): ${e}`); } break; - + // 默认情况下保持原值 default: staff[field] = value; @@ -682,12 +721,12 @@ export default function StaffTable() { try { const wb = read(e.target?.result); const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); - + if (data.length === 0) { message.warning('Excel文件中没有数据'); return; } - + message.info(`读取到${data.length}条数据,正在处理...`); handleImportData(data); } catch (error) { @@ -712,7 +751,7 @@ export default function StaffTable() { ) : ( setGridApi(params.api)} // 添加gridApi回调 + onGridReady={(params) => setGridApi(params.api)} rowData={staffs} columnDefs={columnDefs} defaultColDef={{ @@ -725,6 +764,10 @@ export default function StaffTable() { pagination={paginationEnabled} paginationAutoPageSize={true} cacheQuickFilter={true} + rowSelection="single" + onSelectionChanged={(event) => { + setSelectedRows(event.api.getSelectedRows()); + }} /> )}