Merge branch 'main' of http://113.45.157.195:3003/linfeng/training_data
This commit is contained in:
commit
91e45f523f
|
@ -0,0 +1,9 @@
|
|||
import { Controller, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from '@server/auth/auth.guard';
|
||||
import { sportProjectService } from "./sportProject.service";
|
||||
|
||||
@Controller('sportProject')
|
||||
export class sportProjectController {
|
||||
constructor(private readonly sportProjectService: sportProjectService) {}
|
||||
//@UseGuards(AuthGuard)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { StaffModule } from '../staff/staff.module';
|
||||
import { TrpcService } from '@server/trpc/trpc.service';
|
||||
import { sportProjectService } from './sportProject.service';
|
||||
import { sportProjectController } from './sportProject.controller';
|
||||
import { SportProjectRouter } from './sportProject.router';
|
||||
@Module({
|
||||
imports: [StaffModule],
|
||||
controllers: [sportProjectController],
|
||||
providers: [sportProjectService,SportProjectRouter,TrpcService],
|
||||
exports: [sportProjectService,SportProjectRouter],
|
||||
})
|
||||
export class SportProjectModule {}
|
|
@ -0,0 +1,33 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { TrpcService } from "@server/trpc/trpc.service";
|
||||
import { sportProjectService } from "./sportProject.service";
|
||||
import { z, ZodType } from "zod";
|
||||
import { Prisma } from "@nice/common";
|
||||
|
||||
const SportProjectArgsSchema:ZodType<Prisma.SportProjectCreateArgs> = z.any()
|
||||
const SportProjectUpdateArgsSchema:ZodType<Prisma.SportProjectUpdateArgs> = z.any()
|
||||
const SportProjectFindManyArgsSchema:ZodType<Prisma.SportProjectFindManyArgs> = z.any()
|
||||
@Injectable()
|
||||
export class SportProjectRouter {
|
||||
constructor(
|
||||
private readonly trpc: TrpcService,
|
||||
private readonly sportProjectService: sportProjectService,
|
||||
) { }
|
||||
|
||||
router = this.trpc.router({
|
||||
create:this.trpc.procedure.input(SportProjectArgsSchema)
|
||||
.mutation(async ({input})=>{
|
||||
console.log(input)
|
||||
return this.sportProjectService.create(input)
|
||||
}),
|
||||
update:this.trpc.procedure.input(SportProjectUpdateArgsSchema)
|
||||
.mutation(async ({input})=>{
|
||||
return this.sportProjectService.update(input)
|
||||
}),
|
||||
findMany:this.trpc.procedure.input(SportProjectFindManyArgsSchema)
|
||||
.query(async ({input})=>{
|
||||
return this.sportProjectService.findMany(input)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { BaseService } from "../base/base.service";
|
||||
import { db, ObjectType, Prisma, UserProfile } from "@nice/common";
|
||||
import EventBus, { CrudOperation } from "@server/utils/event-bus";
|
||||
import { DefaultArgs } from "@prisma/client/runtime/library";
|
||||
|
||||
interface AgeRange {
|
||||
start: number | null;
|
||||
end: number | null;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface ScoreStandard {
|
||||
ageRanges: AgeRange[];
|
||||
scoreTable: {
|
||||
[score: string]: number[];
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class sportProjectService extends BaseService<Prisma.SportProjectDelegate> {
|
||||
constructor() {
|
||||
super(db,ObjectType.SPORT_PROJECT,false);
|
||||
}
|
||||
async create(args: Prisma.SportProjectCreateArgs) {
|
||||
console.log(args)
|
||||
const result = await super.create(args)
|
||||
this.emitDataChanged(CrudOperation.CREATED,result)
|
||||
return result
|
||||
}
|
||||
|
||||
async update(args:Prisma.SportProjectUpdateArgs){
|
||||
const result = await super.update(args)
|
||||
this.emitDataChanged(CrudOperation.UPDATED,result)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
async findMany(args: Prisma.SportProjectFindManyArgs) {
|
||||
const result = await super.findMany(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
private emitDataChanged(operation: CrudOperation, data: any) {
|
||||
EventBus.emit('dataChanged', {
|
||||
type:ObjectType.SPORT_STANDARD,
|
||||
operation,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { Controller, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from '@server/auth/auth.guard';
|
||||
import { SportStandardService } from "./sportStandard.service";
|
||||
|
||||
@Controller('sportStandard')
|
||||
export class SportStandardController {
|
||||
constructor(private readonly sportStandardService: SportStandardService) {}
|
||||
//@UseGuards(AuthGuard)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { StaffModule } from '../staff/staff.module';
|
||||
import { TrpcService } from '@server/trpc/trpc.service';
|
||||
import { SportStandardService } from './sportStandard.service';
|
||||
import { SportStandardController } from './sportStandard.controller';
|
||||
import { SportStandardRouter } from './sportStandard.router';
|
||||
@Module({
|
||||
imports: [StaffModule],
|
||||
controllers: [SportStandardController],
|
||||
providers: [SportStandardService,SportStandardRouter,TrpcService],
|
||||
exports: [SportStandardService,SportStandardRouter],
|
||||
})
|
||||
export class SportStandardModule {}
|
|
@ -0,0 +1,54 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { TrpcService } from "@server/trpc/trpc.service";
|
||||
import { SportStandardService } from "./sportStandard.service";
|
||||
import { z, ZodType } from "zod";
|
||||
import { Prisma } from "@nice/common";
|
||||
|
||||
const SportStandardArgsSchema:ZodType<Prisma.SportStandardCreateArgs> = z.any()
|
||||
const SportStandardUpdateArgsSchema:ZodType<Prisma.SportStandardUpdateArgs> = z.any()
|
||||
const SportStandardFindManyArgsSchema:ZodType<Prisma.SportStandardFindManyArgs> = z.any()
|
||||
const SportStandardCreateStandardArgsSchema:ZodType<Prisma.SportStandardCreateArgs> = z.any()
|
||||
|
||||
interface AgeRange {
|
||||
start: number | null;
|
||||
end: number | null;
|
||||
label: string;
|
||||
}
|
||||
interface Record {
|
||||
[key: string]: number[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SportStandardRouter {
|
||||
constructor(
|
||||
private readonly trpc: TrpcService,
|
||||
private readonly sportStandardService: SportStandardService,
|
||||
) { }
|
||||
|
||||
router = this.trpc.router({
|
||||
// create:this.trpc.procedure.input(SportStandardArgsSchema)
|
||||
// .mutation(async ({input})=>{
|
||||
// return this.sportStandardService.create(input)
|
||||
// }),
|
||||
update:this.trpc.procedure.input(SportStandardUpdateArgsSchema)
|
||||
.mutation(async ({input})=>{
|
||||
return this.sportStandardService.update(input)
|
||||
}),
|
||||
findMany:this.trpc.procedure.input(SportStandardFindManyArgsSchema)
|
||||
.query(async ({input})=>{
|
||||
return this.sportStandardService.findMany(input)
|
||||
}),
|
||||
createStandard:this.trpc.procedure.input(SportStandardCreateStandardArgsSchema)
|
||||
.mutation(async ({input})=>{
|
||||
const data = {
|
||||
projectId: input.data.projectId,
|
||||
gender: input.data.gender,
|
||||
personType: input.data.personType,
|
||||
ageRanges: input.data.ageRanges as any as AgeRange[],
|
||||
scoreTable: input.data.scoreTable as Record
|
||||
}
|
||||
return this.sportStandardService.createStandard(data,input.select,input.include)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { BaseService } from "../base/base.service";
|
||||
import { db, ObjectType, Prisma, UserProfile } from "@nice/common";
|
||||
import EventBus, { CrudOperation } from "@server/utils/event-bus";
|
||||
import { DefaultArgs } from "@prisma/client/runtime/library";
|
||||
|
||||
interface AgeRange {
|
||||
start: number | null;
|
||||
end: number | null;
|
||||
label: string;
|
||||
}
|
||||
interface Record {
|
||||
[key: string]: number[];
|
||||
}
|
||||
interface ScoreStandard {
|
||||
ageRanges: AgeRange[];
|
||||
scoreTable: Record;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SportStandardService extends BaseService<Prisma.SportStandardDelegate> {
|
||||
constructor() {
|
||||
super(db, ObjectType.SPORT_STANDARD, true);
|
||||
}
|
||||
async create(args: Prisma.SportStandardCreateArgs) {
|
||||
console.log(args)
|
||||
const result = await super.create(args)
|
||||
this.emitDataChanged(CrudOperation.CREATED, result)
|
||||
return result
|
||||
}
|
||||
|
||||
async update(args: Prisma.SportStandardUpdateArgs) {
|
||||
const result = await super.update(args)
|
||||
this.emitDataChanged(CrudOperation.UPDATED, result)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
async findMany(args: Prisma.SportStandardFindManyArgs) {
|
||||
const result = await super.findMany(args);
|
||||
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; }> {
|
||||
const result = await super.findUnique(args)
|
||||
return result
|
||||
}
|
||||
|
||||
private emitDataChanged(operation: CrudOperation, data: any) {
|
||||
EventBus.emit('dataChanged', {
|
||||
type: ObjectType.SPORT_STANDARD,
|
||||
operation,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async createStandard(
|
||||
data: {
|
||||
projectId: string;
|
||||
gender: boolean;
|
||||
personType: string;
|
||||
ageRanges: AgeRange[];
|
||||
scoreTable: Record;
|
||||
},
|
||||
select?: Prisma.SportStandardSelect<DefaultArgs>,
|
||||
include?: Prisma.SportStandardInclude<DefaultArgs>
|
||||
) {
|
||||
this.validateAgeRanges(data.ageRanges);
|
||||
this.validateScoreTable(data.scoreTable, data.ageRanges.length);
|
||||
return this.create({
|
||||
data: {
|
||||
projectId: data.projectId,
|
||||
gender: data.gender,
|
||||
personType: data.personType,
|
||||
ageRanges: JSON.stringify(data.ageRanges),
|
||||
scoreTable: JSON.stringify(data.scoreTable)
|
||||
},
|
||||
select,
|
||||
include
|
||||
})
|
||||
}
|
||||
private validateAgeRanges(ranges: AgeRange[]) {
|
||||
// 检查年龄段是否按顺序排列且无重叠
|
||||
for (let i = 0; i < ranges.length - 1; i++) {
|
||||
const current = ranges[i];
|
||||
const next = ranges[i + 1];
|
||||
|
||||
if (current.end !== next.start) {
|
||||
throw new Error('年龄段必须连续且不重叠');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private validateScoreTable(
|
||||
scoreTable: Record,
|
||||
expectedLength: number
|
||||
) {
|
||||
Object.values(scoreTable).forEach(standards => {
|
||||
if (standards.length !== expectedLength) {
|
||||
throw new Error('分数表的每行数据长度必须与年龄段数量匹配');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public SportScoreCalculator(performance: number, age: number, scoreStandard: ScoreStandard): number {
|
||||
// 1. 找到对应的年龄段索引
|
||||
const ageRangeIndex = scoreStandard.ageRanges.findIndex(range => {
|
||||
const isAboveStart = range.start === null || age > range.start;
|
||||
const isBelowEnd = range.end === null || age <= range.end;
|
||||
return isAboveStart && isBelowEnd;
|
||||
});
|
||||
|
||||
if (ageRangeIndex === -1) {
|
||||
throw new Error('未找到匹配的年龄段');
|
||||
}
|
||||
|
||||
// 2. 查找对应分数
|
||||
const scores = Object.keys(scoreStandard.scoreTable)
|
||||
.map(Number)
|
||||
.sort((a, b) => b - a);
|
||||
|
||||
for (const score of scores) {
|
||||
if (performance >= scoreStandard.scoreTable[score][ageRangeIndex]) {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
async getScore(data: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
gender: boolean;
|
||||
age: number;
|
||||
performance: number;
|
||||
personType: string;
|
||||
}) {
|
||||
const standard = await this.findUnique({
|
||||
where: {
|
||||
id: data.id,
|
||||
projectId: data.projectId,
|
||||
gender: data.gender,
|
||||
personType: data.personType
|
||||
}
|
||||
})
|
||||
if (!standard) {
|
||||
throw new Error('未找到对应的评分标准');
|
||||
}
|
||||
|
||||
return this.SportScoreCalculator(
|
||||
data.performance,
|
||||
data.age,
|
||||
standard.scoreTable as any as ScoreStandard
|
||||
)
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ import { TrainContentModule } from '@server/models/train-content/trainContent.mo
|
|||
import { ResourceModule } from '@server/models/resource/resource.module';
|
||||
import { TrainSituationModule } from '@server/models/train-situation/trainSituation.module';
|
||||
import { DailyTrainModule } from '@server/models/daily-train/dailyTrain.module';
|
||||
import { SportStandardModule } from '@server/models/sport-standard/sportStandard.module';
|
||||
import { SportProjectModule } from '@server/models/sport-project/sportProject.module';
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
|
@ -28,6 +30,7 @@ import { DailyTrainModule } from '@server/models/daily-train/dailyTrain.module';
|
|||
TaxonomyModule,
|
||||
RoleMapModule,
|
||||
TransformModule,
|
||||
SportStandardModule,
|
||||
MessageModule,
|
||||
AppConfigModule,
|
||||
PostModule,
|
||||
|
@ -36,7 +39,8 @@ import { DailyTrainModule } from '@server/models/daily-train/dailyTrain.module';
|
|||
ResourceModule,
|
||||
TrainContentModule,
|
||||
TrainSituationModule,
|
||||
DailyTrainModule
|
||||
DailyTrainModule,
|
||||
SportProjectModule
|
||||
],
|
||||
controllers: [],
|
||||
providers: [TrpcService, TrpcRouter, Logger],
|
||||
|
|
|
@ -17,7 +17,8 @@ import { ResourceRouter } from '../models/resource/resource.router';
|
|||
import { TrainContentRouter } from '@server/models/train-content/trainContent.router';
|
||||
import { TrainSituationRouter } from '@server/models/train-situation/trainSituation.router';
|
||||
import { DailyTrainRouter } from '@server/models/daily-train/dailyTrain.router';
|
||||
|
||||
import { SportStandardRouter } from '@server/models/sport-standard/sportStandard.router';
|
||||
import { SportProjectRouter } from '@server/models/sport-project/sportProject.router';
|
||||
@Injectable()
|
||||
export class TrpcRouter {
|
||||
logger = new Logger(TrpcRouter.name);
|
||||
|
@ -37,7 +38,9 @@ export class TrpcRouter {
|
|||
private readonly resource: ResourceRouter,
|
||||
private readonly trainContent: TrainContentRouter,
|
||||
private readonly trainSituation:TrainSituationRouter,
|
||||
private readonly dailyTrain:DailyTrainRouter
|
||||
private readonly dailyTrain:DailyTrainRouter,
|
||||
private readonly sportStandard:SportStandardRouter,
|
||||
private readonly sportProject:SportProjectRouter
|
||||
) {}
|
||||
getRouter() {
|
||||
return;
|
||||
|
@ -57,7 +60,9 @@ export class TrpcRouter {
|
|||
resource: this.resource.router,
|
||||
trainContent:this.trainContent.router,
|
||||
trainSituation:this.trainSituation.router,
|
||||
dailyTrain:this.dailyTrain.router
|
||||
dailyTrain:this.dailyTrain.router,
|
||||
sportStandard:this.sportStandard.router,
|
||||
sportProject:this.sportProject.router
|
||||
});
|
||||
wss: WebSocketServer = undefined;
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
"framer-motion": "^11.15.0",
|
||||
"hls.js": "^1.5.18",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"mind-elixir": "workspace:^",
|
||||
"mitt": "^3.0.1",
|
||||
"quill": "2.0.3",
|
||||
"react": "18.2.0",
|
||||
|
|
|
@ -0,0 +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() {
|
||||
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>
|
||||
);
|
||||
}
|
|
@ -128,7 +128,7 @@ export default function BaseSettingPage() {
|
|||
<Input></Input>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div className="p-2 grid grid-cols-8 gap-2 border-b">
|
||||
{/* <div className="p-2 grid grid-cols-8 gap-2 border-b">
|
||||
<Form.Item
|
||||
label="首页轮播图"
|
||||
name={["appConfig", "slides"]}>
|
||||
|
@ -141,7 +141,7 @@ export default function BaseSettingPage() {
|
|||
name={["appConfig", "slideLinks"]}>
|
||||
<CarouselUrlInput ></CarouselUrlInput>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* <div
|
||||
className="p-2 border-b flex items-center justify-between"
|
||||
style={{
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
import { Button } from "antd"
|
||||
import { api } from "@nice/client"
|
||||
import React from "react"
|
||||
|
||||
import { useSport } from "@nice/client"
|
||||
export default function Dashboard() {
|
||||
// const {createSportProject} = useSport()
|
||||
// const handleCreateSportProject = () => {
|
||||
// createSportProject.mutate({
|
||||
// data:{
|
||||
// name:"测试项目",
|
||||
// description:"测试项目描述",
|
||||
// type:"测试类型",
|
||||
// unit:"测试单位",
|
||||
// isAscending:true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
return (
|
||||
<div >
|
||||
数据看板(待开发)
|
||||
{/* <Button type="primary" onClick={()=>handleCreateSportProject()}>创建体育项目</Button> */}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -64,7 +64,20 @@ const items = [
|
|||
getItem("体育", "/assessment/sportsassessment", null, null, null),
|
||||
],
|
||||
null,
|
||||
)
|
||||
),
|
||||
getItem(
|
||||
"系统设置",
|
||||
"/admin",
|
||||
<i className="iconfont icon-icon-user" />,
|
||||
[
|
||||
getItem("基本设置", "/admin/base-setting", null, null, null),
|
||||
getItem("用户管理", "/admin/user", null, null, null),
|
||||
getItem("组织架构", "/admin/department", null, null, null),
|
||||
getItem("角色管理", "/admin/role", null, null, null),
|
||||
getItem("考核标准管理", "/admin/assessment-standard", null, null, null),
|
||||
],
|
||||
null,
|
||||
),
|
||||
];
|
||||
|
||||
const NavigationMenu: React.FC = () => {
|
||||
|
@ -76,7 +89,12 @@ const NavigationMenu: React.FC = () => {
|
|||
// 添加考核成绩子路径的匹配规则
|
||||
"^/assessment/positionassessment": ["/assessment"],
|
||||
"^/assessment/commonassessment": ["/assessment"],
|
||||
"^/assessment/sportsassessment": ["/assessment"]
|
||||
"^/assessment/sportsassessment": ["/assessment"],
|
||||
"^/admin/base-setting": ["/admin"],
|
||||
"^/admin/department": ["/admin"],
|
||||
"^/admin/role": ["/admin"],
|
||||
"^/admin/assessment-standard": ["/admin"],
|
||||
"^/admin/user": ["/admin"],
|
||||
};
|
||||
|
||||
// 同时在 useEffect 中更新路径判断逻辑
|
||||
|
@ -89,6 +107,12 @@ const NavigationMenu: React.FC = () => {
|
|||
location.pathname.startsWith("/assessment/") ||
|
||||
location.pathname === "/plan/weekplan" ||
|
||||
location.pathname === "/plan/monthplan"
|
||||
)
|
||||
{
|
||||
setSelectedKeys([location.pathname]);
|
||||
setOpenKeys([location.pathname.split('/').slice(0, 2).join('/')]);
|
||||
} else if (
|
||||
location.pathname.startsWith("/admin/")
|
||||
) {
|
||||
setSelectedKeys([location.pathname]);
|
||||
setOpenKeys([location.pathname.split('/').slice(0, 2).join('/')]);
|
||||
|
|
|
@ -2,14 +2,14 @@ import { Outlet } from "react-router-dom";
|
|||
import { Layout } from "antd";
|
||||
|
||||
import { adminRoute } from "@web/src/routes/admin-route";
|
||||
import AdminSidebar from "./AdminSidebar";
|
||||
// import AdminSidebar from "./AdminSidebar";
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
export default function AdminLayout() {
|
||||
return (
|
||||
<Layout className="min-h-screen">
|
||||
<AdminSidebar routes={adminRoute.children || []} />
|
||||
{/* <AdminSidebar routes={adminRoute.children || []} /> */}
|
||||
<Layout>
|
||||
<Content>
|
||||
<Outlet />
|
||||
|
|
|
@ -15,6 +15,7 @@ import WithAuth from "../components/utils/with-auth";
|
|||
import { CustomRouteObject } from "./types";
|
||||
import StaffPage from "../app/admin/staff/page";
|
||||
import AdminLayout from "../components/layout/admin/AdminLayout";
|
||||
import AssessmentStandardPage from "../app/admin/assessmentstandard/assessment-standardpage";
|
||||
|
||||
export const adminRoute: CustomRouteObject = {
|
||||
path: "admin",
|
||||
|
@ -58,7 +59,7 @@ export const adminRoute: CustomRouteObject = {
|
|||
},
|
||||
},
|
||||
{
|
||||
path: "staff",
|
||||
path: "user",
|
||||
name: "用户管理",
|
||||
icon: <UserOutlined />,
|
||||
element: (
|
||||
|
@ -74,31 +75,37 @@ export const adminRoute: CustomRouteObject = {
|
|||
),
|
||||
handle: {
|
||||
crumb() {
|
||||
return <Link to={"/admin/staff"}>用户管理</Link>;
|
||||
return <Link to={"/admin/user"}>用户管理</Link>;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "term",
|
||||
name: "分类配置",
|
||||
path: "assessment-standard",
|
||||
name: "考核标准管理",
|
||||
icon: <TagsOutlined />,
|
||||
element: (
|
||||
<WithAuth
|
||||
options={{
|
||||
orPermissions: [
|
||||
RolePerms.MANAGE_ANY_TERM,
|
||||
// RolePerms.MANAGE_DOM_TERM
|
||||
],
|
||||
}}>
|
||||
<TermAdminPage></TermAdminPage>
|
||||
</WithAuth>
|
||||
),
|
||||
handle: {
|
||||
crumb() {
|
||||
return <Link to={"/admin/term"}>分类配置</Link>;
|
||||
},
|
||||
},
|
||||
element: <AssessmentStandardPage></AssessmentStandardPage>
|
||||
},
|
||||
// {
|
||||
// path: "term",
|
||||
// name: "分类配置",
|
||||
// icon: <TagsOutlined />,
|
||||
// element: (
|
||||
// <WithAuth
|
||||
// options={{
|
||||
// orPermissions: [
|
||||
// RolePerms.MANAGE_ANY_TERM,
|
||||
// // RolePerms.MANAGE_DOM_TERM
|
||||
// ],
|
||||
// }}>
|
||||
// <TermAdminPage></TermAdminPage>
|
||||
// </WithAuth>
|
||||
// ),
|
||||
// handle: {
|
||||
// crumb() {
|
||||
// return <Link to={"/admin/term"}>分类配置</Link>;
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
path: "role",
|
||||
name: "角色管理",
|
||||
|
|
|
@ -13,6 +13,8 @@ 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 AdminLayout from "../components/layout/admin/AdminLayout";
|
||||
import { adminRoute } from "./admin-route";
|
||||
import SportPage from "../app/main/sport/page";
|
||||
interface CustomIndexRouteObject extends IndexRouteObject {
|
||||
name?: string;
|
||||
|
@ -43,6 +45,7 @@ export const routes: CustomRouteObject[] = [
|
|||
return <Link to={"/"}>主页</Link>;
|
||||
},
|
||||
},
|
||||
|
||||
children: [
|
||||
{
|
||||
element: <MainLayout></MainLayout>,
|
||||
|
@ -89,11 +92,18 @@ export const routes: CustomRouteObject[] = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/admin",
|
||||
element: <AdminLayout></AdminLayout>,
|
||||
children: adminRoute.children,
|
||||
}
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "/login",
|
||||
breadcrumb: "登录",
|
||||
|
|
|
@ -11,3 +11,4 @@ export * from "./useMessage"
|
|||
export * from "./usePost"
|
||||
export * from "./useEntity"
|
||||
export * from "./useTrainSituation"
|
||||
export * from "./useSport"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { getQueryKey } from "@trpc/react-query";
|
||||
import { api } from "../trpc"; // Adjust path as necessary
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { ObjectType } from "@nice/common";
|
||||
import { CrudOperation, emitDataChange } from "../../event";
|
||||
|
||||
export function useSport() {
|
||||
const queryClient = useQueryClient();
|
||||
const queryKey = getQueryKey(api.sportProject);
|
||||
const queryKeyStandard = getQueryKey(api.sportStandard);
|
||||
|
||||
const createSportProject = api.sportProject.create.useMutation({
|
||||
onSuccess: (result) => {
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
emitDataChange(ObjectType.SPORT_PROJECT, result, CrudOperation.CREATED);
|
||||
},
|
||||
});
|
||||
|
||||
const createSportStandard = api.sportStandard.createStandard.useMutation({
|
||||
onSuccess: (result) => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeyStandard });
|
||||
emitDataChange(ObjectType.SPORT_STANDARD, result, CrudOperation.CREATED);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
createSportProject,
|
||||
//createSportStandard
|
||||
};
|
||||
}
|
|
@ -486,3 +486,34 @@ model TrainPlan {
|
|||
|
||||
@@map("train_plan")
|
||||
}
|
||||
|
||||
|
||||
model SportProject {
|
||||
id String @id @default(cuid())
|
||||
name String @map("name") // 项目名称
|
||||
type String @map("type") // 项目类型
|
||||
description String? @map("description") // 项目描述
|
||||
unit String @map("unit") // 成绩单位(如:秒、米、个)
|
||||
isAscending Boolean @map("is_ascending") // 是否为升序计分
|
||||
standards SportStandard[]
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("sport_project")
|
||||
}
|
||||
|
||||
model SportStandard {
|
||||
id String @id @default(cuid())
|
||||
|
||||
projectId String @map("project_id")
|
||||
project SportProject @relation(fields: [projectId], references: [id])
|
||||
|
||||
gender Boolean @map("gender") // true为男,false为女
|
||||
personType String @map("person_type") // 人员类型
|
||||
|
||||
ageRanges Json @map("age_ranges") // 年龄段定义
|
||||
scoreTable Json @map("score_table") // 评分标准表
|
||||
|
||||
@@unique([projectId, gender, personType])
|
||||
@@map("sport_standard")
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ export enum ObjectType {
|
|||
RESOURCE = "resource",
|
||||
TRAIN_CONTENT = "trainContent",
|
||||
TRAIN_SITUATION = "trainSituation",
|
||||
DAILY_TRAIN = "dailyTrainTime"
|
||||
DAILY_TRAIN = "dailyTrainTime",
|
||||
SPORT_STANDARD = "sportStandard",
|
||||
SPORT_PROJECT = "sportProject"
|
||||
}
|
||||
export enum RolePerms {
|
||||
// Create Permissions 创建权限
|
||||
|
@ -208,4 +210,9 @@ export const LessonTypeLabel = {
|
|||
export enum TrainContentType {
|
||||
SUBJECTS = "科目",
|
||||
COURSE = "课目"
|
||||
}
|
||||
|
||||
export enum SportStandardType {
|
||||
STANDARD = "标准",
|
||||
OTHER = "其他"
|
||||
}
|
1555
pnpm-lock.yaml
1555
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue