diff --git a/apps/web/src/app/main/admin/deptsettingpage/page.tsx b/apps/web/src/app/main/admin/deptsettingpage/page.tsx new file mode 100644 index 0000000..8ef3ae4 --- /dev/null +++ b/apps/web/src/app/main/admin/deptsettingpage/page.tsx @@ -0,0 +1,7 @@ +export default function DeptSettingPage() { + return ( +
+

部门设置

+
+ ); +} \ No newline at end of file diff --git a/apps/web/src/app/main/layout/NavigationMenu.tsx b/apps/web/src/app/main/layout/NavigationMenu.tsx index 490cea3..4ffd306 100644 --- a/apps/web/src/app/main/layout/NavigationMenu.tsx +++ b/apps/web/src/app/main/layout/NavigationMenu.tsx @@ -44,6 +44,15 @@ const items = [ null, null, ), + getItem( + "系统设置", + "/admin", + , + [ + getItem("部门设置", "/admin/department", null, null, null), + ], + null, + ), getItem( "训练计划", "/plan", @@ -83,7 +92,8 @@ const NavigationMenu: React.FC = () => { // 添加考核成绩子路径的匹配规则 "^/assessment/positionassessment": ["/assessment"], "^/assessment/commonassessment": ["/assessment"], - "^/assessment/sportsassessment": ["/assessment"] + "^/assessment/sportsassessment": ["/assessment"], + "^/admin/department": ["/admin"], }; // 选中的菜单 @@ -109,7 +119,10 @@ const NavigationMenu: React.FC = () => { setOpenKeys(["/staff"]); } else if (path.startsWith("/assessment/") || path.startsWith("/plan/")) { setOpenKeys([path.split('/').slice(0, 2).join('/')]); - } else { + } else if(path.startsWith("/admin/")){ + setOpenKeys(["/admin"]); + } + else { setOpenKeys(openKeyMerge(path)); } diff --git a/apps/web/src/app/main/staffinformation/page.tsx b/apps/web/src/app/main/staffinformation/page.tsx index acc41e8..5bf9eaf 100644 --- a/apps/web/src/app/main/staffinformation/page.tsx +++ b/apps/web/src/app/main/staffinformation/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { Button, Form, Input, Select, DatePicker, Radio, message, Modal, Cascader } from "antd"; +import { Button, Form, Input, Select, DatePicker, Radio, message, Modal, Cascader, InputNumber } from "antd"; import { useState } from "react"; import dayjs from "dayjs"; import { useStaff } from "@nice/client"; @@ -144,7 +144,7 @@ const StaffInformation = () => { - + - + + + diff --git a/apps/web/src/app/main/staffpage/stafftable/page.tsx b/apps/web/src/app/main/staffpage/stafftable/page.tsx index 61b27a5..d89fa69 100644 --- a/apps/web/src/app/main/staffpage/stafftable/page.tsx +++ b/apps/web/src/app/main/staffpage/stafftable/page.tsx @@ -8,10 +8,10 @@ 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 } from 'antd'; +import { Modal, Input, Button, Switch } from 'antd'; import { api } from '@nice/client'; -import { StaffDto } from 'packages/common/dist'; import DepartmentSelect from '@web/src/components/models/department/department-select'; +import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-model'; // 修改函数类型定义 @@ -40,107 +40,100 @@ export default function StaffTable() { } }); const [gridApi, setGridApi] = useState(null); // 添加gridApi状态 - const [confirmVisible, setConfirmVisible] = useState(false); const [fileNameVisible, setFileNameVisible] = useState(false); const [fileName, setFileName] = useState(''); const [defaultFileName] = useState(`员工数据_${new Date().toISOString().slice(0, 10)}`); - const [exporting, setExporting] = useState(false); - const [selectedDepartment, setSelectedDepartment] = useState(''); - const [tempExportData, setTempExportData] = useState(null); - const [exportScope, setExportScope] = useState<'current' | 'department'>('current'); - const handleExport = async () => { - setConfirmVisible(true); - }; + const [paginationEnabled, setPaginationEnabled] = useState(true); const handleConfirm = async () => { - setConfirmVisible(false); - setExporting(true); - if (exportScope === 'current') { - setExporting(false); - try { - const allStaffs = await api.staff.findMany.useQuery({ - where: { - deletedAt: null, - department: { name: selectedDepartment } // 添加部门过滤条件 - }, - include: { department: true } - }); - - setTempExportData(allStaffs); - } finally { - setExporting(false); - } - } setFileNameVisible(true); }; // 添加导出处理函数 const handleFileNameConfirm = () => { - setFileNameVisible(false) - if (!tempExportData) return; - - if (!gridApi) return; - - const finalFileName = fileName || defaultFileName; - - const flattenColumns = (cols: any[]): any[] => { - return cols.flatMap(col => - col.children ? flattenColumns(col.children) : col - ); - }; - - const allColDefs = flattenColumns(gridApi.getColumnDefs()); - let rowData; - if (exportScope === 'current') { - rowData = gridApi.getDisplayedRowNodes().map((node: any) => node.data) || []; - } else { - rowData = tempExportData || []; + setFileNameVisible(false); + if (!gridApi || typeof gridApi.getRenderedNodes !== 'function') { + console.error('Grid API 未正确初始化'); + return; } - rowData.map((node: any) => { + + // 修改获取节点方式(使用更可靠的 getRenderedNodes) + const rowNodes = gridApi.getRenderedNodes(); + + // 获取所有列定义 + const flattenColumns = (cols: any[]): any[] => cols.flatMap(col => col.children ? flattenColumns(col.children) : col); + const allColDefs = flattenColumns(gridApi.getColumnDefs()); + + // 获取数据(兼容分页状态) + + // 处理数据格式 + const processRowData = (node: any) => { const row: Record = {}; allColDefs.forEach((colDef: any) => { - const field = colDef.field; - if (field) { - const value = node.data[field]; - const formatter = colDef.valueFormatter; - const renderer = colDef.cellRenderer; + if (!colDef.field || !colDef.headerName) return; - let renderedValue = value; - if (formatter) { - renderedValue = formatter({ value }); - } else if (renderer) { - const renderResult = renderer({ value }); - // 改进渲染结果处理 - if (typeof renderResult === 'string') { - renderedValue = renderResult; - } else if (renderResult?.props?.children) { // 处理React元素文本内容 - renderedValue = String(renderResult.props.children); - } else if (renderResult?.props?.dangerouslySetInnerHTML?.__html) { - const html = renderResult.props.dangerouslySetInnerHTML.__html; - renderedValue = html.replace(//gi, '\n'); - } - } + // 修改字段访问方式,支持嵌套对象 + const value = colDef.field.includes('.') + ? colDef.field.split('.').reduce((obj: any, key: string) => (obj || {})[key], node.data) + : node.data[colDef.field]; - // 增强布尔值处理逻辑 - if (typeof renderedValue === 'boolean' || - (typeof renderedValue === 'string' && ['true', 'false'].includes(renderedValue.toLowerCase()))) { - const boolValue = typeof renderedValue === 'boolean' ? renderedValue : renderedValue.toLowerCase() === 'true'; - renderedValue = boolValue ? '是' : '否'; - } + let renderedValue = value; - row[colDef.headerName] = renderedValue; + // 应用列格式化 + if (colDef.valueFormatter) { + renderedValue = colDef.valueFormatter({ value }); } + + // 处理特殊数据类型 + if (colDef.cellRenderer) { + const renderResult = colDef.cellRenderer({ value }); + if (typeof renderResult === 'string') { + renderedValue = renderResult; + } else if (renderResult?.props?.dangerouslySetInnerHTML?.__html) { + renderedValue = renderResult.props.dangerouslySetInnerHTML.__html.replace(//gi, '\n'); + } + } + + // 统一布尔值显示 + if (typeof renderedValue === 'boolean') { + renderedValue = renderedValue ? '是' : '否'; + } + + // 日期字段处理 + if (['hireDate', 'rankDate', 'seniority'].includes(colDef.field) && value) { + renderedValue = new Date(value).toLocaleDateString('zh-CN'); + } + + // 特别处理部门名称显示 + if (colDef.field === 'department.name' && renderedValue === undefined) { + renderedValue = node.data.department?.name || ''; + } + + row[colDef.headerName] = renderedValue; }); return row; - }); + }; - // 创建工作表并导出 - const ws = utils.json_to_sheet(rowData); - const wb = utils.book_new(); - utils.book_append_sheet(wb, ws, "Sheet1"); - // 修改导出文件名生成方式 - writeFile(wb, `${fileName || '未命名数据'}.xlsx`); + try { + // 生成工作表 + const rowData = rowNodes.map(processRowData); + const ws = utils.json_to_sheet(rowData); + const wb = utils.book_new(); + utils.book_append_sheet(wb, ws, "员工数据"); + + // 生成文件名 + const finalFileName = fileName || `${defaultFileName}_${paginationEnabled ? '当前页' : '全部'}`; + writeFile(wb, `${finalFileName}.xlsx`); + } catch (error) { + console.error('导出失败:', error); + } + }; + const handleResetFilters = () => { + if (gridApi) { + gridApi.setFilterModel(null); + gridApi.onFilterChanged(); // 触发筛选更新 + } }; const columnDefs: (ColDef | ColGroupDef)[] = [ @@ -169,7 +162,17 @@ export default function StaffTable() { { field: 'age', headerName: '年龄', minWidth: 80 }, { field: 'sex', headerName: '性别', minWidth: 80, - cellRenderer: (params: any) => params.value ? '男' : '女' + cellRenderer: (params: any) => { + switch (params.value) { + case true: + return '男'; + case false: + return '女'; + default: + return '未知'; + } + } + }, { field: 'bloodType', headerName: '血型', minWidth: 80 }, { @@ -197,7 +200,8 @@ export default function StaffTable() { field: 'rankDate', headerName: '衔职时间', minWidth: 120, valueFormatter: (params: any) => params.value ? new Date(params.value).toLocaleDateString() : '' }, - { field: 'proxyPosition', headerName: '代理职务', minWidth: 120 } + { field: 'proxyPosition', headerName: '代理职务', minWidth: 120 }, + {field: 'post', headerName: '岗位', minWidth: 120} ] }, { @@ -344,45 +348,30 @@ export default function StaffTable() { }} > {!isLoading && ( - - )} - - setConfirmVisible(false)} - okText="确认" - cancelText="取消" - > -
- 导出范围: - -
- - {exportScope === 'department' && ( -
-

部门名称:

- setSelectedDepartment(value)} - placeholder="请选择部门" + 导出Excel + + +
+ 启用分页: + setPaginationEnabled(checked)} + checkedChildren="开" + unCheckedChildren="关" />
- )} - +
+ )} ) : ( setGridApi(params.api)} // 添加gridApi回调 rowData={staffs} columnDefs={columnDefs} @@ -415,7 +404,7 @@ export default function StaffTable() { } }} enableCellTextSelection={true} - pagination={true} + pagination={paginationEnabled} paginationAutoPageSize={true} cacheQuickFilter={true} /> diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index a9da741..234727e 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -14,6 +14,8 @@ import DailyPage from "../app/main/daily/page"; import Dashboard from "../app/main/home/page"; import WeekPlanPage from "../app/main/plan/weekplan/page"; import StaffInformation from "../app/main/staffinformation/page"; +import SettingPage from "../app/main/admin/deptsettingpage/settingpage"; +import DeptSettingPage from "../app/main/admin/deptsettingpage/settingpage"; interface CustomIndexRouteObject extends IndexRouteObject { name?: string; breadcrumb?: string; @@ -59,6 +61,15 @@ export const routes: CustomRouteObject[] = [ path: "/staff", element: , }, + { + path: "/admin", + children: [ + { + path: "department", + element: , + }, + ] + }, { path:"/plan", children:[ diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 0d83874..ead43b0 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -449,8 +449,8 @@ model Staff { rank String? @map("rank") // 衔职级别 rankDate DateTime? @map("rank_date") // 衔职时间 proxyPosition String? @map("proxy_position") // 代理职务 - position Position? @relation("StaffPosition", fields: [positionId], references: [id]) // 岗位 - positionId String? @map("position_id") + post String? @map("post") // 岗位 + // 入职相关信息 hireDate DateTime? @map("hire_date") // 入职时间 @@ -501,7 +501,8 @@ model Staff { enrollments Enrollment[] teachedPosts PostInstructor[] ownedResources Resource[] - + position Position? @relation("StaffPosition", fields: [positionId], references: [id]) + positionId String? @map("position_id") // 系统信息 registerToken String? createdAt DateTime @default(now()) @map("created_at")