Compare commits

...

2 Commits

Author SHA1 Message Date
Li1304553726 91e45f523f Merge branch 'main' of http://113.45.157.195:3003/linfeng/training_data 2025-03-25 09:45:48 +08:00
Li1304553726 0a3ccda369 add 2025-03-25 09:44:01 +08:00
5 changed files with 442 additions and 5 deletions

View File

@ -0,0 +1,64 @@
import React, { useContext, useEffect, useState } from "react";
import { Form,Input } from "antd";
// 创建可编辑单元格组件
export const EditableContext = React.createContext(null);
export const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
export const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const form = useContext(EditableContext);
useEffect(() => {
if (editable) {
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
}
}, [form, dataIndex, record, editable]);
const save = async () => {
try {
const values = await form.validateFields();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('保存失败:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[
{
required: true,
message: `${title}不能为空!`,
},
]}
>
<Input onPressEnter={save} onBlur={save} />
</Form.Item>
);
}
return <td {...restProps}>{childNode}</td>;
};

View File

@ -0,0 +1,375 @@
import { Button, Select, Table, Modal, Form, Input, InputNumber } from "antd";
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { useEffect, useState, useCallback, useContext } from "react";
import toast from "react-hot-toast";
import React from "react";
import _ from "lodash";
import { useMainContext } from "../layout/MainProvider";
import { EditableRow, EditableContext, EditableCell } from '../sport/context/EditableContext';
export default function SportPage() {
const { form, setVisible, searchValue, setSearchValue } = useMainContext();
const { editingRecord, setEditingRecord } = useMainContext();
// 模拟数据实际使用时应替换为API调用
const [isLoading, setIsLoading] = useState(false);
const [sportsData, setSportsData] = useState([
{
id: 1,
name: "张三",
gender: "男",
age: 25,
unit: "信息部",
threeKm: "85",
pullUp: 72,
shuttle: "65",
sitUp: 90,
bodyType: "标准",
totalScore: 85
},
{
id: 2,
name: "李四",
gender: "女",
age: 22,
unit: "市场部",
threeKm: "79",
pullUp: 58,
shuttle: "81",
sitUp: 63,
bodyType: "偏瘦",
totalScore: 78
},
{
id: 3,
name: "王五",
gender: "男",
age: 28,
unit: "技术部",
threeKm: "92",
pullUp: 85,
shuttle: "77",
sitUp: 88,
bodyType: "标准",
totalScore: 90
},
{
id: 4,
name: "赵六",
gender: "女",
age: 24,
unit: "人力资源部",
threeKm: "75",
pullUp: 56,
shuttle: "71",
sitUp: 67,
bodyType: "偏瘦",
totalScore: 75
},
{
id: 5,
name: "钱七",
gender: "男",
age: 30,
unit: "财务部",
threeKm: "68",
pullUp: 77,
shuttle: "59",
sitUp: 82,
bodyType: "偏胖",
totalScore: 82
},
{
id: 6,
name: "孙八",
gender: "男",
age: 26,
unit: "销售部",
threeKm: "93",
pullUp: 88,
shuttle: "84",
sitUp: 95,
bodyType: "标准",
totalScore: 92
},
{
id: 7,
name: "周九",
gender: "女",
age: 23,
unit: "客服部",
threeKm: "73",
pullUp: 60,
shuttle: "65",
sitUp: 75,
bodyType: "标准",
totalScore: 79
},
{
id: 8,
name: "吴十",
gender: "男",
age: 32,
unit: "研发部",
threeKm: "82",
pullUp: 70,
shuttle: "68",
sitUp: 79,
bodyType: "偏胖",
totalScore: 80
},
{
id: 9,
name: "郑十一",
gender: "女",
age: 27,
unit: "市场营销部",
threeKm: "62",
pullUp: 53,
shuttle: "69",
sitUp: 71,
bodyType: "偏瘦",
totalScore: 76
},
{
id: 10,
name: "刘十二",
gender: "男",
age: 29,
unit: "产品部",
threeKm: "87",
pullUp: 82,
shuttle: "78",
sitUp: 86,
bodyType: "标准",
totalScore: 88
}
]);
// 新增搜索功能
const filteredData = sportsData.filter(item =>
item.name.toLowerCase().includes(searchValue.toLowerCase()) ||
item.unit.toLowerCase().includes(searchValue.toLowerCase())
);
const handleNew = () => {
form.resetFields();
setEditingRecord(null);
setVisible(true);
};
// 计算总成绩的函数
const calculateTotalScore = (record) => {
// 确保所有值都转为数字
const threeKmScore = parseInt(record.threeKm, 10) || 0;
const pullUpScore = parseInt(record.pullUp, 10) || 0;
const shuttleScore = parseInt(record.shuttle, 10) || 0;
const sitUpScore = parseInt(record.sitUp, 10) || 0;
// 计算总分
return threeKmScore + pullUpScore + shuttleScore + sitUpScore;
};
// 初始化时计算所有记录的总成绩
useEffect(() => {
const updatedData = sportsData.map(record => ({
...record,
totalScore: calculateTotalScore(record)
}));
setSportsData(updatedData);
}, []);
const handleSave = (row) => {
const newData = [...sportsData];
const index = newData.findIndex(item => row.id === item.id);
const item = newData[index];
// 创建更新后的记录
const updatedRecord = { ...item, ...row };
// 重新计算总成绩
updatedRecord.totalScore = calculateTotalScore(updatedRecord);
// 更新数据
newData.splice(index, 1, updatedRecord);
setSportsData(newData);
toast.success("保存成功");
};
const columns = [
{
title: "姓名",
dataIndex: "name",
key: "name",
},
{
title: "性别",
dataIndex: "gender",
key: "gender",
},
{
title: "年龄",
dataIndex: "age",
key: "age",
},
{
title: "单位",
dataIndex: "unit",
key: "unit",
},
{
title: "三公里",
dataIndex: "threeKm",
key: "threeKm",
editable: true,
},
{
title: "单杠",
dataIndex: "pullUp",
key: "pullUp",
editable: true,
},
{
title: "30x2折返跑",
dataIndex: "shuttle",
key: "shuttle",
editable: true,
},
{
title: "仰卧卷腹",
dataIndex: "sitUp",
key: "sitUp",
editable: true,
},
{
title: "BMI",
dataIndex: "bodyType",
key: "bodyType",
editable: true,
},
{
title: "总成绩",
dataIndex: "totalScore",
key: "totalScore",
editable: true,
},
{
title: "操作",
key: "action",
render: (_, record) => (
<div className="flex space-x-2">
<Button
type="primary"
key={record.id}
onClick={() => handleEdit(record)}
>
</Button>
<Button
danger
onClick={() => handleDelete(record.id)}
>
</Button>
</div>
),
}
];
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
useEffect(() => {
if (editingRecord) {
form.setFieldsValue(editingRecord);
}
}, [editingRecord, form]);
const handleEdit = (record) => {
setEditingRecord(record);
form.setFieldsValue(record);
setVisible(true);
};
const handleDelete = (id) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除该记录吗?',
okText: '确定',
cancelText: '取消',
onOk: async () => {
try {
// 模拟删除操作
setSportsData(sportsData.filter(item => item.id !== id));
toast.success("删除成功");
} catch (error) {
console.error('删除记录时出错:', error);
toast.error("删除失败");
}
}
});
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
return (
<div className="p-2 min-h-screen bg-gradient-to-br">
<Form>
<div className="p-4 h-full flex flex-col">
<div className="max-w-full mx-auto flex-1 flex flex-col">
<div className="flex justify-between mb-4 space-x-4 items-center">
<div className="text-2xl"></div>
<div className="relative w-1/3">
<Input
placeholder="输入姓名搜索"
onChange={(e) => setSearchValue(e.target.value)}
className="pl-10 w-full border"
/>
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 " />
</div>
<Button
type="primary"
onClick={handleNew}
className="font-bold py-2 px-4 rounded"
>
</Button>
</div>
{isLoading ? (
<div>...</div>
) : (
<Table
components={components}
rowClassName={() => 'editable-row'}
columns={mergedColumns}
dataSource={filteredData}
tableLayout="fixed"
rowKey="id"
pagination={{
position: ["bottomCenter"],
className: "flex justify-center mt-4",
pageSize: 10,
}}
/>
)}
</div>
</div>
</Form>
</div>
);
}

View File

@ -5,7 +5,6 @@ import _ from "lodash";
import { useMainContext } from "../layout/MainProvider";
import StaffTable from "./stafftable/page";
import StaffModal from "./staffmodal/page";
export default function StaffMessage() {
const {form, formValue, setFormValue, setVisible, setSearchValue, editingRecord} = useMainContext();
useEffect(()=>{
@ -24,14 +23,12 @@ export default function StaffMessage() {
console.log(editingRecord);
setVisible(true);
};
const handleSearch = useCallback(
_.debounce((value: string) => {
setSearchValue(value);
}, 500),
[]
);
return (
<div className="p-2 min-h-screen bg-gradient-to-br">
<Form>

View File

@ -15,6 +15,7 @@ import Dashboard from "../app/main/home/page";
import WeekPlanPage from "../app/main/plan/weekplan/page";
import AdminLayout from "../components/layout/admin/AdminLayout";
import { adminRoute } from "./admin-route";
import SportPage from "../app/main/sport/page";
interface CustomIndexRouteObject extends IndexRouteObject {
name?: string;
breadcrumb?: string;
@ -87,7 +88,7 @@ export const routes: CustomRouteObject[] = [
},
{
path:"sportsassessment",
element:<DailyPage></DailyPage>
element:<SportPage></SportPage>
}
]
},

View File

@ -100,7 +100,7 @@ server {
# 仅供内部使用
internal;
# 代理到认证服务
proxy_pass http://192.168.252.77:3000/auth/file;
proxy_pass http://192.168.252.77:3001/auth/file;
# 请求优化:不传递请求体
proxy_pass_request_body off;