diff --git a/apps/server/src/models/sport-project/sportProject.router.ts b/apps/server/src/models/sport-project/sportProject.router.ts index cf3bef4..5b2a746 100644 --- a/apps/server/src/models/sport-project/sportProject.router.ts +++ b/apps/server/src/models/sport-project/sportProject.router.ts @@ -27,7 +27,11 @@ export class SportProjectRouter { findMany:this.trpc.procedure.input(SportProjectFindManyArgsSchema) .query(async ({input})=>{ return this.sportProjectService.findMany(input) - }) + }), + softDeleteByIds:this.trpc.procedure.input(z.object({ ids: z.array(z.string()) })) + .mutation(async ({input})=>{ + return this.sportProjectService.softDeleteByIds(input.ids) + }), }) } \ No newline at end of file diff --git a/apps/server/src/models/sport-project/sportProject.service.ts b/apps/server/src/models/sport-project/sportProject.service.ts index d45a7cf..57c19b9 100644 --- a/apps/server/src/models/sport-project/sportProject.service.ts +++ b/apps/server/src/models/sport-project/sportProject.service.ts @@ -23,7 +23,6 @@ export class sportProjectService extends BaseService = z.any() const SportStandardUpdateArgsSchema:ZodType = z.any() const SportStandardFindManyArgsSchema:ZodType = z.any() const SportStandardCreateStandardArgsSchema:ZodType = z.any() - +const SportStandardUpdateStandardArgsSchema:ZodType = z.any() interface AgeRange { start: number | null; end: number | null; @@ -48,6 +48,15 @@ export class SportStandardRouter { scoreTable: input.data.scoreTable as Record } return this.sportStandardService.createStandard(data,input.select,input.include) + }), + updateStandard:this.trpc.procedure.input(SportStandardUpdateStandardArgsSchema) + .mutation(async ({input})=>{ + const data = { + id: input.data.id as string, + ageRanges: input.data.ageRanges as any as AgeRange[], + scoreTable: input.data.scoreTable as Record + } + return this.sportStandardService.updateStandard(data) }) }) diff --git a/apps/server/src/models/sport-standard/sportStandard.service.ts b/apps/server/src/models/sport-standard/sportStandard.service.ts index 99402c3..f5dfae2 100644 --- a/apps/server/src/models/sport-standard/sportStandard.service.ts +++ b/apps/server/src/models/sport-standard/sportStandard.service.ts @@ -40,7 +40,7 @@ export class SportStandardService extends BaseService; include?: Prisma.SportStandardInclude; where: Prisma.SportStandardWhereUniqueInput; }): Promise<{ id: string; projectId: string; gender: boolean; personType: string; ageRanges: Prisma.JsonValue; scoreTable: Prisma.JsonValue; }> { + async findUnique(args: Prisma.SportStandardFindUniqueArgs) { const result = await super.findUnique(args) return result } @@ -104,6 +104,28 @@ export class SportStandardService extends BaseService { diff --git a/apps/web/src/app/admin/assessmentstandard/assessment-modal.tsx b/apps/web/src/app/admin/assessmentstandard/assessment-modal.tsx new file mode 100644 index 0000000..df958d6 --- /dev/null +++ b/apps/web/src/app/admin/assessmentstandard/assessment-modal.tsx @@ -0,0 +1,42 @@ +import { Form, InputNumber, Modal } from "antd"; +import { useAssessmentStandardContext } from "./assessment-standard-provider"; +export default function AssessmentModal() { + const { isAgeModalVisible, isScoreModalVisible, ageForm, scoreForm, handleAgeOk, handleAgeCancel, handleScoreOk, handleScoreCancel, ageRanges } = useAssessmentStandardContext(); + return ( +
+ +
+ + + + + + +
+
+ + +
+ + + + {ageRanges.map((range, index) => ( + + + + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/apps/web/src/app/admin/assessmentstandard/assessment-standard-layout.tsx b/apps/web/src/app/admin/assessmentstandard/assessment-standard-layout.tsx new file mode 100644 index 0000000..518f875 --- /dev/null +++ b/apps/web/src/app/admin/assessmentstandard/assessment-standard-layout.tsx @@ -0,0 +1,14 @@ +import AssessmentModal from "./assessment-modal"; +import SportCreateContent from "./sport-create-content"; +import StandardCreateContent from "./standard-create-content"; + +export default function AssessmentStandardLayout() { + return ( +
+

考核标准管理

+ + + +
+ ) +} \ No newline at end of file diff --git a/apps/web/src/app/admin/assessmentstandard/assessment-standard-provider.tsx b/apps/web/src/app/admin/assessmentstandard/assessment-standard-provider.tsx new file mode 100644 index 0000000..6230473 --- /dev/null +++ b/apps/web/src/app/admin/assessmentstandard/assessment-standard-provider.tsx @@ -0,0 +1,138 @@ +import React, { + createContext, + ReactNode, + useContext, + useMemo, + useState, +} from "react"; +// import { useDebounce } from "use-debounce"; +import { Form, FormInstance } from 'antd'; +import { api } from "@nice/client"; + +interface AssessmentStandardContextType { + form: FormInstance; + sportProjectList: { + id: string; + name: string; + unit: string; + }[]; + sportProjectLoading: boolean; + ageRanges: { start: number; end: number; label: string; }[]; + records: { score: number, standards: number[] }[]; + isAgeModalVisible: boolean; + isScoreModalVisible: boolean; + showAgeModal: () => void; + handleAgeOk: (values: any) => void; + handleAgeCancel: () => void; + showScoreModal: () => void; + handleScoreOk: (values: any) => void; + handleScoreCancel: () => void; + setRecords: (records: { score: number, standards: number[] }[]) => void; + setIsAgeModalVisible: (isAgeModalVisible: boolean) => void; + setIsScoreModalVisible: (isScoreModalVisible: boolean) => void; + ageForm: FormInstance; + scoreForm: FormInstance; + setAgeRanges: (ageRanges: { start: number; end: number; label: string; }[]) => void; + isStandardCreate: boolean; + setIsStandardCreate: (isStandardCreate: boolean) => void; +} + +const AssessmentStandardContext = createContext(null); +interface AssessmentStandardProviderProps { + children: ReactNode; +} + +export function AssessmentStandardProvider({ children }: AssessmentStandardProviderProps) { + const [form] = Form.useForm(); + const [ageForm] = Form.useForm(); + const [scoreForm] = Form.useForm(); + const { data: sportProjectList, isLoading: sportProjectLoading } = api.sportProject.findMany.useQuery() + const [ageRanges, setAgeRanges] = useState<{ start: number; end: number; label: string; }[]>([]); + const [records, setRecords] = useState<{ score: number, standards: number[] }[]>([]); + const [isAgeModalVisible, setIsAgeModalVisible] = useState(false); + const [isScoreModalVisible, setIsScoreModalVisible] = useState(false); + const [isStandardCreate, setIsStandardCreate] = useState(true); + // 显示年龄范围模态框 + const showAgeModal = () => { + setIsAgeModalVisible(true); + }; + + // 处理年龄范围模态框确定 + const handleAgeOk = (values: any) => { + console.log('values',values) + const { start, end } = values; + 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; + console.log(values) + const standardsArray = Object.keys(values) + .filter(key => key.startsWith('standards_')) + .map(key => values[key]); + setRecords([...records, { score, standards: standardsArray }]); + setIsScoreModalVisible(false); + }; + + // 处理分数标准模态框取消 + const handleScoreCancel = () => { + setIsScoreModalVisible(false); + }; + return ( + ({ + id: item.id, + name: item.name, + unit: item.unit + })), + sportProjectLoading, + ageRanges, + records, + isAgeModalVisible, + isScoreModalVisible, + setIsAgeModalVisible, + setIsScoreModalVisible, + showAgeModal, + handleAgeOk, + handleAgeCancel, + showScoreModal, + handleScoreOk, + handleScoreCancel, + setRecords, + ageForm, + scoreForm, + setAgeRanges, + isStandardCreate, + setIsStandardCreate + }}> + {children} + + ); +} +export const useAssessmentStandardContext = () => { + const context = useContext(AssessmentStandardContext); + if (!context) { + throw new Error("useAssessmentStandardContext must be used within AssessmentStandardProvider"); + } + return context; +}; diff --git a/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx b/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx deleted file mode 100644 index 42af287..0000000 --- a/apps/web/src/app/admin/assessmentstandard/assessment-standardpage.tsx +++ /dev/null @@ -1,181 +0,0 @@ -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() { - 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 ( -
-

考核标准管理

- - -
- - - - - - - + + + + + +
+ {sportProjectLoading ? + : +
+ {sportProjectList?.map((item) => ( +
+
{item.name}({item.unit})
+ handleDeleteProject(item.id)}>删除 +
+ ))} +
+ } + + + ) +} \ No newline at end of file diff --git a/apps/web/src/app/admin/assessmentstandard/standard-create-content.tsx b/apps/web/src/app/admin/assessmentstandard/standard-create-content.tsx new file mode 100644 index 0000000..b80f712 --- /dev/null +++ b/apps/web/src/app/admin/assessmentstandard/standard-create-content.tsx @@ -0,0 +1,165 @@ +import { Button, Form, Input, Select, Space, Table } from "antd"; +import { useAssessmentStandardContext } from "./assessment-standard-provider"; +import toast from "react-hot-toast"; +import { api, useSport } from "@nice/client"; +import { useEffect, useMemo } from "react"; +export default function StandardCreateContent() { + const {form,sportProjectList,ageRanges,records,showAgeModal,showScoreModal,setRecords,setAgeRanges,isStandardCreate,setIsStandardCreate} = useAssessmentStandardContext(); + const { createSportStandard,updateSportStandard } = useSport(); + const projectId = Form.useWatch('projectId', form); + const gender = Form.useWatch('gender', form); + const personType = Form.useWatch('personType', form); + const transformedObject = useMemo(()=>{ + return records.reduce((acc, current) => { + const scoreKey = current.score.toString(); // 将 score 转换为字符串作为键 + if (acc[scoreKey]) { + // 如果键已存在,将当前 standard 数组合并到已有的数组中 + acc[scoreKey] = acc[scoreKey].concat(current.standards); + } else { + // 如果键不存在,创建一个新的数组 + acc[scoreKey] = current.standards; + } + return acc; + }, {}); + },[records]) + 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) => ( + { + const inputValue = e.target.value; + const numericValue = inputValue ? Number(inputValue) : null; + handleStandardChange(record.score, index, numericValue); + }} + /> + ), + })), + ]; + // 处理标准值变化 + const handleStandardChange = (score: number, ageIndex: number, value: number | null) => { + const updatedRecords = records.map(record => { + if (record.score === score) { + const updatedStandards = [...record.standards]; + updatedStandards[ageIndex] = value; + return { ...record, standards: updatedStandards }; + } + return record; + }); + setRecords(updatedRecords); + }; + const handleSave = async () => { + // 转换为新格式 + if(isStandardCreate){ + await createStandard() + }else{ + await updateStandard() + } + } + const createStandard = async ()=>{ + const result = await createSportStandard.mutateAsync({ + data: { + projectId: form.getFieldsValue().projectId, + gender: form.getFieldsValue().gender, + personType: form.getFieldsValue().personType, + ageRanges: ageRanges, + scoreTable: transformedObject + } + } as any) + console.log(result) + toast.success("保存标准成功") + } + const updateStandard = async ()=>{ + const result = await updateSportStandard.mutateAsync({ + data: { + id: data[0].id, + ageRanges: ageRanges, + scoreTable: transformedObject + } + } as any) + console.log(result) + toast.success("更新标准成功") + } + const { data, isLoading } = api.sportStandard.findMany.useQuery({ + where: { + projectId: projectId || "", // 空值处理 + gender: gender ?? undefined, + personType: personType || "" + } + }, { + enabled: !!projectId && gender !== undefined && !!personType // 查询启用条件 + }); + useEffect(() => { + if (data && data.length) { + setIsStandardCreate(false) + const records: { + score: number; + standards: number[]; + }[] = Object.entries(JSON.parse(String(data[0].scoreTable))).map(([score, standards]) => ({ + score: Number(score), + standards: standards as number[] + })); + setAgeRanges(JSON.parse(String(data[0].ageRanges))) + setRecords(records) + } + }, [data]) + + + return ( +
+ + + + + + + +