lin
This commit is contained in:
parent
2f31b1db29
commit
28aea56c12
|
@ -1,10 +1,12 @@
|
|||
import { Outlet } from "react-router-dom";
|
||||
|
||||
import MainHeader from "./MainHeader";
|
||||
import { Content } from "antd/es/layout/layout";
|
||||
import { MainProvider } from "./MainProvider";
|
||||
export default function MainLayout() {
|
||||
return (
|
||||
<>
|
||||
<MainHeader />
|
||||
<MainProvider>
|
||||
<MainHeader />
|
||||
</MainProvider>
|
||||
</>
|
||||
|
||||
)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { Prisma, StaffDto } from "@nice/common";
|
||||
import React, {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
// import { useDebounce } from "use-debounce";
|
||||
import { Form, FormInstance } from 'antd';
|
||||
|
||||
|
||||
interface MainContextType {
|
||||
searchValue?: string;
|
||||
setSearchValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
formValue?: {[key: string]: any};
|
||||
setFormValue: React.Dispatch<React.SetStateAction<{[key: string]: any}>>;
|
||||
form: FormInstance; // 新增表单实例
|
||||
visible: boolean;
|
||||
setVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
editingRecord?: StaffDto | null;
|
||||
setEditingRecord: React.Dispatch<React.SetStateAction<StaffDto | null>>;
|
||||
}
|
||||
|
||||
const MainContext = createContext<MainContextType | null>(null);
|
||||
interface MainProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function MainProvider({ children }: MainProviderProps) {
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [formValue, setFormValue] = useState<{[key: string]: any}>({});
|
||||
const [form] = Form.useForm(); // 添加AntD表单实例
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const [editingRecord, setEditingRecord] = useState<StaffDto | null>(null);
|
||||
return (
|
||||
<MainContext.Provider
|
||||
value={{
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
formValue,
|
||||
setFormValue,
|
||||
form, // 暴露表单实例
|
||||
visible,
|
||||
setVisible,
|
||||
editingRecord,
|
||||
setEditingRecord
|
||||
}}>
|
||||
<Form
|
||||
form={form}
|
||||
onValuesChange={(changed, all) => setFormValue(all)}
|
||||
initialValues={formValue}
|
||||
>
|
||||
{children}
|
||||
</Form>
|
||||
</MainContext.Provider>
|
||||
);
|
||||
}
|
||||
export const useMainContext = () => {
|
||||
const context = useContext(MainContext);
|
||||
if (!context) {
|
||||
throw new Error("useMainContext must be used within MainProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
|
@ -1,177 +1,38 @@
|
|||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import { api, useStaff } from "@nice/client";
|
||||
import { Button, Form, Input, Modal, Select, Table } from "antd";
|
||||
import { StaffDto } from "@nice/common";
|
||||
import { StaffDto, trainSituationDetailSelect } from "@nice/common";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import _ from "lodash";
|
||||
import { useMainContext } from "../layout/MainProvider";
|
||||
import StaffTable from "./stafftable/page";
|
||||
import StaffModal from "./staffmodal/page";
|
||||
|
||||
export default function StaffMessage() {
|
||||
const initialValues = {
|
||||
username: "",
|
||||
deptId: "",
|
||||
absent: "是",
|
||||
position: "",
|
||||
trainSituation: [{"": ""}],
|
||||
};
|
||||
const { create, update } = useStaff();
|
||||
const [searchName, setSearchName] = useState("");
|
||||
const { data: staffs, isLoading } = api.staff.findMany.useQuery({
|
||||
where: {
|
||||
username: {
|
||||
contains: searchName
|
||||
}
|
||||
const {form, formValue, setFormValue, setVisible, setSearchValue} = useMainContext();
|
||||
useEffect(()=>{
|
||||
setFormValue(
|
||||
{
|
||||
username: "",
|
||||
deptId: "",
|
||||
absent: false,
|
||||
position: "",
|
||||
trainSituations: "",
|
||||
}
|
||||
});
|
||||
console.log(staffs);
|
||||
const [form] = Form.useForm();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [editingRecord, setEditingRecord] = useState<StaffDto | null>(null);
|
||||
const colnums = [
|
||||
{
|
||||
title: "姓名",
|
||||
dataIndex: "username",
|
||||
key: "username",
|
||||
},
|
||||
{
|
||||
title: "部门",
|
||||
dataIndex: "deptId",
|
||||
key: "deptId",
|
||||
},
|
||||
{
|
||||
title: "职务",
|
||||
dataIndex: "position",
|
||||
key: "position",
|
||||
},
|
||||
{
|
||||
title: "在位",
|
||||
dataIndex: "absent",
|
||||
key: "absent",
|
||||
render: (_, record) => (
|
||||
<Select
|
||||
// defaultValue={record.absent ? "是" : "否"}
|
||||
style={{ width: 120 }}
|
||||
onChange={async (value) => {
|
||||
try {
|
||||
await update.mutateAsync({
|
||||
where: { id: record.id },
|
||||
data: { absent: value }
|
||||
});
|
||||
toast.success("状态更新成功");
|
||||
} catch (error) {
|
||||
toast.error("状态更新失败");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select.Option value="是">是</Select.Option>
|
||||
<Select.Option value="否">否</Select.Option>
|
||||
</Select>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "应时",
|
||||
dataIndex: "time",
|
||||
key: "time",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
render: (_, record) => (
|
||||
<Button
|
||||
type="primary"
|
||||
key={record.id}
|
||||
onClick={() => handleEdit(record)}>编辑</Button>
|
||||
),
|
||||
}
|
||||
];
|
||||
|
||||
)
|
||||
},[])
|
||||
const handleNew = () => {
|
||||
form.setFieldsValue(initialValues);
|
||||
form.setFieldsValue(formValue);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (editingRecord) {
|
||||
form.setFieldsValue(editingRecord);
|
||||
console.log(editingRecord);
|
||||
}
|
||||
}, [editingRecord]);
|
||||
|
||||
const handleEdit = (record) => {
|
||||
setEditingRecord(record);
|
||||
form.setFieldsValue(editingRecord);
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
const values = await form.getFieldsValue();
|
||||
console.log(values.username);
|
||||
try {
|
||||
if (editingRecord && editingRecord.id) {
|
||||
// console.log(editingRecord);
|
||||
const result = await update.mutateAsync(
|
||||
{
|
||||
where: {
|
||||
id: editingRecord.id,
|
||||
},
|
||||
data : {
|
||||
username: values.username,
|
||||
deptId: values.deptId,
|
||||
position: values.position,
|
||||
absent: values.absent,
|
||||
trainSituations: values.trainSituations ? {
|
||||
upsert: values.trainSituations.map((situation) => ({
|
||||
where: { id: situation.id || "" },
|
||||
update: {
|
||||
mustTrainTime: situation.mustTrainTime,
|
||||
trainContent: { connect: { id: situation.trainContentId } },
|
||||
// 其他字段...
|
||||
},
|
||||
create: {
|
||||
trainContent: { connect: { id: situation.trainContentId } },
|
||||
mustTrainTime: situation.mustTrainTime,
|
||||
staffId: editingRecord.id,
|
||||
// 其他必填字段...
|
||||
}
|
||||
}))
|
||||
} : undefined,
|
||||
updatedAt: new Date()
|
||||
} as any
|
||||
}
|
||||
);
|
||||
// console.log(result);
|
||||
} else {
|
||||
await create.mutateAsync(
|
||||
{
|
||||
data: {
|
||||
username: values.username,
|
||||
deptId: values.deptId,
|
||||
createdAt: new Date(),
|
||||
showname: values.username,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
toast.success("保存成功");
|
||||
setVisible(false);
|
||||
} catch (error) {
|
||||
toast.error("保存失败");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
// 添加防抖的搜索处理函数
|
||||
const handleSearch = useCallback(
|
||||
_.debounce((value: string) => {
|
||||
setSearchName(value);
|
||||
setSearchValue(value);
|
||||
}, 500),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-2 min-h-screen bg-gradient-to-br">
|
||||
<Form>
|
||||
|
@ -196,122 +57,8 @@ export default function StaffMessage() {
|
|||
新建人员
|
||||
</Button>
|
||||
</div>
|
||||
{/* 表格容器增加flex布局 */}
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center mt-4">
|
||||
<div className="animate-spin rounded-full h-10 w-10 border-4"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 min-h-[calc(100vh-200px)]">
|
||||
<Table
|
||||
key={"username"}
|
||||
columns={colnums}
|
||||
dataSource={staffs}
|
||||
className=" backdrop-blur-sm border-2
|
||||
[&_.ant-table-tbody>tr>td]:!text-lg
|
||||
[&_.ant-table-tbody>tr>td]:!py-5
|
||||
[&_.ant-table-thead>tr>th]:!text-lg
|
||||
[&_.ant-table-thead>tr>th]:!py-5
|
||||
h-full"
|
||||
tableLayout="fixed"
|
||||
pagination={{
|
||||
position: ["bottomCenter"],
|
||||
pageSize: 12,
|
||||
}}
|
||||
// onRow={(record) => ({
|
||||
// className: "hover:bg-gray-800/50 transition-colors even:bg-gray-800/50 hover:shadow-lg hover:shadow-blue-500/20",
|
||||
// })}
|
||||
>
|
||||
<thead className="">
|
||||
<tr>
|
||||
{colnums.map((column) => (
|
||||
<th
|
||||
key={column.key}
|
||||
className="px-4 border-b-2" // 简化样式
|
||||
>
|
||||
{column.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody> {/* 移除原有text-gray-300 */}
|
||||
{staffs?.map((record) => (
|
||||
<tr
|
||||
key={record.id}
|
||||
className="border-b border-blue-400/10"
|
||||
>
|
||||
{colnums.map((column) => (
|
||||
<td
|
||||
key={column.key}
|
||||
className="px-4 border" // 简化样式
|
||||
>
|
||||
{column.render?.(record[column.dataIndex], record) || record[column.dataIndex]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 模态框样式更新 */}
|
||||
<Modal
|
||||
title="编辑员工信息"
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={initialValues}
|
||||
>
|
||||
<Form.Item
|
||||
name={"username"}
|
||||
label="姓名"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className="rounded-lg" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={"deptId"}
|
||||
label="部门"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className=" rounded-lg" />
|
||||
</Form.Item>
|
||||
<Form.List name="trainSituations">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<div key={key} className="flex space-x-2">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'trainContentId']}
|
||||
label="培训内容"
|
||||
className="flex-1"
|
||||
>
|
||||
<Select placeholder="选择培训内容">
|
||||
{/* 这里需要从后端获取培训内容选项 */}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'mustTrainTime']}
|
||||
label="需训时长"
|
||||
className="flex-1"
|
||||
>
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
<Button onClick={() => remove(name)}>删除</Button>
|
||||
</div>
|
||||
))}
|
||||
<Button onClick={() => add()}>添加培训</Button>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form>
|
||||
</Modal>
|
||||
<StaffTable></StaffTable>
|
||||
<StaffModal></StaffModal>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
import { useStaff } from "@nice/client";
|
||||
import { useMainContext } from "../../layout/MainProvider";
|
||||
import toast from "react-hot-toast";
|
||||
import { Button, Form, Input, Modal, Select } from "antd";
|
||||
|
||||
export default function StaffModal() {
|
||||
|
||||
const { form, formValue, setVisible, visible, editingRecord } = useMainContext()
|
||||
const { create, update } = useStaff();
|
||||
const handleOk = async () => {
|
||||
const values = await form.getFieldsValue();
|
||||
console.log(values.username);
|
||||
try {
|
||||
if (editingRecord && editingRecord.id) {
|
||||
const result = await update.mutateAsync(
|
||||
{
|
||||
where: {
|
||||
id: editingRecord.id,
|
||||
},
|
||||
data: {
|
||||
username: values.username,
|
||||
deptId: values.deptId,
|
||||
position: values.position,
|
||||
absent: values.absent,
|
||||
trainSituations: values.trainSituations ? {
|
||||
upsert: values.trainSituations.map((situation) => ({
|
||||
where: { id: situation.id || "" },
|
||||
update: {
|
||||
mustTrainTime: situation.mustTrainTime,
|
||||
trainContent: { connect: { id: situation.trainContentId } },
|
||||
// 其他字段...
|
||||
},
|
||||
}))
|
||||
} : undefined,
|
||||
updatedAt: new Date()
|
||||
} as any
|
||||
}
|
||||
);
|
||||
// console.log(result);
|
||||
} else {
|
||||
await create.mutateAsync(
|
||||
{
|
||||
data: {
|
||||
username: values.username,
|
||||
deptId: values.deptId,
|
||||
createdAt: new Date(),
|
||||
showname: values.username,
|
||||
absent: values.absent,
|
||||
trainSituations: {
|
||||
create: values.trainSituations.map((situation) => ({
|
||||
trainContent: { connect: { id: situation.trainContentId } },
|
||||
mustTrainTime: situation.mustTrainTime,
|
||||
// 其他必填字段...
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
toast.success("保存成功");
|
||||
setVisible(false);
|
||||
} catch (error) {
|
||||
toast.error("保存失败");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{/* 模态框样式更新 */}
|
||||
<Modal
|
||||
title="编辑员工信息"
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={formValue}
|
||||
>
|
||||
<Form.Item
|
||||
name={"username"}
|
||||
label="姓名"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className="rounded-lg" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={"deptId"}
|
||||
label="部门"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className=" rounded-lg" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={"absent"}
|
||||
label="在位"
|
||||
>
|
||||
<Select className="rounded-lg" >
|
||||
<Select.Option value={true}>否</Select.Option>
|
||||
<Select.Option value={false}>是</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.List name="trainSituations">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<div key={key} className="flex space-x-2">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'trainContentId']}
|
||||
label="培训内容"
|
||||
className="flex-1"
|
||||
>
|
||||
<Select placeholder="选择培训内容">
|
||||
{/* 这里需要从后端获取培训内容选项 */}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'mustTrainTime']}
|
||||
label="需训时长"
|
||||
className="flex-1"
|
||||
>
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
<Button onClick={() => remove(name)}>删除</Button>
|
||||
</div>
|
||||
))}
|
||||
<Button onClick={() => add()}>添加培训</Button>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
import { Button, Select, Table } 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";
|
||||
|
||||
export default function StaffTable() {
|
||||
const{form, setVisible,searchValue} = useMainContext()
|
||||
|
||||
const { data: staffs, isLoading } = api.staff.findMany.useQuery({
|
||||
where: {
|
||||
username: {
|
||||
contains: searchValue
|
||||
}
|
||||
}
|
||||
});
|
||||
const { create, update } = useStaff();
|
||||
const {editingRecord, setEditingRecord} = useMainContext();
|
||||
const colnums = [
|
||||
{
|
||||
title: "姓名",
|
||||
dataIndex: "username",
|
||||
key: "username",
|
||||
},
|
||||
{
|
||||
title: "部门",
|
||||
dataIndex: "deptId",
|
||||
key: "deptId",
|
||||
},
|
||||
{
|
||||
title: "职务",
|
||||
dataIndex: "position",
|
||||
key: "position",
|
||||
},
|
||||
{
|
||||
title: "在位",
|
||||
dataIndex: "absent",
|
||||
key: "absent",
|
||||
render: (_, record) => (
|
||||
<Select
|
||||
// defaultValue={record.absent ? "是" : "否"}
|
||||
style={{ width: 120 }}
|
||||
onChange={async (value) => {
|
||||
try {
|
||||
await update.mutateAsync({
|
||||
where: { id: record.id },
|
||||
data: { absent: value }
|
||||
});
|
||||
toast.success("状态更新成功");
|
||||
} catch (error) {
|
||||
toast.error("状态更新失败");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select.Option value="是">是</Select.Option>
|
||||
<Select.Option value="否">否</Select.Option>
|
||||
</Select>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "应时",
|
||||
dataIndex: "trainSituation",
|
||||
key: "trainSituation",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
render: (_, record) => (
|
||||
<Button
|
||||
type="primary"
|
||||
key={record.id}
|
||||
onClick={() => handleEdit(record)}>编辑</Button>
|
||||
),
|
||||
}
|
||||
];
|
||||
useEffect(() => {
|
||||
if (editingRecord) {
|
||||
form.setFieldsValue(editingRecord);
|
||||
console.log(editingRecord);
|
||||
}
|
||||
}, [editingRecord]);
|
||||
const handleEdit = (record) => {
|
||||
setEditingRecord(record);
|
||||
form.setFieldsValue(editingRecord);
|
||||
setVisible(true);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isLoading ? (<div>加载中...</div>) :
|
||||
(
|
||||
<Table
|
||||
key={"username"}
|
||||
columns={colnums}
|
||||
dataSource={staffs}
|
||||
className="table-auto w-full border-collapse border border-gray-300"
|
||||
tableLayout="fixed"
|
||||
pagination={{
|
||||
position: ["bottomCenter"],
|
||||
className: "flex justify-center mt-4",
|
||||
pageSize: 12,
|
||||
|
||||
}}
|
||||
onRow={(record) => ({
|
||||
onMouseEnter: () => {
|
||||
const row = document.querySelector(`tr[data-row-key="${record.id}"]`);
|
||||
if (row) {
|
||||
row.classList.add("bg-gray-100");
|
||||
}
|
||||
},
|
||||
onMouseLeave: () => {
|
||||
const row = document.querySelector(`tr[data-row-key="${record.id}"]`);
|
||||
if (row) {
|
||||
row.classList.remove("bg-gray-100");
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
<thead className="bg-gray-200">
|
||||
<tr>
|
||||
{colnums.map((column) => (
|
||||
<th
|
||||
key={column.key}
|
||||
className="px-4 py-2 border border-gray-300 text-center font-bold"
|
||||
>
|
||||
{column.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{staffs?.map((record) => (
|
||||
<tr
|
||||
key={record.id}
|
||||
className="border border-gray-300"
|
||||
>
|
||||
{colnums.map((column) => (
|
||||
<td
|
||||
key={column.key}
|
||||
className="px-4 py-2 border border-gray-300 text-center"
|
||||
>
|
||||
{column.render?.(record[column.dataIndex], record) || record[column.dataIndex]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue