227 lines
8.5 KiB
TypeScript
Executable File
227 lines
8.5 KiB
TypeScript
Executable File
import { Button, Select, Table, Modal } from "antd"
|
|
import { StaffDto } from "@nice/common";
|
|
import { useStaff, api } from "@nice/client";
|
|
import { useEffect, useState } from "react";
|
|
import toast from "react-hot-toast";
|
|
import React from "react";
|
|
import { useMainContext } from "../../layout/MainProvider";
|
|
import { render } from "react-dom";
|
|
|
|
// 提取处理嵌套字段的函数
|
|
const getNestedValue = (record: any, dataIndex: string | string[]) => {
|
|
if (Array.isArray(dataIndex)) {
|
|
return dataIndex.reduce((obj, key) => obj?.[key], record);
|
|
}
|
|
return record[dataIndex];
|
|
};
|
|
|
|
export default function StaffTable() {
|
|
const{form, setVisible,searchValue} = useMainContext()
|
|
|
|
const { data: staffs, isLoading } = api.staff.findMany.useQuery({
|
|
where: {
|
|
deletedAt: null,
|
|
username: {
|
|
contains: searchValue
|
|
}
|
|
},
|
|
include: {
|
|
department: true,
|
|
position: true,
|
|
trainSituations: {
|
|
include: {
|
|
trainContent: {
|
|
select: {
|
|
id: true,
|
|
title: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
useEffect(() => {
|
|
console.log(staffs);
|
|
}, [staffs]);
|
|
const { softDeleteByIds } = useStaff();
|
|
const {editingRecord, setEditingRecord} = useMainContext();
|
|
const [isTrainingModalVisible, setIsTrainingModalVisible] = useState(false);
|
|
const [selectedTrainings, setSelectedTrainings] = useState([]);
|
|
|
|
const showTrainingDetails = (situations) => {
|
|
setSelectedTrainings(situations);
|
|
setIsTrainingModalVisible(true);
|
|
};
|
|
|
|
// 修正拼写错误
|
|
const columns = [
|
|
{
|
|
title: "姓名",
|
|
dataIndex: "username",
|
|
key: "username",
|
|
},
|
|
{
|
|
title: "部门",
|
|
dataIndex: ["department", "name"],
|
|
key: "deptId",
|
|
render: (_, record) => record.department?.name || "无部门"
|
|
},
|
|
{
|
|
title: "职务",
|
|
dataIndex: ["position", "type"],
|
|
key: "position",
|
|
render: (_, record) => record.position?.type || "无职务"
|
|
},
|
|
{
|
|
title: "在位",
|
|
dataIndex: "absent",
|
|
key: "absent",
|
|
render: (absent) => absent === null ? "未知" : absent ? "否" : "是"
|
|
},
|
|
{
|
|
title: "培训情况",
|
|
dataIndex: "trainSituations",
|
|
key: "trainSituations",
|
|
render: (situations) => {
|
|
if (!situations?.length) return "无培训记录";
|
|
|
|
return (
|
|
<div>
|
|
{situations.slice(0, 2).map((s) => (
|
|
<div key={s.id} className="mb-1">
|
|
<span>{s.trainContent?.title}: </span>
|
|
<span>已完成 {s.alreadyTrainTime}/{s.mustTrainTime} 小时</span>
|
|
</div>
|
|
))}
|
|
{situations.length > 2 && (
|
|
<span
|
|
className="text-blue-500 cursor-pointer hover:underline"
|
|
onClick={() => showTrainingDetails(situations)}
|
|
>
|
|
还有 {situations.length - 2} 条记录
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
},
|
|
{
|
|
title: "操作",
|
|
key: "action",
|
|
render: (_, record) => (
|
|
<div className="flex space-x-2">
|
|
<Button
|
|
type="primary"
|
|
key={record.id}
|
|
onClick={() => handleEdit(record)}
|
|
>
|
|
编辑
|
|
</Button>
|
|
<Button
|
|
danger
|
|
onClick={async () => {
|
|
if (confirm("确定要删除该员工吗?")) {
|
|
try {
|
|
await softDeleteByIds.mutateAsync({
|
|
ids: [record.id],
|
|
});
|
|
toast.success("删除成功");
|
|
} catch (error) {
|
|
// 添加更详细的错误日志
|
|
console.error('删除员工时出错:', error);
|
|
toast.error("删除失败");
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
),
|
|
}
|
|
];
|
|
useEffect(() => {
|
|
if (editingRecord) {
|
|
form.setFieldsValue(editingRecord);
|
|
console.log(editingRecord);
|
|
}
|
|
}, [editingRecord]);
|
|
const handleEdit = (record) => {
|
|
setEditingRecord(record);
|
|
form.setFieldsValue(record); // 修正为设置当前记录的值
|
|
setVisible(true);
|
|
};
|
|
return (
|
|
<>
|
|
{
|
|
isLoading ? (<div>加载中...</div>) :
|
|
(
|
|
<Table
|
|
key={"username"}
|
|
columns={columns}
|
|
dataSource={staffs}
|
|
tableLayout="fixed"
|
|
pagination={{
|
|
position: ["bottomCenter"],
|
|
className: "flex justify-center mt-4",
|
|
pageSize: 10,
|
|
|
|
}}
|
|
>
|
|
<thead >
|
|
<tr>
|
|
{columns.map((column) => (
|
|
<th
|
|
key={column.key}
|
|
>
|
|
{column.title}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{staffs?.map((record) => (
|
|
<tr
|
|
key={record.id}
|
|
>
|
|
{columns.map((column) => (
|
|
// 使用提取的函数处理嵌套字段
|
|
<td key={column.key}>
|
|
{column.render?.(
|
|
getNestedValue(record, column.dataIndex),
|
|
record
|
|
) || getNestedValue(record, column.dataIndex)}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</Table>
|
|
)
|
|
}
|
|
|
|
<Modal
|
|
title="培训详情"
|
|
open={isTrainingModalVisible}
|
|
onCancel={() => setIsTrainingModalVisible(false)}
|
|
footer={null}
|
|
width={600}
|
|
>
|
|
<div className="max-h-96 overflow-y-auto">
|
|
{selectedTrainings.map((training) => (
|
|
<div key={training.id} className="mb-4 p-4 border rounded">
|
|
<h3 className="font-bold mb-2">{training.trainContent?.title}</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>必修时长: {training.mustTrainTime} 小时</div>
|
|
<div>已完成时长: {training.alreadyTrainTime} 小时</div>
|
|
<div>完成率: {((training.alreadyTrainTime / training.mustTrainTime) * 100).toFixed(1)}%</div>
|
|
{/* <div>得分: {training.score}</div> */}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
);
|
|
}
|