This commit is contained in:
Li1304553726 2025-03-25 13:28:59 +08:00
parent feae80d0f8
commit 4a111e05f9
1 changed files with 165 additions and 122 deletions

View File

@ -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<any[]>([]);
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) => (
<div
style={{ lineHeight: '24px' }}
@ -291,7 +292,7 @@ export default function StaffTable() {
{
field: 'projects',
headerName: '演训任务经历',
cellRenderer: (params: any) => (
<div
style={{ lineHeight: '24px' }}
@ -304,7 +305,7 @@ export default function StaffTable() {
{
field: 'awards',
headerName: '奖励信息',
cellRenderer: (params: any) => (
<div
style={{ lineHeight: '24px' }}
@ -316,7 +317,7 @@ export default function StaffTable() {
{
field: 'punishments',
headerName: '处分信息',
cellRenderer: (params: any) => (
<div
style={{ lineHeight: '24px' }}
@ -348,90 +349,128 @@ export default function StaffTable() {
// 修改导出模板处理函数
const handleExportTemplate = () => {
const headerNames = extractHeaders(columnDefs);
// 创建一个空白行对象,键为列名,值为空字符串
// 创建示例数据行
let exampleRow: Record<string, string> = {};
// 检查是否有选中行
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<string, string>);
} 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<string, string>);
}
// 创建空白行供用户填写
const emptyRow = headerNames.reduce((obj, header) => {
obj[header] = '';
return obj;
}, {} as Record<string, string>);
// 创建示例数据行
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<string, string>);
// 创建工作簿和工作表,包含示例行和空白行
// 创建工作簿和工作表
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() {
) : (
<AgGridReact
modules={[SetFilterModule, ClientSideRowModelModule]}
onGridReady={(params) => 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());
}}
/>
)}
</div>