rht
This commit is contained in:
parent
fe0bc8d8d7
commit
656ba75ce7
|
@ -27,7 +27,11 @@ export class SportProjectRouter {
|
||||||
findMany:this.trpc.procedure.input(SportProjectFindManyArgsSchema)
|
findMany:this.trpc.procedure.input(SportProjectFindManyArgsSchema)
|
||||||
.query(async ({input})=>{
|
.query(async ({input})=>{
|
||||||
return this.sportProjectService.findMany(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)
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,7 +23,6 @@ export class sportProjectService extends BaseService<Prisma.SportProjectDelegate
|
||||||
super(db,ObjectType.SPORT_PROJECT,false);
|
super(db,ObjectType.SPORT_PROJECT,false);
|
||||||
}
|
}
|
||||||
async create(args: Prisma.SportProjectCreateArgs) {
|
async create(args: Prisma.SportProjectCreateArgs) {
|
||||||
console.log(args)
|
|
||||||
const result = await super.create(args)
|
const result = await super.create(args)
|
||||||
this.emitDataChanged(CrudOperation.CREATED,result)
|
this.emitDataChanged(CrudOperation.CREATED,result)
|
||||||
return result
|
return result
|
||||||
|
@ -41,6 +40,12 @@ export class sportProjectService extends BaseService<Prisma.SportProjectDelegate
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async softDeleteByIds(ids: string[]) {
|
||||||
|
const result = await super.softDeleteByIds(ids);
|
||||||
|
this.emitDataChanged(CrudOperation.DELETED,result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private emitDataChanged(operation: CrudOperation, data: any) {
|
private emitDataChanged(operation: CrudOperation, data: any) {
|
||||||
EventBus.emit('dataChanged', {
|
EventBus.emit('dataChanged', {
|
||||||
type:ObjectType.SPORT_STANDARD,
|
type:ObjectType.SPORT_STANDARD,
|
||||||
|
|
|
@ -8,7 +8,7 @@ const SportStandardArgsSchema:ZodType<Prisma.SportStandardCreateArgs> = z.any()
|
||||||
const SportStandardUpdateArgsSchema:ZodType<Prisma.SportStandardUpdateArgs> = z.any()
|
const SportStandardUpdateArgsSchema:ZodType<Prisma.SportStandardUpdateArgs> = z.any()
|
||||||
const SportStandardFindManyArgsSchema:ZodType<Prisma.SportStandardFindManyArgs> = z.any()
|
const SportStandardFindManyArgsSchema:ZodType<Prisma.SportStandardFindManyArgs> = z.any()
|
||||||
const SportStandardCreateStandardArgsSchema:ZodType<Prisma.SportStandardCreateArgs> = z.any()
|
const SportStandardCreateStandardArgsSchema:ZodType<Prisma.SportStandardCreateArgs> = z.any()
|
||||||
|
const SportStandardUpdateStandardArgsSchema:ZodType<Prisma.SportStandardUpdateArgs> = z.any()
|
||||||
interface AgeRange {
|
interface AgeRange {
|
||||||
start: number | null;
|
start: number | null;
|
||||||
end: number | null;
|
end: number | null;
|
||||||
|
@ -48,6 +48,15 @@ export class SportStandardRouter {
|
||||||
scoreTable: input.data.scoreTable as Record
|
scoreTable: input.data.scoreTable as Record
|
||||||
}
|
}
|
||||||
return this.sportStandardService.createStandard(data,input.select,input.include)
|
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ export class SportStandardService extends BaseService<Prisma.SportStandardDelega
|
||||||
const result = await super.findMany(args);
|
const result = await super.findMany(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
async findUnique(args: { select?: Prisma.SportStandardSelect<DefaultArgs>; include?: Prisma.SportStandardInclude<DefaultArgs>; 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)
|
const result = await super.findUnique(args)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,28 @@ export class SportStandardService extends BaseService<Prisma.SportStandardDelega
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateStandard(
|
||||||
|
data: {
|
||||||
|
id: string;
|
||||||
|
ageRanges: AgeRange[];
|
||||||
|
scoreTable: Record;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this.validateAgeRanges(data.ageRanges);
|
||||||
|
this.validateScoreTable(data.scoreTable, data.ageRanges.length);
|
||||||
|
const result = await super.update({
|
||||||
|
where: {
|
||||||
|
id: data.id
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
ageRanges: JSON.stringify(data.ageRanges),
|
||||||
|
scoreTable: JSON.stringify(data.scoreTable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.emitDataChanged(CrudOperation.UPDATED, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
public SportScoreCalculator(performance: number, age: number, scoreStandard: ScoreStandard): number {
|
public SportScoreCalculator(performance: number, age: number, scoreStandard: ScoreStandard): number {
|
||||||
// 1. 找到对应的年龄段索引
|
// 1. 找到对应的年龄段索引
|
||||||
const ageRangeIndex = scoreStandard.ageRanges.findIndex(range => {
|
const ageRangeIndex = scoreStandard.ageRanges.findIndex(range => {
|
||||||
|
|
|
@ -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 (
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
title="添加年龄范围"
|
||||||
|
visible={isAgeModalVisible}
|
||||||
|
onOk={ageForm.submit}
|
||||||
|
onCancel={handleAgeCancel}
|
||||||
|
>
|
||||||
|
<Form form={ageForm} onFinish={handleAgeOk}>
|
||||||
|
<Form.Item name="start" label="起始年龄" rules={[{ required: true }]}>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="end" label="结束年龄">
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title="添加分数与对应标准"
|
||||||
|
visible={isScoreModalVisible}
|
||||||
|
onOk={scoreForm.submit}
|
||||||
|
onCancel={handleScoreCancel}
|
||||||
|
>
|
||||||
|
<Form form={scoreForm} onFinish={handleScoreOk}>
|
||||||
|
<Form.Item name="score" label="分数" rules={[{ required: true }]}>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
{ageRanges.map((range, index) => (
|
||||||
|
<Form.Item key={index} name={`standards_${index}`} label={range.label}>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<div className="p-6">
|
||||||
|
<h1 className="text-2xl font-bold mb-6">考核标准管理</h1>
|
||||||
|
<AssessmentModal></AssessmentModal>
|
||||||
|
<SportCreateContent></SportCreateContent>
|
||||||
|
<StandardCreateContent></StandardCreateContent>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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<AssessmentStandardContextType | null>(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 (
|
||||||
|
<AssessmentStandardContext.Provider
|
||||||
|
value={{
|
||||||
|
form,
|
||||||
|
sportProjectList: sportProjectList?.map((item) => ({
|
||||||
|
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}
|
||||||
|
</AssessmentStandardContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export const useAssessmentStandardContext = () => {
|
||||||
|
const context = useContext(AssessmentStandardContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useAssessmentStandardContext must be used within AssessmentStandardProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
|
@ -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) => (
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
value={record[`standard_${index}`]}
|
|
||||||
onChange={(value) => 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 (
|
|
||||||
<div className="p-6">
|
|
||||||
<h1 className="text-2xl font-bold mb-6">考核标准管理</h1>
|
|
||||||
<Button onClick={showAgeModal} className="mb-2">添加年龄范围</Button>
|
|
||||||
<Button onClick={showScoreModal} className="mb-2 ml-2">添加分数标准</Button>
|
|
||||||
<Form form={form} layout="vertical">
|
|
||||||
<Space size="large" className="mb-6">
|
|
||||||
<Form.Item label="项目" name="projectId">
|
|
||||||
<Select
|
|
||||||
style={{ width: 200 }}
|
|
||||||
placeholder="选择考核项目"
|
|
||||||
options={[
|
|
||||||
{ value: '1', label: '引体向上' },
|
|
||||||
{ value: '2', label: '3000米跑' },
|
|
||||||
// 更多项目...
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="性别" name="gender">
|
|
||||||
<Select
|
|
||||||
style={{ width: 120 }}
|
|
||||||
placeholder="选择性别"
|
|
||||||
options={[
|
|
||||||
{ value: true, label: '男' },
|
|
||||||
{ value: false, label: '女' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="人员类型" name="personType">
|
|
||||||
<Select
|
|
||||||
style={{ width: 160 }}
|
|
||||||
placeholder="选择人员类型"
|
|
||||||
options={[
|
|
||||||
{ value: 'OFFICER', label: '警员' },
|
|
||||||
{ value: 'STAFF', label: '职工' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={[
|
|
||||||
{ score: 100, standard_0: 20, standard_1: 18, standard_2: 15, standard_3: 12 },
|
|
||||||
{ score: 90, standard_0: 18, standard_1: 16, standard_2: 13, standard_3: 10 },
|
|
||||||
{ score: 80, standard_0: 16, standard_1: 14, standard_2: 11, standard_3: 8 },
|
|
||||||
// 更多分数标准...
|
|
||||||
]}
|
|
||||||
bordered
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ x: 'max-content' }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="mt-6">
|
|
||||||
<Button type="primary">保存标准</Button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="添加年龄范围"
|
|
||||||
visible={isAgeModalVisible}
|
|
||||||
onOk={form.submit}
|
|
||||||
onCancel={handleAgeCancel}
|
|
||||||
>
|
|
||||||
<Form form={form} onFinish={handleAgeOk}>
|
|
||||||
<Form.Item name="start" label="起始年龄" rules={[{ required: true }]}>
|
|
||||||
<InputNumber min={1} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="end" label="结束年龄">
|
|
||||||
<InputNumber min={1} />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="添加分数标准"
|
|
||||||
visible={isScoreModalVisible}
|
|
||||||
onOk={form.submit}
|
|
||||||
onCancel={handleScoreCancel}
|
|
||||||
>
|
|
||||||
<Form form={form} onFinish={handleScoreOk}>
|
|
||||||
<Form.Item name="score" label="分数" rules={[{ required: true }]}>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
{ageRanges.map((range, index) => (
|
|
||||||
<Form.Item key={index} name={`standards[${index}]`} label={range.label}>
|
|
||||||
<InputNumber min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
))}
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { Button, Form, Input, Select, Skeleton } from "antd";
|
||||||
|
import { useAssessmentStandardContext } from "./assessment-standard-provider";
|
||||||
|
import { useSport } from "@nice/client";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
|
export default function SportCreateContent() {
|
||||||
|
const { form, sportProjectList, sportProjectLoading } = useAssessmentStandardContext();
|
||||||
|
const { createSportProject, softDeleteByIds } = useSport();
|
||||||
|
const handleCreateProject = async () => {
|
||||||
|
console.log(form.getFieldsValue().createProjectName)
|
||||||
|
if (form.getFieldsValue().createProjectName && form.getFieldsValue().unit) {
|
||||||
|
await createSportProject.mutateAsync({
|
||||||
|
data: {
|
||||||
|
name: form.getFieldsValue().createProjectName,
|
||||||
|
type: "sport",
|
||||||
|
unit: form.getFieldsValue().unit,
|
||||||
|
isAscending: true
|
||||||
|
}
|
||||||
|
} as any)
|
||||||
|
toast.success("创建项目成功")
|
||||||
|
form.resetFields()
|
||||||
|
} else {
|
||||||
|
toast.error("请输入项目名称或成绩单位")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleDeleteProject = async (id: string) => {
|
||||||
|
console.log(id)
|
||||||
|
await softDeleteByIds.mutateAsync({
|
||||||
|
ids: [id]
|
||||||
|
} as any)
|
||||||
|
toast.success("删除项目成功")
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<div className="flex items-center space-x-4 w-1/2">
|
||||||
|
<Form.Item label="创建项目" name="createProjectName">
|
||||||
|
<Input placeholder="请输入创建的项目名称" className="mr-2" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="成绩单位" name="unit" className='w-32'>
|
||||||
|
<Select
|
||||||
|
className='mr-2 w-24'
|
||||||
|
placeholder="请选择成绩单位"
|
||||||
|
options={[
|
||||||
|
{ value: 'time', label: '时间' },
|
||||||
|
{ value: 'count', label: '次数' },
|
||||||
|
]}
|
||||||
|
></Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Button type="primary" className='mt-10 ml-2' onClick={handleCreateProject}>创建项目</Button>
|
||||||
|
</div>
|
||||||
|
{sportProjectLoading ?
|
||||||
|
<Skeleton></Skeleton> :
|
||||||
|
<div className='w-1/3 my-3 max-h-48 overflow-y-auto'>
|
||||||
|
{sportProjectList?.map((item) => (
|
||||||
|
<div key={item.id} className='w-full flex justify-between p-4 mt-2 bg-white rounded-md'>
|
||||||
|
<div className='font-bold'>{item.name}({item.unit})</div>
|
||||||
|
<span className='text-red-500 cursor-pointer' onClick={() => handleDeleteProject(item.id)}>删除</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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) => (
|
||||||
|
<Input
|
||||||
|
style={{ width: '80px' }}
|
||||||
|
value={record.standards[index]}
|
||||||
|
onChange={(e) => {
|
||||||
|
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 (
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
|
||||||
|
<Space size="large" className="my-6">
|
||||||
|
<Form.Item label="项目" name="projectId">
|
||||||
|
<Select
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="选择考核项目"
|
||||||
|
options={sportProjectList?.map((item) => ({ value: item.id, label: `${item.name}(${item.unit})` })) || []}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="性别" name="gender">
|
||||||
|
<Select
|
||||||
|
style={{ width: 120 }}
|
||||||
|
placeholder="选择性别"
|
||||||
|
options={[
|
||||||
|
{ value: true, label: '男' },
|
||||||
|
{ value: false, label: '女' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="人员类型" name="personType">
|
||||||
|
<Select
|
||||||
|
style={{ width: 160 }}
|
||||||
|
placeholder="选择人员类型"
|
||||||
|
options={[
|
||||||
|
{ value: 'OFFICER', label: '警员' },
|
||||||
|
{ value: 'STAFF', label: '职工' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Button onClick={showAgeModal} className="mt-9">添加年龄范围</Button>
|
||||||
|
<Button onClick={showScoreModal} className="mt-9 ml-2">添加分数与对应标准</Button>
|
||||||
|
<Button type="primary" onClick={handleSave} className='mt-9 ml-2'>保存标准</Button>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={records}
|
||||||
|
bordered
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,11 +5,11 @@ import { useSport } from "@nice/client"
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const { createSportStandard } = useSport()
|
const { createSportStandard } = useSport()
|
||||||
const handleCreateSportStandard = async () => {
|
const handleCreateSportStandard = async () => {
|
||||||
await createSportStandard.mutateAsync({
|
const res = await createSportStandard.mutateAsync({
|
||||||
data: {
|
data: {
|
||||||
projectId: "cm8nsk1c0000czg9ix8d4yzml",
|
projectId: "cm8o6jzp908bp846bv513aqvo",
|
||||||
gender: true,
|
gender: true,
|
||||||
personType: "学生",
|
personType: "STAFF",
|
||||||
ageRanges: [
|
ageRanges: [
|
||||||
{ start: null, end: 24, label: "24岁以下" },
|
{ start: null, end: 24, label: "24岁以下" },
|
||||||
{ start: 24, end: 27, label: "25-27岁" },
|
{ start: 24, end: 27, label: "25-27岁" },
|
||||||
|
@ -22,7 +22,8 @@ export default function Dashboard() {
|
||||||
"90": [65, 61, 59, 57]
|
"90": [65, 61, 59, 57]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} as any)
|
} as any)
|
||||||
|
console.log(res)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div >
|
<div >
|
||||||
|
|
|
@ -15,8 +15,8 @@ import WithAuth from "../components/utils/with-auth";
|
||||||
import { CustomRouteObject } from "./types";
|
import { CustomRouteObject } from "./types";
|
||||||
import StaffPage from "../app/admin/staff/page";
|
import StaffPage from "../app/admin/staff/page";
|
||||||
import AdminLayout from "../components/layout/admin/AdminLayout";
|
import AdminLayout from "../components/layout/admin/AdminLayout";
|
||||||
import AssessmentStandardPage from "../app/admin/assessmentstandard/assessment-standardpage";
|
import { AssessmentStandardProvider } from "../app/admin/assessmentstandard/assessment-standard-provider";
|
||||||
|
import AssessmentStandardLayout from "../app/admin/assessmentstandard/assessment-standard-layout";
|
||||||
export const adminRoute: CustomRouteObject = {
|
export const adminRoute: CustomRouteObject = {
|
||||||
path: "admin",
|
path: "admin",
|
||||||
name: "系统设置",
|
name: "系统设置",
|
||||||
|
@ -83,7 +83,9 @@ export const adminRoute: CustomRouteObject = {
|
||||||
path: "assessment-standard",
|
path: "assessment-standard",
|
||||||
name: "考核标准管理",
|
name: "考核标准管理",
|
||||||
icon: <TagsOutlined />,
|
icon: <TagsOutlined />,
|
||||||
element: <AssessmentStandardPage></AssessmentStandardPage>
|
element: <AssessmentStandardProvider>
|
||||||
|
<AssessmentStandardLayout></AssessmentStandardLayout>
|
||||||
|
</AssessmentStandardProvider>
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// path: "term",
|
// path: "term",
|
||||||
|
|
|
@ -100,7 +100,7 @@ server {
|
||||||
# 仅供内部使用
|
# 仅供内部使用
|
||||||
internal;
|
internal;
|
||||||
# 代理到认证服务
|
# 代理到认证服务
|
||||||
proxy_pass http://192.168.252.77:3001/auth/file;
|
proxy_pass http://192.168.252.77:3000/auth/file;
|
||||||
|
|
||||||
# 请求优化:不传递请求体
|
# 请求优化:不传递请求体
|
||||||
proxy_pass_request_body off;
|
proxy_pass_request_body off;
|
||||||
|
|
|
@ -3,10 +3,32 @@ import { api, RouterOutputs } from "../trpc"; // Adjust path as necessary
|
||||||
import { useQueryClient, UseMutationResult } from "@tanstack/react-query";
|
import { useQueryClient, UseMutationResult } from "@tanstack/react-query";
|
||||||
import { ObjectType } from "@nice/common";
|
import { ObjectType } from "@nice/common";
|
||||||
import { CrudOperation, emitDataChange } from "../../event";
|
import { CrudOperation, emitDataChange } from "../../event";
|
||||||
|
import { UseTRPCMutationResult } from "@trpc/react-query/shared";
|
||||||
|
|
||||||
interface SportOperation {
|
interface SportOperation {
|
||||||
createSportProject: UseMutationResult<RouterOutputs["sportProject"]["create"], Error, Parameters<typeof api.sportProject.create.useMutation>[0], unknown>;
|
createSportProject: UseTRPCMutationResult<
|
||||||
createSportStandard: UseMutationResult<RouterOutputs["sportStandard"]["createStandard"], Error, Parameters<typeof api.sportStandard.createStandard.useMutation<RouterOutputs["sportStandard"]["createStandard"]>>[0], unknown>;
|
RouterOutputs["sportProject"]["create"],
|
||||||
|
Error, // 统一使用Error类型
|
||||||
|
Parameters<typeof api.sportProject.create.useMutation>[0],
|
||||||
|
unknown
|
||||||
|
>;
|
||||||
|
createSportStandard: UseTRPCMutationResult<
|
||||||
|
RouterOutputs["sportStandard"]["createStandard"],
|
||||||
|
Error, Parameters<typeof api.sportStandard.createStandard.useMutation<RouterOutputs["sportStandard"]["createStandard"]>>[0],
|
||||||
|
unknown
|
||||||
|
>;
|
||||||
|
softDeleteByIds: UseTRPCMutationResult<
|
||||||
|
RouterOutputs["sportProject"]["softDeleteByIds"],
|
||||||
|
Error,
|
||||||
|
Parameters<typeof api.sportProject.softDeleteByIds.useMutation>[0],
|
||||||
|
unknown
|
||||||
|
>;
|
||||||
|
updateSportStandard: UseTRPCMutationResult<
|
||||||
|
RouterOutputs["sportStandard"]["updateStandard"],
|
||||||
|
Error,
|
||||||
|
Parameters<typeof api.sportStandard.updateStandard.useMutation<RouterOutputs["sportStandard"]["updateStandard"]>>[0],
|
||||||
|
unknown
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
export function useSport(): SportOperation {
|
export function useSport(): SportOperation {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
@ -26,8 +48,24 @@ export function useSport(): SportOperation {
|
||||||
emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.CREATED);
|
emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.CREATED);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const softDeleteByIds = api.sportProject.softDeleteByIds.useMutation({
|
||||||
|
onSuccess: (result) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey });
|
||||||
|
emitDataChange(ObjectType.SPORT_PROJECT, result, CrudOperation.DELETED);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateSportStandard = api.sportStandard.updateStandard.useMutation<RouterOutputs["sportStandard"]["updateStandard"]>({
|
||||||
|
onSuccess: (result) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: queryKeyStandard });
|
||||||
|
emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.UPDATED);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createSportProject: createSportProject as any as UseMutationResult<RouterOutputs["sportProject"]["create"], Error, Parameters<typeof api.sportProject.create.useMutation>[0], unknown>,
|
createSportProject: createSportProject as any as UseTRPCMutationResult<RouterOutputs["sportProject"]["create"], Error, Parameters<typeof api.sportProject.create.useMutation>[0], unknown>,
|
||||||
createSportStandard: createSportStandard as any as UseMutationResult<RouterOutputs["sportStandard"]["createStandard"], Error, Parameters<typeof api.sportStandard.createStandard.useMutation<RouterOutputs["sportStandard"]["createStandard"]>>[0], unknown>,
|
createSportStandard: createSportStandard as any as UseTRPCMutationResult<RouterOutputs["sportStandard"]["createStandard"], Error, Parameters<typeof api.sportStandard.createStandard.useMutation<RouterOutputs["sportStandard"]["createStandard"]>>[0], unknown>,
|
||||||
|
softDeleteByIds: softDeleteByIds as any as UseTRPCMutationResult<RouterOutputs["sportProject"]["softDeleteByIds"], Error, Parameters<typeof api.sportProject.softDeleteByIds.useMutation>[0], unknown>,
|
||||||
|
updateSportStandard: updateSportStandard as any as UseTRPCMutationResult<RouterOutputs["sportStandard"]["updateStandard"], Error, Parameters<typeof api.sportStandard.updateStandard.useMutation<RouterOutputs["sportStandard"]["updateStandard"]>>[0], unknown>,
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -498,7 +498,7 @@ model SportProject {
|
||||||
standards SportStandard[]
|
standards SportStandard[]
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
@@map("sport_project")
|
@@map("sport_project")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,6 +514,10 @@ model SportStandard {
|
||||||
ageRanges Json @map("age_ranges") // 年龄段定义
|
ageRanges Json @map("age_ranges") // 年龄段定义
|
||||||
scoreTable Json @map("score_table") // 评分标准表
|
scoreTable Json @map("score_table") // 评分标准表
|
||||||
|
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
@@unique([projectId, gender, personType])
|
@@unique([projectId, gender, personType])
|
||||||
@@map("sport_standard")
|
@@map("sport_standard")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue