diff --git a/apps/web/src/app/main/staffpage/stafftable/page.tsx b/apps/web/src/app/main/staffpage/stafftable/page.tsx index d89fa69..780f308 100644 --- a/apps/web/src/app/main/staffpage/stafftable/page.tsx +++ b/apps/web/src/app/main/staffpage/stafftable/page.tsx @@ -7,33 +7,45 @@ import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; import { areaOptions } from '@web/src/app/main/staffinformation/area-options'; import type { CascaderProps } from 'antd/es/cascader'; -import { utils, writeFile } from 'xlsx'; -import { Modal, Input, Button, Switch } from 'antd'; +import { utils, writeFile, read } from 'xlsx'; +import { Modal, Input, Button, Switch, Upload, message } from 'antd'; import { api } from '@nice/client'; import DepartmentSelect from '@web/src/components/models/department/department-select'; import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-model'; - +import { UploadOutlined } from '@ant-design/icons'; // 修改函数类型定义 - +type ExcelColumn = { header: string; key: string }; function getAreaName(codes: string[], level?: number): string { const result: string[] = []; let currentLevel: CascaderProps['options'] = areaOptions; - for (const code of codes) { const found = currentLevel?.find(opt => opt.value === code); if (!found) break; - result.push(String(found.label)); currentLevel = found.children || []; if (level && result.length >= level) break; // 添加层级控制 } - - return level ? result[level - 1] || '' : result.join(' / ') || codes.join('/'); } - +// 修改表头提取工具函数 +function extractHeaders(columns: (ColDef | ColGroupDef)[]): string[] { + const result: string[] = []; + + const extractHeadersRecursive = (cols: (ColDef | ColGroupDef)[]) => { + cols.forEach(col => { + if ('children' in col && col.children) { + extractHeadersRecursive(col.children); + } else if (col.headerName) { + result.push(col.headerName); + } + }); + }; + + extractHeadersRecursive(columns); + return result; +} export default function StaffTable() { - const { data: staffs, isLoading } = api.staff.findMany.useQuery({ + const { data: staffs, isLoading, refetch } = api.staff.findMany.useQuery({ where: { deletedAt: null }, include: { // 添加关联查询 department: true @@ -44,12 +56,12 @@ export default function StaffTable() { const [fileName, setFileName] = useState(''); const [defaultFileName] = useState(`员工数据_${new Date().toISOString().slice(0, 10)}`); const [paginationEnabled, setPaginationEnabled] = useState(true); + const [importVisible, setImportVisible] = useState(false); const handleConfirm = async () => { setFileNameVisible(true); }; - // 添加导出处理函数 const handleFileNameConfirm = () => { setFileNameVisible(false); @@ -201,7 +213,7 @@ export default function StaffTable() { valueFormatter: (params: any) => params.value ? new Date(params.value).toLocaleDateString() : '' }, { field: 'proxyPosition', headerName: '代理职务', minWidth: 120 }, - {field: 'post', headerName: '岗位', minWidth: 120} + { field: 'post', headerName: '岗位', minWidth: 120 } ] }, { @@ -338,6 +350,99 @@ export default function StaffTable() { wrapText: true, autoHeight: true }; + // 修改导出模板处理函数 + const handleExportTemplate = () => { + const headerNames = extractHeaders(columnDefs); + + // 创建一个对象,键为列名,值为空字符串 + const templateRow = headerNames.reduce((obj, header) => { + obj[header] = ''; + return obj; + }, {} as Record); + + // 创建工作簿和工作表 + const wb = utils.book_new(); + const ws = utils.json_to_sheet([templateRow], { header: headerNames }); + + // 设置列宽 + const colWidth = headerNames.map(() => ({ wch: 20 })); + ws['!cols'] = colWidth; + + utils.book_append_sheet(wb, ws, "员工模板"); + writeFile(wb, `员工数据模板_${new Date().toISOString().slice(0, 10)}.xlsx`); + }; + + // 添加导入API钩子 + const createManyMutation = api.staff.create.useMutation({ + onSuccess: () => { + message.success('员工数据导入成功'); + refetch(); // 刷新表格数据 + setImportVisible(false); + }, + onError: (error) => { + 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) { + colDef.children.forEach(childCol => { + if ('field' in childCol && childCol.headerName) { + // 使用表头名称查找Excel数据 + const value = row[childCol.headerName]; + if (value !== undefined) { + // 处理嵌套属性 (如 department.name) + if (childCol.field.includes('.')) { + const [parent, child] = childCol.field.split('.'); + // 对于department.name特殊处理 + if (parent === 'department' && child === 'name') { + // 仅存储部门名称,后续可处理 + staff.departmentName = value; + } + } else { + // 性别特殊处理 + if (childCol.field === 'sex') { + staff[childCol.field] = value === '男' ? true : value === '女' ? false : null; + } else { + staff[childCol.field] = value; + } + } + } + } + }); + } else if ('field' in colDef && colDef.headerName) { + const value = row[colDef.headerName]; + if (value !== undefined) { + staff[colDef.field] = value; + } + } + }); + + return staff; + }); + + // 调用后端API保存数据 + if (staffData.length > 0) { + // 逐个创建员工记录 + staffData.forEach(staff => { + createManyMutation.mutate({ + data: staff // 直接传递正确格式的员工数据对象 + }); + }); + + message.success(`已提交${staffData.length}条员工数据导入请求`); + } else { + message.warning('没有可导入的有效数据'); + } + }; return (
导出Excel + + + +
+ 提示:请使用导出模板功能获取标准模板,按格式填写数据后导入 +
+ {isLoading ? (
加载中...
diff --git a/apps/web/src/components/models/department/department-select.tsx b/apps/web/src/components/models/department/department-select.tsx index 79cb7f4..eb7c54f 100755 --- a/apps/web/src/components/models/department/department-select.tsx +++ b/apps/web/src/components/models/department/department-select.tsx @@ -53,7 +53,6 @@ export default function DepartmentSelect({ }, [utils] ); - const fetchDepts = useCallback(async () => { try { const rootDepts =