From 8b66b2a5c60aec7c13a9f07b8512269d6cd8ec9f Mon Sep 17 00:00:00 2001 From: linfeng <2819853134@qq.com> Date: Tue, 25 Mar 2025 09:40:03 +0800 Subject: [PATCH 1/4] lin --- .../assessment-standardpage.tsx | 180 +++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx b/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx index e01ef41..42af287 100644 --- a/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx +++ b/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx @@ -1,3 +1,181 @@ +import { Table, Select, Form, Button, Space, InputNumber, Modal } from 'antd'; +import { useState } from 'react'; + +// 模拟接口调用函数 +const addAgeRangeApi = async (start: number, end: number | null) => { + // 这里替换为实际的接口调用 + console.log(`调用接口添加年龄范围: start=${start}, end=${end}`); +}; + +const addScoreStandardApi = async (score: number, standards: (number | null)[]) => { + // 这里替换为实际的接口调用 + console.log(`调用接口添加分数标准: score=${score}, standards=${standards}`); +}; + export default function AssessmentStandardPage() { - return
AssessmentStandardPage
; + const [form] = Form.useForm(); + const [ageRanges, setAgeRanges] = useState<{ start: number; end: number; label: string; }[]>([ + { start: 18, end: 24, label: '18-24岁' }, + { start: 25, end: 34, label: '25-34岁' }, + { start: 35, end: 44, label: '35-44岁' }, + { start: 45, end: null, label: '45岁以上' }, + ]); + const [isAgeModalVisible, setIsAgeModalVisible] = useState(false); + const [isScoreModalVisible, setIsScoreModalVisible] = useState(false); + + const columns = [ + { + title: '分数', + dataIndex: 'score', + key: 'score', + width: 100, + }, + ...ageRanges.map((range, index) => ({ + title: range.label, + dataIndex: `standard_${index}`, + key: `standard_${index}`, + render: (_: any, record: any) => ( + handleStandardChange(record.score, index, value)} + /> + ), + })), + ]; + + const handleStandardChange = (score: number, ageIndex: number, value: number | null) => { + // 处理标准值变化 + }; + + const showAgeModal = () => { + setIsAgeModalVisible(true); + }; + + const handleAgeOk = async (values: any) => { + const { start, end } = values; + await addAgeRangeApi(start, end); + const newRange = { + start, + end, + label: end ? `${start}-${end}岁` : `${start}岁以上`, + }; + setAgeRanges([...ageRanges, newRange]); + setIsAgeModalVisible(false); + }; + + const handleAgeCancel = () => { + setIsAgeModalVisible(false); + }; + + const showScoreModal = () => { + setIsScoreModalVisible(true); + }; + + const handleScoreOk = async (values: any) => { + const { score, standards } = values; + await addScoreStandardApi(score, standards); + // 这里可以更新表格的数据源 + setIsScoreModalVisible(false); + }; + + const handleScoreCancel = () => { + setIsScoreModalVisible(false); + }; + + return ( +
+

考核标准管理

+ + +
+ + + + + + + + + ); + } + + return {childNode}; + }; \ No newline at end of file diff --git a/apps/web/src/app/main/sport/page.tsx b/apps/web/src/app/main/sport/page.tsx new file mode 100644 index 0000000..3d07979 --- /dev/null +++ b/apps/web/src/app/main/sport/page.tsx @@ -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) => ( +
+ + +
+ ), + } + ]; + + 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 ( +
+ +
+
+
+
成绩总览
+
+ setSearchValue(e.target.value)} + className="pl-10 w-full border" + /> + +
+ +
+ + {isLoading ? ( +
加载中...
+ ) : ( + 'editable-row'} + columns={mergedColumns} + dataSource={filteredData} + tableLayout="fixed" + rowKey="id" + pagination={{ + position: ["bottomCenter"], + className: "flex justify-center mt-4", + pageSize: 10, + }} + /> + )} + + + + + ); +} \ No newline at end of file diff --git a/apps/web/src/app/main/staffpage/page.tsx b/apps/web/src/app/main/staffpage/page.tsx index 6a9eeed..6a7f341 100644 --- a/apps/web/src/app/main/staffpage/page.tsx +++ b/apps/web/src/app/main/staffpage/page.tsx @@ -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 (
diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index df11ea0..ca35c73 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -13,6 +13,7 @@ import MainLayout from "../app/main/layout/MainLayout"; import DailyPage from "../app/main/daily/page"; import Dashboard from "../app/main/home/page"; import WeekPlanPage from "../app/main/plan/weekplan/page"; +import SportPage from "../app/main/sport/page"; interface CustomIndexRouteObject extends IndexRouteObject { name?: string; breadcrumb?: string; @@ -84,11 +85,10 @@ export const routes: CustomRouteObject[] = [ }, { path:"sportsassessment", - element: + element: } ] }, - ], }, diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 2dd9d9e..eb29fb7 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -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; From 7b6b046305e13976200304fdd40ed36d76f1c4af Mon Sep 17 00:00:00 2001 From: linfeng <2819853134@qq.com> Date: Tue, 25 Mar 2025 09:48:35 +0800 Subject: [PATCH 3/4] lin --- packages/client/src/api/hooks/useSport.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/src/api/hooks/useSport.ts b/packages/client/src/api/hooks/useSport.ts index d0b8f42..3c71702 100644 --- a/packages/client/src/api/hooks/useSport.ts +++ b/packages/client/src/api/hooks/useSport.ts @@ -16,12 +16,12 @@ export function useSport() { }, }); - const createSportStandard = api.sportStandard.createStandard.useMutation({ - onSuccess: (result) => { - queryClient.invalidateQueries({ queryKey: queryKeyStandard }); - emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.CREATED); - }, - }); + // const createSportStandard = api.sportStandard.createStandard.useMutation({ + // onSuccess: (result) => { + // queryClient.invalidateQueries({ queryKey: queryKeyStandard }); + // emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.CREATED); + // }, + // }); return { From a022912ad0af9a57e266f863ec012c89f1036061 Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Tue, 25 Mar 2025 10:08:21 +0800 Subject: [PATCH 4/4] add --- apps/web/src/app/main/sport/page.tsx | 91 +++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/main/sport/page.tsx b/apps/web/src/app/main/sport/page.tsx index 3d07979..89d6193 100644 --- a/apps/web/src/app/main/sport/page.tsx +++ b/apps/web/src/app/main/sport/page.tsx @@ -1,11 +1,14 @@ -import { Button, Select, Table, Modal, Form, Input, InputNumber } from "antd"; -import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import { Button, Select, Table, Modal, Form, Input, InputNumber, Upload, message } from "antd"; +import { MagnifyingGlassIcon, ArrowUpTrayIcon, ArrowDownTrayIcon } 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'; +import * as XLSX from 'xlsx'; +import { UploadOutlined, DownloadOutlined } from '@ant-design/icons'; + export default function SportPage() { const { form, setVisible, searchValue, setSearchValue } = useMainContext(); const { editingRecord, setEditingRecord } = useMainContext(); @@ -326,6 +329,61 @@ export default function SportPage() { }, }; + // 导出成绩为Excel + const handleExport = () => { + // 创建工作簿 + const wb = XLSX.utils.book_new(); + + // 准备导出数据(移除id字段) + const exportData = sportsData.map(({ id, ...rest }) => rest); + + // 转换为工作表 + const ws = XLSX.utils.json_to_sheet(exportData); + + // 将工作表添加到工作簿 + XLSX.utils.book_append_sheet(wb, ws, "成绩数据"); + + // 生成Excel文件并下载 + XLSX.writeFile(wb, "体育成绩表.xlsx"); + + toast.success("成绩导出成功"); + }; + + // 导入成绩 + const handleImport = (file) => { + const reader = new FileReader(); + + reader.onload = (e) => { + try { + const data = new Uint8Array(e.target.result); + const workbook = XLSX.read(data, { type: 'array' }); + // 获取第一个工作表 + const worksheet = workbook.Sheets[workbook.SheetNames[0]]; + // 转换为JSON + const jsonData = XLSX.utils.sheet_to_json(worksheet); + // 添加id字段 + const importedData = jsonData.map((item, index) => ({ + id: sportsData.length + index + 1, + ...item, + // 确保totalScore是计算所有项目成绩的和 + totalScore: calculateTotalScore(item) + })); + + // 更新数据 + setSportsData([...sportsData, ...importedData]); + toast.success(`成功导入${importedData.length}条记录`); + } catch (error) { + console.error('导入失败:', error); + toast.error("导入失败,请检查文件格式"); + } + }; + + reader.readAsArrayBuffer(file); + + // 防止自动上传 + return false; + }; + return (
@@ -341,13 +399,28 @@ export default function SportPage() { />
- +
+ + + + + +
{isLoading ? (