Compare commits
No commits in common. "42a0800e415ddeaeb6272f879e99784555cd2be8" and "fbca6ea82cfaafb31ba80c82f0f8dbe1e4dd06e1" have entirely different histories.
42a0800e41
...
fbca6ea82c
|
@ -95,7 +95,7 @@ export class BaseTreeService<
|
||||||
? [
|
? [
|
||||||
...(
|
...(
|
||||||
await transaction[this.ancestryType].findMany({
|
await transaction[this.ancestryType].findMany({
|
||||||
where: { descendantId: anyArgs.data.parentId,},
|
where: { descendantId: anyArgs.data.parentId },
|
||||||
select: { ancestorId: true, relDepth: true },
|
select: { ancestorId: true, relDepth: true },
|
||||||
})
|
})
|
||||||
).map(({ ancestorId, relDepth }) => ({
|
).map(({ ancestorId, relDepth }) => ({
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Controller } from '@nestjs/common';
|
|
||||||
|
|
||||||
|
|
||||||
import { CarService } from './car.service';
|
|
||||||
|
|
||||||
@Controller('car')
|
|
||||||
export class CarController {
|
|
||||||
constructor(private readonly carService: CarService) {}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { QueueModule } from '@server/queue/queue.module';
|
|
||||||
import { MessageModule } from '../message/message.module';
|
|
||||||
import { CarRouter } from './car.router';
|
|
||||||
import { CarController } from './car.controller';
|
|
||||||
import { CarService } from './car.service';
|
|
||||||
import { RoleMapModule } from '../rbac/rbac.module';
|
|
||||||
import { DepartmentModule } from '../department/department.module';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [QueueModule, RoleMapModule, MessageModule, DepartmentModule],
|
|
||||||
providers: [CarService, CarRouter, TrpcService, DepartmentService],
|
|
||||||
exports: [CarRouter, CarService],
|
|
||||||
controllers: [CarController],
|
|
||||||
})
|
|
||||||
export class CarModule {}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { CarService } from './car.service';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
|
|
||||||
const CarCreateArgsSchema: ZodType<Prisma.CarCreateArgs> = z.any();
|
|
||||||
const CarUpdateArgsSchema: ZodType<Prisma.CarUpdateArgs> = z.any();
|
|
||||||
const CarFindManyArgsSchema: ZodType<Prisma.CarFindManyArgs> = z.any();
|
|
||||||
const CarWhereUniqueInputSchema: ZodType<Prisma.CarWhereUniqueInput> = z.any();
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CarRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly carService: CarService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
router = this.trpc.router({
|
|
||||||
create: this.trpc.protectProcedure
|
|
||||||
.input(CarCreateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.carService.create(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
update: this.trpc.protectProcedure
|
|
||||||
.input(CarUpdateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.carService.update(input);
|
|
||||||
}),
|
|
||||||
softDeleteByIds: this.trpc.protectProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
ids: z.array(z.string()),
|
|
||||||
data: CarFindManyArgsSchema.nullish(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.carService.softDeleteByIds(input.ids);
|
|
||||||
}),
|
|
||||||
delete: this.trpc.protectProcedure
|
|
||||||
.input(CarWhereUniqueInputSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.carService.delete({ where: input });
|
|
||||||
}),
|
|
||||||
|
|
||||||
findMany: this.trpc.procedure
|
|
||||||
.input(CarFindManyArgsSchema)
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.carService.findMany(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
|
|
||||||
findWithScores: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.carService.findWithScores(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
search: this.trpc.procedure
|
|
||||||
.input(z.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
courseId: z.number().optional(),
|
|
||||||
limit: z.number().optional()
|
|
||||||
}))
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.carService.search(input);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
db,
|
|
||||||
Prisma,
|
|
||||||
UserProfile,
|
|
||||||
VisitType,
|
|
||||||
Post,
|
|
||||||
PostType,
|
|
||||||
RolePerms,
|
|
||||||
ResPerm,
|
|
||||||
ObjectType,
|
|
||||||
CourseMethodSchema,
|
|
||||||
} from '@nice/common';
|
|
||||||
import { MessageService } from '../message/message.service';
|
|
||||||
import { BaseService } from '../base/base.service';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
|
||||||
import { BaseTreeService } from '../base/base.tree.service';
|
|
||||||
@Injectable()
|
|
||||||
export class CarService extends BaseTreeService<Prisma.CarDelegate> {
|
|
||||||
constructor(
|
|
||||||
private readonly messageService: MessageService,
|
|
||||||
private readonly departmentService: DepartmentService,
|
|
||||||
) {
|
|
||||||
super(db, ObjectType.CAR, 'carAncestry', true);
|
|
||||||
}
|
|
||||||
async create(
|
|
||||||
args: Prisma.CarCreateArgs,
|
|
||||||
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
|
||||||
) {
|
|
||||||
const result = await db.car.create(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.CAR,
|
|
||||||
operation: CrudOperation.CREATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async update(args: Prisma.CarUpdateArgs, staff?: UserProfile) {
|
|
||||||
const result = await db.car.update(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.CAR,
|
|
||||||
operation: CrudOperation.UPDATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 查找赛车并包含比赛信息
|
|
||||||
*/
|
|
||||||
async findWithScores(clubId: number) {
|
|
||||||
return await db.car.findUnique({
|
|
||||||
where: { id: clubId },
|
|
||||||
select: {
|
|
||||||
name:true,
|
|
||||||
club:true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 搜索
|
|
||||||
*/
|
|
||||||
async search(params: { name?: string, clubId?: number, limit?: number }) {
|
|
||||||
const { name, clubId, limit = 30 } = params;
|
|
||||||
|
|
||||||
return await db.car.findMany({
|
|
||||||
where: {
|
|
||||||
...(name && { name: { contains: name } }),
|
|
||||||
...(clubId && { clubId })
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
clubId: true,
|
|
||||||
name:true
|
|
||||||
},
|
|
||||||
take: limit
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
|
||||||
EventBus.emit("dataChanged", {
|
|
||||||
type: this.objectType,
|
|
||||||
operation,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Controller } from '@nestjs/common';
|
|
||||||
|
|
||||||
|
|
||||||
import { CourseService } from './course.service';
|
|
||||||
|
|
||||||
@Controller('course')
|
|
||||||
export class CourseController {
|
|
||||||
constructor(private readonly courseService: CourseService) {}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { QueueModule } from '@server/queue/queue.module';
|
|
||||||
import { MessageModule } from '../message/message.module';
|
|
||||||
import { CourseRouter } from './course.router';
|
|
||||||
import { CourseController } from './course.controller';
|
|
||||||
import { CourseService } from './course.service';
|
|
||||||
import { RoleMapModule } from '../rbac/rbac.module';
|
|
||||||
import { StudentModule } from '../student/student.module';
|
|
||||||
import { DepartmentModule } from '../department/department.module';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [QueueModule, RoleMapModule, MessageModule,StudentModule,DepartmentModule],
|
|
||||||
providers: [CourseService, CourseRouter, TrpcService,DepartmentService],
|
|
||||||
exports: [CourseRouter, CourseService],
|
|
||||||
controllers: [CourseController],
|
|
||||||
})
|
|
||||||
export class CourseModule {}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { CourseService } from './course.service';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
|
|
||||||
const CourseCreateArgsSchema: ZodType<Prisma.CourseCreateArgs> = z.any();
|
|
||||||
const CourseUpdateArgsSchema: ZodType<Prisma.CourseUpdateArgs> = z.any();
|
|
||||||
const CourseFindManyArgsSchema: ZodType<Prisma.CourseFindManyArgs> = z.any();
|
|
||||||
const CourseWhereUniqueInputSchema: ZodType<Prisma.CourseWhereUniqueInput> = z.any();
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CourseRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly courseService: CourseService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
router = this.trpc.router({
|
|
||||||
create: this.trpc.protectProcedure
|
|
||||||
.input(CourseCreateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.courseService.create(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
update: this.trpc.protectProcedure
|
|
||||||
.input(CourseUpdateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.courseService.update(input);
|
|
||||||
}),
|
|
||||||
softDeleteByIds: this.trpc.protectProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
ids: z.array(z.string()),
|
|
||||||
data: CourseUpdateArgsSchema.nullish(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.courseService.softDeleteByIds(input.ids);
|
|
||||||
}),
|
|
||||||
delete: this.trpc.protectProcedure
|
|
||||||
.input(CourseWhereUniqueInputSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.courseService.delete({ where: input });
|
|
||||||
}),
|
|
||||||
|
|
||||||
findMany: this.trpc.procedure
|
|
||||||
.input(CourseFindManyArgsSchema)
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.courseService.findMany(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
|
|
||||||
findWithScores: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.courseService.findWithScores(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
search: this.trpc.procedure
|
|
||||||
.input(z.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
courseId: z.number().optional(),
|
|
||||||
limit: z.number().optional()
|
|
||||||
}))
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.courseService.search(input);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
db,
|
|
||||||
Prisma,
|
|
||||||
UserProfile,
|
|
||||||
VisitType,
|
|
||||||
Post,
|
|
||||||
PostType,
|
|
||||||
RolePerms,
|
|
||||||
ResPerm,
|
|
||||||
ObjectType,
|
|
||||||
CourseMethodSchema,
|
|
||||||
} from '@nice/common';
|
|
||||||
import { MessageService } from '../message/message.service';
|
|
||||||
import { BaseService } from '../base/base.service';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
|
||||||
import { BaseTreeService } from '../base/base.tree.service';
|
|
||||||
import { z } from 'zod';
|
|
||||||
@Injectable()
|
|
||||||
export class CourseService extends BaseTreeService<Prisma.CourseDelegate> {
|
|
||||||
constructor(
|
|
||||||
private readonly messageService: MessageService,
|
|
||||||
private readonly departmentService: DepartmentService,
|
|
||||||
) {
|
|
||||||
super(db, ObjectType.COURSE, 'courseAncestry', true);
|
|
||||||
}
|
|
||||||
async create(
|
|
||||||
args: Prisma.CourseCreateArgs,
|
|
||||||
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
|
||||||
) {
|
|
||||||
const result = await super.create(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.COURSE,
|
|
||||||
operation: CrudOperation.CREATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async update(args: Prisma.CourseUpdateArgs, staff?: UserProfile) {
|
|
||||||
const result = await super.update(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.COURSE,
|
|
||||||
operation: CrudOperation.UPDATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 查找学生并包含成绩信息
|
|
||||||
*/
|
|
||||||
async findWithScores(courseId: number) {
|
|
||||||
return await db.course.findUnique({
|
|
||||||
where: { id: courseId },
|
|
||||||
include: {
|
|
||||||
scores: {
|
|
||||||
include: {
|
|
||||||
course: true,
|
|
||||||
student: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 搜索学生
|
|
||||||
*/
|
|
||||||
async search(params: { name?: string, courseId?: number, limit?: number }) {
|
|
||||||
const { name, courseId, limit = 30 } = params;
|
|
||||||
|
|
||||||
return await db.course.findMany({
|
|
||||||
where: {
|
|
||||||
...(name && { name: { contains: name } }),
|
|
||||||
...(courseId && { courseId })
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
scores: true
|
|
||||||
},
|
|
||||||
take: limit
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
|
||||||
EventBus.emit("dataChanged", {
|
|
||||||
type: this.objectType,
|
|
||||||
operation,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Controller } from '@nestjs/common';
|
|
||||||
|
|
||||||
|
|
||||||
import { DriverService } from './driver.service';
|
|
||||||
|
|
||||||
@Controller('driver')
|
|
||||||
export class DriverController {
|
|
||||||
constructor(private readonly driverService: DriverService) {}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { QueueModule } from '@server/queue/queue.module';
|
|
||||||
import { MessageModule } from '../message/message.module';
|
|
||||||
import { DriverRouter } from './driver.router';
|
|
||||||
import { DriverController } from './driver.controller';
|
|
||||||
import { DriverService } from './driver.service';
|
|
||||||
import { RoleMapModule } from '../rbac/rbac.module';
|
|
||||||
import { DepartmentModule } from '../department/department.module';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [QueueModule, RoleMapModule, MessageModule,,DepartmentModule,DriverModule],
|
|
||||||
providers: [DriverService, DriverRouter, TrpcService,DepartmentService],
|
|
||||||
exports: [DriverRouter, DriverService],
|
|
||||||
controllers: [DriverController],
|
|
||||||
})
|
|
||||||
export class DriverModule {}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { DriverService } from './driver.service';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
|
|
||||||
const CourseCreateArgsSchema: ZodType<Prisma.CarCreateArgs> = z.any();
|
|
||||||
const CourseUpdateArgsSchema: ZodType<Prisma.CarUpdateArgs> = z.any();
|
|
||||||
const CourseFindManyArgsSchema: ZodType<Prisma.CarFindManyArgs> = z.any();
|
|
||||||
const CourseWhereUniqueInputSchema: ZodType<Prisma.CarWhereUniqueInput> = z.any();
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DriverRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly driverService: DriverService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
router = this.trpc.router({
|
|
||||||
create: this.trpc.protectProcedure
|
|
||||||
.input(CourseCreateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.driverService.create(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
// update: this.trpc.protectProcedure
|
|
||||||
// .input(CourseUpdateArgsSchema)
|
|
||||||
// .mutation(async ({ input }) => {
|
|
||||||
// return await this.driverService.update(input);
|
|
||||||
// }),
|
|
||||||
softDeleteByIds: this.trpc.protectProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
ids: z.array(z.string()),
|
|
||||||
data: CourseUpdateArgsSchema.nullish(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.driverService.softDeleteByIds(input.ids);
|
|
||||||
}),
|
|
||||||
// delete: this.trpc.protectProcedure
|
|
||||||
// .input(CourseWhereUniqueInputSchema)
|
|
||||||
// .mutation(async ({ input }) => {
|
|
||||||
// return await this.driverService.delete({ where: input });
|
|
||||||
// }),
|
|
||||||
|
|
||||||
// findMany: this.trpc.procedure
|
|
||||||
// .input(CourseFindManyArgsSchema)
|
|
||||||
// .query(async ({ input }) => {
|
|
||||||
// return await this.driverService.findMany(input);
|
|
||||||
// }),
|
|
||||||
|
|
||||||
|
|
||||||
findWithScores: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.driverService.findWithScores(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
search: this.trpc.procedure
|
|
||||||
.input(z.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
courseId: z.number().optional(),
|
|
||||||
limit: z.number().optional()
|
|
||||||
}))
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.driverService.search(input);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
db,
|
|
||||||
Prisma,
|
|
||||||
UserProfile,
|
|
||||||
VisitType,
|
|
||||||
Post,
|
|
||||||
PostType,
|
|
||||||
RolePerms,
|
|
||||||
ResPerm,
|
|
||||||
ObjectType,
|
|
||||||
CourseMethodSchema,
|
|
||||||
} from '@nice/common';
|
|
||||||
import { MessageService } from '../message/message.service';
|
|
||||||
import { BaseService } from '../base/base.service';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
|
||||||
import { BaseTreeService } from '../base/base.tree.service';
|
|
||||||
import { z } from 'zod';
|
|
||||||
@Injectable()
|
|
||||||
export class DriverService extends BaseTreeService<Prisma.DriverDelegate> {
|
|
||||||
constructor(
|
|
||||||
private readonly messageService: MessageService,
|
|
||||||
private readonly departmentService: DepartmentService,
|
|
||||||
) {
|
|
||||||
super(db, ObjectType.DRIVER, 'driverAncestry', true);
|
|
||||||
}
|
|
||||||
async create(
|
|
||||||
args: Prisma.DriverCreateArgs,
|
|
||||||
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
|
||||||
) {
|
|
||||||
const result = await db.driver.create(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.DRIVER,
|
|
||||||
operation: CrudOperation.CREATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async update(args: Prisma.DriverUpdateArgs, staff?: UserProfile) {
|
|
||||||
const result = await db.driver.update(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.DRIVER,
|
|
||||||
operation: CrudOperation.UPDATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 查找赛车并包含比赛信息
|
|
||||||
*/
|
|
||||||
async findWithScores(clubId: number) {
|
|
||||||
return await db.driver.findUnique({
|
|
||||||
where: { id: clubId },
|
|
||||||
select: {
|
|
||||||
name:true,
|
|
||||||
club:true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 搜索学生
|
|
||||||
*/
|
|
||||||
async search(params: { name?: string, clubId?: number, limit?: number }) {
|
|
||||||
const { name, clubId, limit = 30 } = params;
|
|
||||||
|
|
||||||
return await db.driver.findMany({
|
|
||||||
where: {
|
|
||||||
...(name && { name: { contains: name } }),
|
|
||||||
...(clubId && { clubId })
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
clubId: true,
|
|
||||||
name:true
|
|
||||||
},
|
|
||||||
take: limit
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
|
||||||
EventBus.emit("dataChanged", {
|
|
||||||
type: this.objectType,
|
|
||||||
operation,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +1,45 @@
|
||||||
import { Controller, Get, Query, Param } from '@nestjs/common';
|
import { Controller, Get, Query, Param } from '@nestjs/common';
|
||||||
import { GoodsService } from './goods.service';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
// 定义商品相关的控制器,路由前缀为 /goods
|
|
||||||
@Controller('goods')
|
@Controller('goods')
|
||||||
export class GoodsController {
|
export class GoodsController {
|
||||||
// 构造函数,在控制器实例化时执行 只读实例
|
constructor() {
|
||||||
constructor(private readonly goodsService: GoodsService) {
|
console.log('goods controller')
|
||||||
console.log('goods controller'); // 打印日志,用于调试 调用商品服务的方法
|
}
|
||||||
}
|
|
||||||
// 示例1:基本查询参数
|
|
||||||
// GET /goods/hello?name=xxx
|
|
||||||
@Get('hello')
|
|
||||||
getHello(@Query('name') name?: string) {
|
|
||||||
return this.goodsService.getHello(name);
|
|
||||||
// 返回传入的name参数,如果未传入则返回'Guest'
|
|
||||||
}
|
|
||||||
// 示例2:路径参数
|
|
||||||
// GET /goods/detail/123
|
|
||||||
@Get('detail/:id')
|
|
||||||
getDetail(@Param('id') id: string) {
|
|
||||||
return this.goodsService.getDetail(id) // 返回路径参数中的id
|
|
||||||
}
|
|
||||||
|
|
||||||
// 示例3:多个查询参数
|
// 示例1:基本查询参数
|
||||||
// GET /goods/search?keyword=xxx&page=2&limit=20
|
@Get('hello')
|
||||||
@Get('search')
|
getHello(@Query('name') name?: string) {
|
||||||
searchProducts(
|
return {
|
||||||
@Query('keyword') keyword: string, // 搜索关键词
|
message: 'Hello World!',
|
||||||
@Query('page') page: number = 1, // 页码,默认值为1
|
name: name || 'Guest'
|
||||||
@Query('limit') limit: number = 10, // 每页数量,默认值为10
|
};
|
||||||
) {
|
}
|
||||||
return {
|
|
||||||
keyword, // 返回搜索关键词
|
// 示例2:路径参数
|
||||||
page, // 返回当前页码
|
@Get('detail/:id')
|
||||||
limit, // 返回每页数量
|
getDetail(@Param('id') id: string) {
|
||||||
results: [], // 返回搜索结果(示例中为空数组)
|
return {
|
||||||
};
|
id: id,
|
||||||
}
|
detail: `Detail for product ${id}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例3:多个查询参数
|
||||||
|
@Get('search')
|
||||||
|
searchProducts(
|
||||||
|
@Query('keyword') keyword: string,
|
||||||
|
@Query('page') page: number = 1,
|
||||||
|
@Query('limit') limit: number = 10
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
keyword,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
results: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
}
|
||||||
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { GoodsService } from './goods.service';
|
import { GoodsService } from './goods.service';
|
||||||
import { GoodsController } from './goods.controller';
|
import { GoodsController } from './goods.controller';
|
||||||
import { GoodsRouter } from './goods.router';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [GoodsService,GoodsRouter,TrpcService], // 1. 注册服务提供者,使 GoodsService 可在本模块中使用
|
<<<<<<< HEAD
|
||||||
controllers:[GoodsController], // 2. 注册控制器,使 GoodsController 的路由可用
|
|
||||||
exports:[GoodsRouter]
|
providers: [GoodsService],
|
||||||
|
controllers:[GoodsController]
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
export class GoodsModule {}
|
||||||
|
=======
|
||||||
|
providers: [GoodsService],
|
||||||
|
controllers: [GoodsController],
|
||||||
})
|
})
|
||||||
export class GoodsModule {}
|
export class GoodsModule {}
|
||||||
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { GoodsService } from './goods.service';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
const GoodsUncheckedCreateInputSchema: ZodType<Prisma.GoodsUncheckedCreateInput> =
|
|
||||||
z.any();
|
|
||||||
const GoodsWhereInputSchema: ZodType<Prisma.GoodsWhereInput> = z.any();
|
|
||||||
const GoodsSelectSchema: ZodType<Prisma.GoodsSelect> = z.any();
|
|
||||||
@Injectable()
|
|
||||||
export class GoodsRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly goodsService: GoodsService,
|
|
||||||
) {}
|
|
||||||
// trpc路由
|
|
||||||
router = this.trpc.router({
|
|
||||||
// 希望前端传什么参数
|
|
||||||
//最简单的trpc写法
|
|
||||||
hello: this.trpc.procedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
name: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.query(({ input }) => {
|
|
||||||
return input.name;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GoodsService {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
// 注解相当于mapper
|
|
||||||
@Injectable()
|
|
||||||
export class GoodsService {
|
|
||||||
getHello(name: string) {
|
|
||||||
return {
|
|
||||||
message: 'Hello World!',
|
|
||||||
name: name || 'Guest',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET /goods/detail/123
|
@Injectable()
|
||||||
getDetail(id: string) {
|
<<<<<<< HEAD
|
||||||
return {
|
<<<<<<< HEAD
|
||||||
id: id, // 返回路径参数中的id
|
<<<<<<< HEAD
|
||||||
detail: `Detail for product ${id}`, // 返回包含id的详细信息
|
export class GoodsService {}
|
||||||
};
|
=======
|
||||||
}
|
<<<<<<< HEAD
|
||||||
}
|
=======
|
||||||
|
>>>>>>> dc75314e36f86056790758574c3426f379c16a87
|
||||||
|
export class GoodsService {}
|
||||||
|
=======
|
||||||
|
export class GoodsService {
|
||||||
|
|
||||||
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
>>>>>>> 28132992c8dc4afaf9311d9ecd4ecd992e43e827
|
||||||
|
>>>>>>> 5a581fdda6e53f086e068357e1ce0cb6994ddbf2
|
||||||
|
=======
|
||||||
|
export class GoodsService {
|
||||||
|
|
||||||
|
}
|
||||||
|
=======
|
||||||
|
>>>>>>> dc75314e36f86056790758574c3426f379c16a87
|
||||||
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { DepartmentModule } from '../department/department.module';
|
||||||
import { MessageController } from './message.controller';
|
import { MessageController } from './message.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [DepartmentModule],
|
imports: [DepartmentModule], // 导入其他模块
|
||||||
providers: [MessageService, MessageRouter, TrpcService],
|
providers: [MessageService, MessageRouter, TrpcService], // 注册服务器,可以被自己使用
|
||||||
exports: [MessageService, MessageRouter],
|
exports: [MessageService, MessageRouter], // 导出服务器
|
||||||
controllers: [MessageController],
|
controllers: [MessageController], // 注册控制器
|
||||||
})
|
})
|
||||||
export class MessageModule { }
|
export class MessageModule {}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
|
||||||
|
|
||||||
|
|
||||||
import { ScoreService } from './score.service';
|
|
||||||
|
|
||||||
@Controller('score')
|
|
||||||
export class ScoreController {
|
|
||||||
constructor(private readonly scoreService: ScoreService) {}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { DepartmentService } from '@server/models/department/department.service';
|
|
||||||
|
|
||||||
import { QueueModule } from '@server/queue/queue.module';
|
|
||||||
import { MessageModule } from '../message/message.module';
|
|
||||||
import { ScoreRouter } from './score.router';
|
|
||||||
import { ScoreController } from './score.controller';
|
|
||||||
import { ScoreService } from './score.service';
|
|
||||||
import { RoleMapModule } from '../rbac/rbac.module';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [QueueModule, RoleMapModule, MessageModule],
|
|
||||||
providers: [ScoreService, ScoreRouter, TrpcService, DepartmentService],
|
|
||||||
exports: [ScoreRouter, ScoreService],
|
|
||||||
controllers: [ScoreController],
|
|
||||||
})
|
|
||||||
export class ScoreModule {}
|
|
|
@ -1,75 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { ScoreService } from './score.service';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
|
|
||||||
const ScoreCreateArgsSchema: ZodType<Prisma.ScoreCreateArgs> = z.any();
|
|
||||||
const ScoreUpdateArgsSchema: ZodType<Prisma.ScoreUpdateArgs> = z.any();
|
|
||||||
const ScoreFindManyArgsSchema: ZodType<Prisma.ScoreFindManyArgs> = z.any();
|
|
||||||
const ScoreWhereUniqueInputSchema: ZodType<Prisma.ScoreWhereUniqueInput> = z.any();
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ScoreRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly scoreService: ScoreService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
router = this.trpc.router({
|
|
||||||
create: this.trpc.protectProcedure
|
|
||||||
.input(ScoreCreateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.scoreService.create(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
update: this.trpc.protectProcedure
|
|
||||||
.input(ScoreUpdateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.scoreService.update(input);
|
|
||||||
}),
|
|
||||||
softDeleteByIds: this.trpc.protectProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
ids: z.array(z.string()),
|
|
||||||
data: ScoreUpdateArgsSchema.nullish(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.scoreService.softDeleteByIds(input.ids);
|
|
||||||
}),
|
|
||||||
delete: this.trpc.protectProcedure
|
|
||||||
.input(ScoreWhereUniqueInputSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.scoreService.delete({ where: input });
|
|
||||||
}),
|
|
||||||
|
|
||||||
findMany: this.trpc.procedure
|
|
||||||
.input(ScoreFindManyArgsSchema)
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.scoreService.findMany(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
findByClass: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.scoreService.findByClass(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
findWithScores: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.scoreService.findWithScores(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
search: this.trpc.procedure
|
|
||||||
.input(z.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
classId: z.number().optional(),
|
|
||||||
limit: z.number().optional()
|
|
||||||
}))
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.scoreService.search(input);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
db,
|
|
||||||
Prisma,
|
|
||||||
UserProfile,
|
|
||||||
VisitType,
|
|
||||||
Post,
|
|
||||||
PostType,
|
|
||||||
RolePerms,
|
|
||||||
ResPerm,
|
|
||||||
ObjectType,
|
|
||||||
CourseMethodSchema,
|
|
||||||
} from '@nice/common';
|
|
||||||
import { MessageService } from '../message/message.service';
|
|
||||||
import { BaseService } from '../base/base.service';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
|
||||||
import { BaseTreeService } from '../base/base.tree.service';
|
|
||||||
import { z } from 'zod';
|
|
||||||
@Injectable()
|
|
||||||
export class ScoreService extends BaseTreeService<Prisma.ScoreDelegate> {
|
|
||||||
constructor(
|
|
||||||
private readonly messageService: MessageService,
|
|
||||||
private readonly departmentService: DepartmentService,
|
|
||||||
) {
|
|
||||||
super(db, ObjectType.SCORE, 'scoreAncestry', true);
|
|
||||||
}
|
|
||||||
async create(
|
|
||||||
args: Prisma.ScoreCreateArgs,
|
|
||||||
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
|
||||||
) {
|
|
||||||
const result = await super.create(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.SCORE,
|
|
||||||
operation: CrudOperation.CREATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async update(args: Prisma.ScoreUpdateArgs, staff?: UserProfile) {
|
|
||||||
const result = await super.update(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.SCORE,
|
|
||||||
operation: CrudOperation.UPDATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async findByClass(scoreId: number) {
|
|
||||||
return await db.score.findMany({
|
|
||||||
where: { id: scoreId },
|
|
||||||
include: {
|
|
||||||
student: true,
|
|
||||||
course: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 查找学生并包含成绩信息
|
|
||||||
*/
|
|
||||||
async findWithScores(studentId: number) {
|
|
||||||
return await db.student.findUnique({
|
|
||||||
where: { id: studentId },
|
|
||||||
include: {
|
|
||||||
class: true,
|
|
||||||
scores: {
|
|
||||||
include: {
|
|
||||||
course: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 搜索学生
|
|
||||||
*/
|
|
||||||
async search(params: { name?: string, classId?: number, limit?: number }) {
|
|
||||||
const { name, classId, limit = 30 } = params;
|
|
||||||
|
|
||||||
return await db.student.findMany({
|
|
||||||
where: {
|
|
||||||
...(name && { name: { contains: name } }),
|
|
||||||
...(classId && { classId })
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
class: true
|
|
||||||
},
|
|
||||||
take: limit
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
|
||||||
EventBus.emit("dataChanged", {
|
|
||||||
type: this.objectType,
|
|
||||||
operation,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { StudentService } from './student.service';
|
|
||||||
|
|
||||||
@Controller('student')
|
|
||||||
export class StudentController {
|
|
||||||
constructor(private readonly studentService: StudentService) {}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { DepartmentService } from '@server/models/department/department.service';
|
|
||||||
|
|
||||||
import { QueueModule } from '@server/queue/queue.module';
|
|
||||||
import { MessageModule } from '../message/message.module';
|
|
||||||
import { StudentRouter } from './student.router';
|
|
||||||
import { StudentController } from './student.controller';
|
|
||||||
import { StudentService } from './student.service';
|
|
||||||
import { RoleMapModule } from '../rbac/rbac.module';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [QueueModule, RoleMapModule, MessageModule],
|
|
||||||
providers: [StudentService, StudentRouter, TrpcService, DepartmentService],
|
|
||||||
exports: [StudentRouter, StudentService],
|
|
||||||
controllers: [StudentController],
|
|
||||||
})
|
|
||||||
export class StudentModule {}
|
|
|
@ -1,75 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
|
||||||
import { StudentService } from './student.service';
|
|
||||||
import { z, ZodType } from 'zod';
|
|
||||||
import { Prisma } from '@nice/common';
|
|
||||||
|
|
||||||
const StudentCreateArgsSchema: ZodType<Prisma.StudentCreateArgs> = z.any();
|
|
||||||
const StudentUpdateArgsSchema: ZodType<Prisma.StudentUpdateArgs> = z.any();
|
|
||||||
const StudentFindManyArgsSchema: ZodType<Prisma.StudentFindManyArgs> = z.any();
|
|
||||||
const StudentWhereUniqueInputSchema: ZodType<Prisma.StudentWhereUniqueInput> = z.any();
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class StudentRouter {
|
|
||||||
constructor(
|
|
||||||
private readonly trpc: TrpcService,
|
|
||||||
private readonly studentService: StudentService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
router = this.trpc.router({
|
|
||||||
create: this.trpc.procedure
|
|
||||||
.input(StudentCreateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.studentService.create(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
update: this.trpc.procedure
|
|
||||||
.input(StudentUpdateArgsSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.studentService.update(input);
|
|
||||||
}),
|
|
||||||
softDeleteByIds: this.trpc.procedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
ids: z.array(z.string()),
|
|
||||||
data: StudentUpdateArgsSchema.nullish(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.studentService.softDeleteByIds(input.ids);
|
|
||||||
}),
|
|
||||||
delete: this.trpc.procedure
|
|
||||||
.input(StudentWhereUniqueInputSchema)
|
|
||||||
.mutation(async ({ input }) => {
|
|
||||||
return await this.studentService.delete({ where: input });
|
|
||||||
}),
|
|
||||||
|
|
||||||
findMany: this.trpc.procedure
|
|
||||||
.input(StudentFindManyArgsSchema)
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.studentService.findMany(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
findByClass: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.studentService.findByClass(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
findWithScores: this.trpc.procedure
|
|
||||||
.input(z.number())
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.studentService.findWithScores(input);
|
|
||||||
}),
|
|
||||||
|
|
||||||
search: this.trpc.procedure
|
|
||||||
.input(z.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
classId: z.number().optional(),
|
|
||||||
limit: z.number().optional()
|
|
||||||
}))
|
|
||||||
.query(async ({ input }) => {
|
|
||||||
return await this.studentService.search(input);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import {
|
|
||||||
db,
|
|
||||||
Prisma,
|
|
||||||
UserProfile,
|
|
||||||
VisitType,
|
|
||||||
Post,
|
|
||||||
PostType,
|
|
||||||
RolePerms,
|
|
||||||
ResPerm,
|
|
||||||
ObjectType,
|
|
||||||
CourseMethodSchema,
|
|
||||||
} from '@nice/common';
|
|
||||||
import { MessageService } from '../message/message.service';
|
|
||||||
import { BaseService } from '../base/base.service';
|
|
||||||
import { DepartmentService } from '../department/department.service';
|
|
||||||
import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
|
||||||
import { BaseTreeService } from '../base/base.tree.service';
|
|
||||||
import { z } from 'zod';
|
|
||||||
@Injectable()
|
|
||||||
export class StudentService extends BaseTreeService<Prisma.StudentDelegate> {
|
|
||||||
constructor(
|
|
||||||
private readonly messageService: MessageService,
|
|
||||||
private readonly departmentService: DepartmentService,
|
|
||||||
) {
|
|
||||||
super(db, ObjectType.STUDENT, 'studentAncestry', true);
|
|
||||||
}
|
|
||||||
async create(
|
|
||||||
args: Prisma.StudentCreateArgs,
|
|
||||||
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
|
|
||||||
) {
|
|
||||||
const result = await db.student.create(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.STUDENT,
|
|
||||||
operation: CrudOperation.CREATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async update(args: Prisma.StudentUpdateArgs, staff?: UserProfile) {
|
|
||||||
const result = await db.student.update(args);
|
|
||||||
EventBus.emit('dataChanged', {
|
|
||||||
type: ObjectType.STUDENT,
|
|
||||||
operation: CrudOperation.UPDATED,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
async findByClass(classId: number) {
|
|
||||||
return await db.student.findMany({
|
|
||||||
where: { classId },
|
|
||||||
include: {
|
|
||||||
class: true,
|
|
||||||
scores: {
|
|
||||||
include: {
|
|
||||||
course: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 查找学生并包含成绩信息
|
|
||||||
*/
|
|
||||||
async findWithScores(studentId: number) {
|
|
||||||
return await db.student.findUnique({
|
|
||||||
where: { id: studentId },
|
|
||||||
include: {
|
|
||||||
class: true,
|
|
||||||
scores: {
|
|
||||||
include: {
|
|
||||||
course: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 搜索学生
|
|
||||||
*/
|
|
||||||
async search(params: { name?: string, classId?: number, limit?: number }) {
|
|
||||||
const { name, classId, limit = 30 } = params;
|
|
||||||
|
|
||||||
return await db.student.findMany({
|
|
||||||
where: {
|
|
||||||
...(name && { name: { contains: name } }),
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
class: true
|
|
||||||
},
|
|
||||||
take: limit
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
|
||||||
EventBus.emit("dataChanged", {
|
|
||||||
type: this.objectType,
|
|
||||||
operation,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,17 +9,14 @@ import { TaxonomyModule } from '@server/models/taxonomy/taxonomy.module';
|
||||||
import { AuthModule } from '@server/auth/auth.module';
|
import { AuthModule } from '@server/auth/auth.module';
|
||||||
import { AppConfigModule } from '@server/models/app-config/app-config.module';
|
import { AppConfigModule } from '@server/models/app-config/app-config.module';
|
||||||
import { MessageModule } from '@server/models/message/message.module';
|
import { MessageModule } from '@server/models/message/message.module';
|
||||||
|
import { PostModule } from '@server/models/post/post.module';
|
||||||
import { VisitModule } from '@server/models/visit/visit.module';
|
import { VisitModule } from '@server/models/visit/visit.module';
|
||||||
import { WebSocketModule } from '@server/socket/websocket.module';
|
import { WebSocketModule } from '@server/socket/websocket.module';
|
||||||
import { RoleMapModule } from '@server/models/rbac/rbac.module';
|
import { RoleMapModule } from '@server/models/rbac/rbac.module';
|
||||||
import { TransformModule } from '@server/models/transform/transform.module';
|
import { TransformModule } from '@server/models/transform/transform.module';
|
||||||
import { PostModule } from '@server/models/post/post.module';
|
|
||||||
import { ResourceModule } from '@server/models/resource/resource.module';
|
import { ResourceModule } from '@server/models/resource/resource.module';
|
||||||
import { GoodsModule } from '@server/models/goods/goods.module';
|
import { GoodsModule } from '@server/models/goods/goods.module';
|
||||||
import { CarModule } from '@server/models/car/car.module';
|
|
||||||
import { StudentModule } from '@server/models/student/student.module';
|
|
||||||
import { CourseModule } from '@server/models/course/course.module';
|
|
||||||
import { ScoreModule } from '@server/models/score/score.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -37,11 +34,11 @@ import { ScoreModule } from '@server/models/score/score.module';
|
||||||
VisitModule,
|
VisitModule,
|
||||||
WebSocketModule,
|
WebSocketModule,
|
||||||
ResourceModule,
|
ResourceModule,
|
||||||
|
<<<<<<< HEAD
|
||||||
GoodsModule,
|
GoodsModule,
|
||||||
CarModule,
|
=======
|
||||||
StudentModule,
|
GoodsModule
|
||||||
CourseModule,
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
ScoreModule,
|
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [TrpcService, TrpcRouter, Logger],
|
providers: [TrpcService, TrpcRouter, Logger],
|
||||||
|
|
|
@ -8,17 +8,13 @@ import * as trpcExpress from '@trpc/server/adapters/express';
|
||||||
import ws, { WebSocketServer } from 'ws';
|
import ws, { WebSocketServer } from 'ws';
|
||||||
import { AppConfigRouter } from '@server/models/app-config/app-config.router';
|
import { AppConfigRouter } from '@server/models/app-config/app-config.router';
|
||||||
import { MessageRouter } from '@server/models/message/message.router';
|
import { MessageRouter } from '@server/models/message/message.router';
|
||||||
|
import { PostRouter } from '@server/models/post/post.router';
|
||||||
import { VisitRouter } from '@server/models/visit/visit.router';
|
import { VisitRouter } from '@server/models/visit/visit.router';
|
||||||
import { RoleMapRouter } from '@server/models/rbac/rolemap.router';
|
import { RoleMapRouter } from '@server/models/rbac/rolemap.router';
|
||||||
import { TransformRouter } from '@server/models/transform/transform.router';
|
import { TransformRouter } from '@server/models/transform/transform.router';
|
||||||
import { RoleRouter } from '@server/models/rbac/role.router';
|
import { RoleRouter } from '@server/models/rbac/role.router';
|
||||||
import { ResourceRouter } from '../models/resource/resource.router';
|
import { ResourceRouter } from '../models/resource/resource.router';
|
||||||
import { GoodsRouter } from '../models/goods/goods.router';
|
|
||||||
import { PostRouter } from '@server/models/post/post.router';
|
|
||||||
import { CarRouter } from '@server/models/car/car.router';
|
|
||||||
import { StudentRouter } from '@server/models/student/student.router';
|
|
||||||
import { CourseRouter } from '@server/models/course/course.router';
|
|
||||||
import { ScoreRouter } from '@server/models/score/score.router';
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TrpcRouter {
|
export class TrpcRouter {
|
||||||
logger = new Logger(TrpcRouter.name);
|
logger = new Logger(TrpcRouter.name);
|
||||||
|
@ -36,11 +32,6 @@ export class TrpcRouter {
|
||||||
private readonly message: MessageRouter,
|
private readonly message: MessageRouter,
|
||||||
private readonly visitor: VisitRouter,
|
private readonly visitor: VisitRouter,
|
||||||
private readonly resource: ResourceRouter,
|
private readonly resource: ResourceRouter,
|
||||||
private readonly goods: GoodsRouter,
|
|
||||||
private readonly car: CarRouter,
|
|
||||||
private readonly student: StudentRouter,
|
|
||||||
private readonly course: CourseRouter,
|
|
||||||
private readonly score: ScoreRouter,
|
|
||||||
) {}
|
) {}
|
||||||
getRouter() {
|
getRouter() {
|
||||||
return;
|
return;
|
||||||
|
@ -58,11 +49,6 @@ export class TrpcRouter {
|
||||||
app_config: this.app_config.router,
|
app_config: this.app_config.router,
|
||||||
visitor: this.visitor.router,
|
visitor: this.visitor.router,
|
||||||
resource: this.resource.router,
|
resource: this.resource.router,
|
||||||
goods: this.goods.router,
|
|
||||||
car:this.car.router,
|
|
||||||
student:this.student.router,
|
|
||||||
course:this.course.router,
|
|
||||||
score:this.score.router
|
|
||||||
});
|
});
|
||||||
wss: WebSocketServer = undefined;
|
wss: WebSocketServer = undefined;
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
const ClubPage = () => {
|
|
||||||
|
|
||||||
const [studentName, setStudentName] = useState('');
|
|
||||||
const [data, setdata] = useState([])
|
|
||||||
const [editStudentId, setEditStudentId] = useState<number | null>(null);
|
|
||||||
const [editStudentName, setEditStudentName] = useState('');
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// data: car = [],
|
|
||||||
// isLoading,
|
|
||||||
// error,
|
|
||||||
// refetch
|
|
||||||
// } = api.car.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log(car);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight">车辆管理</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ClubPage;
|
|
|
@ -1,38 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
const DriverPage = () => {
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// data: car = [],
|
|
||||||
// isLoading,
|
|
||||||
// error,
|
|
||||||
// refetch
|
|
||||||
// } = api.car.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log(car);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight">车辆管理</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DriverPage;
|
|
|
@ -1,45 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
const GamePage = () => {
|
|
||||||
|
|
||||||
const [studentName, setStudentName] = useState('');
|
|
||||||
const [data, setdata] = useState([])
|
|
||||||
const [editStudentId, setEditStudentId] = useState<number | null>(null);
|
|
||||||
const [editStudentName, setEditStudentName] = useState('');
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
|
|
||||||
// const {
|
|
||||||
// data: car = [],
|
|
||||||
// isLoading,
|
|
||||||
// error,
|
|
||||||
// refetch
|
|
||||||
// } = api.car.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log(car);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight">车辆管理</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GamePage;
|
|
|
@ -1,33 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
const SortiePage = () => {
|
|
||||||
|
|
||||||
const [studentName, setStudentName] = useState('');
|
|
||||||
const [data, setdata] = useState([])
|
|
||||||
const [editStudentId, setEditStudentId] = useState<number | null>(null);
|
|
||||||
const [editStudentName, setEditStudentName] = useState('');
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight">车辆管理</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SortiePage;
|
|
|
@ -1,101 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
import { number } from 'zod';
|
|
||||||
import Pagination from '../component/Pagination';
|
|
||||||
export interface CarData {
|
|
||||||
id:number
|
|
||||||
clubId:number
|
|
||||||
name:string
|
|
||||||
club:string
|
|
||||||
score:number
|
|
||||||
}
|
|
||||||
const CarPage = () => {
|
|
||||||
const [data, setdata] = useState([])
|
|
||||||
const [FilterCar, setFilterCar] = useState([]);
|
|
||||||
const [driver, setDriver] = useState([]);
|
|
||||||
const [searchParams, setSearchParams] = useState({ term: '', type: 'name' });
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
|
||||||
const itemsPerPage = 5;
|
|
||||||
const mock: CarData[] = [
|
|
||||||
{ id: 1, clubId: 1, name: "user1", club:"chinese",score: 20},
|
|
||||||
{ id: 2, clubId: 2, name: "user2", club:"chinese",score: 20},
|
|
||||||
{ id: 3, clubId: 3, name: "user3", club:"chinese",score: 20},
|
|
||||||
{ id: 4, clubId: 4, name: "user4", club:"chinese",score: 20},
|
|
||||||
{ id: 5, clubId: 5, name: "user5", club:"chinese",score: 20},
|
|
||||||
{ id: 6, clubId: 6, name: "user6", club:"chinese",score: 20},
|
|
||||||
]
|
|
||||||
// const {
|
|
||||||
// data: car = [],
|
|
||||||
// isLoading,
|
|
||||||
// error,
|
|
||||||
// refetch
|
|
||||||
// } = api.car.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log(car);//查询car数据
|
|
||||||
//搜索
|
|
||||||
const handleSearch = (term, type) => {
|
|
||||||
if (!term.trim()) {
|
|
||||||
setFilterCar(mock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let filtered = [];
|
|
||||||
const lowerTerm = term.toLowerCase();
|
|
||||||
switch (type) {
|
|
||||||
case 'name':
|
|
||||||
filtered = mock.filter(mock => {
|
|
||||||
const car = driver.find(s => s.id === mock.clubId);
|
|
||||||
return car && car.name.toLowerCase().includes(lowerTerm);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight mb-10">车辆管理</h1>
|
|
||||||
{/* // 遍历 data 并渲染每个元素 */}
|
|
||||||
<table className='table-auto w-full'>
|
|
||||||
<thead>
|
|
||||||
<tr className='bg-white'>
|
|
||||||
<th className='px-4 py-2'>id</th>
|
|
||||||
<th className='px-4 py-2'>clubId</th>
|
|
||||||
<th className='px-4 py-2'>name</th>
|
|
||||||
<th className='px-4 py-2'>club</th>
|
|
||||||
<th className='px-4 py-2'>score</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{mock?.map((i) => (
|
|
||||||
<tr key={i.id} className='hover:bg-gray-100 bg-yellow-700'>
|
|
||||||
<td className='border px-4 py-2'>{i.id}</td>
|
|
||||||
<td className='border px-4 py-2'>{i.clubId}</td>
|
|
||||||
<td className='border px-4 py-2'>{i.name}</td>
|
|
||||||
<td className='border px-4 py-2'>{i.club}</td>
|
|
||||||
<td className='border px-4 py-2'>{i.score}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<StudentSearch onSearch={handleSearch} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CarPage;
|
|
|
@ -1,15 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import bgImage from '../../../assets/student.png';
|
|
||||||
const Header = () => {
|
|
||||||
return (
|
|
||||||
<div className="w-full bg-cover bg-center h-64" style={{ backgroundImage: `url(${bgImage})` }}>
|
|
||||||
<div className="flex items-center justify-center h-full w-full bg-opacity-50 bg-black">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold text-white leading-tight">学生成绩管理系统</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
|
@ -1,52 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const Pagination = ({ currentPage, totalPages, onPageChange }) => {
|
|
||||||
const pages = [];
|
|
||||||
for (let i = 1; i <= totalPages; i++) {
|
|
||||||
pages.push(i);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center mt-4">
|
|
||||||
<nav className="flex items-center">
|
|
||||||
<button
|
|
||||||
onClick={() => onPageChange(currentPage - 1)}
|
|
||||||
disabled={currentPage === 1}
|
|
||||||
className={`mx-1 px-3 py-1 rounded-md ${
|
|
||||||
currentPage === 1
|
|
||||||
? 'text-gray-400 cursor-not-allowed'
|
|
||||||
: 'text-blue-500 hover:bg-blue-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
上一页
|
|
||||||
</button>
|
|
||||||
{pages.map(page => (
|
|
||||||
<button
|
|
||||||
key={page}
|
|
||||||
onClick={() => onPageChange(page)}
|
|
||||||
className={`mx-1 px-3 py-1 rounded-md ${
|
|
||||||
currentPage === page
|
|
||||||
? 'bg-blue-500 text-white'
|
|
||||||
: 'text-blue-500 hover:bg-blue-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{page}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => onPageChange(currentPage + 1)}
|
|
||||||
disabled={currentPage === totalPages}
|
|
||||||
className={`mx-1 px-3 py-1 rounded-md ${
|
|
||||||
currentPage === totalPages
|
|
||||||
? 'text-gray-400 cursor-not-allowed'
|
|
||||||
: 'text-blue-500 hover:bg-blue-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
下一页
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Pagination;
|
|
|
@ -1,155 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
|
|
||||||
const ScoreForm = ({ onSubmit, editingScore = null, students, courses }) => {
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
studentId: '',
|
|
||||||
courseId: '',
|
|
||||||
score: '',
|
|
||||||
semester: ''
|
|
||||||
});
|
|
||||||
const [students1, setStudents1] = useState([]);
|
|
||||||
const [courses1, setCourses1] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 如果是编辑模式,使用editingScore填充表单
|
|
||||||
if (editingScore) {
|
|
||||||
setFormData({
|
|
||||||
studentId: editingScore.studentId,
|
|
||||||
courseId: editingScore.courseId,
|
|
||||||
score: editingScore.score,
|
|
||||||
semester: editingScore.semester
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fetchStudentsAndCourses();
|
|
||||||
}, [editingScore]);
|
|
||||||
|
|
||||||
const fetchStudentsAndCourses = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
// 模拟API调用
|
|
||||||
setTimeout(() => {
|
|
||||||
setStudents1(students);
|
|
||||||
setCourses1(courses);
|
|
||||||
setLoading(false);
|
|
||||||
}, 500);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取数据失败', error);
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (e) => {
|
|
||||||
const { name, value } = e.target;
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
[name]: name === 'score' ? parseFloat(value) : value
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
onSubmit(formData);
|
|
||||||
// 重置表单
|
|
||||||
if (!editingScore) {
|
|
||||||
setFormData({
|
|
||||||
studentId: '',
|
|
||||||
courseId: '',
|
|
||||||
score: '',
|
|
||||||
semester: ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow mb-6">
|
|
||||||
<h2 className="text-xl font-semibold mb-4">{editingScore ? '编辑成绩' : '添加成绩'}</h2>
|
|
||||||
{loading ? (
|
|
||||||
<div className="text-center py-4">加载中...</div>
|
|
||||||
) : (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">学生</label>
|
|
||||||
<select
|
|
||||||
name="studentId"
|
|
||||||
value={formData.studentId}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">选择学生</option>
|
|
||||||
{students.map(student => (
|
|
||||||
<option key={student.id} value={student.id}>
|
|
||||||
{student.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">课程</label>
|
|
||||||
<select
|
|
||||||
name="courseId"
|
|
||||||
value={formData.courseId}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">选择课程</option>
|
|
||||||
{courses.map(course => (
|
|
||||||
<option key={course.id} value={course.id}>
|
|
||||||
{course.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">分数</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="score"
|
|
||||||
value={formData.score}
|
|
||||||
onChange={handleChange}
|
|
||||||
placeholder="输入分数"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
step="0.1"
|
|
||||||
className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">学期</label>
|
|
||||||
<select
|
|
||||||
name="semester"
|
|
||||||
value={formData.semester}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">选择学期</option>
|
|
||||||
<option value="2023-春季">2023-春季</option>
|
|
||||||
<option value="2023-秋季">2023-秋季</option>
|
|
||||||
<option value="2024-春季">2024-春季</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-md transition duration-200"
|
|
||||||
>
|
|
||||||
{editingScore ? '更新成绩' : '添加成绩'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ScoreForm;
|
|
|
@ -1,102 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Pagination from './Pagination';
|
|
||||||
const ScoreTable = ({ scores, students, courses, currentPage, totalPages, onPageChange, onEdit, onDelete }) => {
|
|
||||||
// 获取学生和课程名称的辅助函数
|
|
||||||
const getStudentName = (studentId) => {
|
|
||||||
if (!students || !Array.isArray(students)) return '未知学生';
|
|
||||||
if (studentId === undefined || studentId === null) return '未知学生';
|
|
||||||
|
|
||||||
const student = students.find(s => s.id === studentId);
|
|
||||||
return student && student.name ? student.name : '未知学生';
|
|
||||||
};
|
|
||||||
const getCourseName = (courseId) => {
|
|
||||||
if (!courses || !Array.isArray(courses)) return '未知课程';
|
|
||||||
if (courseId === undefined || courseId === null) return '未知课程';
|
|
||||||
|
|
||||||
const course = courses.find(c => c.id === courseId);
|
|
||||||
return course && course.name ? course.name : '未知课程';
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
|
||||||
<h2 className="text-xl font-semibold p-4 border-b">成绩列表</h2>
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
学生姓名
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
课程
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
分数
|
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
操作
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{scores.length > 0 ? (
|
|
||||||
scores.map((score) => (
|
|
||||||
<tr key={score.id} className="hover:bg-gray-50">
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
{getStudentName(score.studentId)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
{getCourseName(score.courseId)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
|
||||||
<span className={`px-2 py-1 rounded-full text-sm ${
|
|
||||||
score.score >= 90 ? 'bg-green-100 text-green-800' :
|
|
||||||
score.score >= 60 ? 'bg-blue-100 text-blue-800' :
|
|
||||||
'bg-red-100 text-red-800'
|
|
||||||
}`}>
|
|
||||||
{score.score}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<button
|
|
||||||
onClick={() => onEdit(score)}
|
|
||||||
className="text-indigo-600 hover:text-indigo-900 mr-4"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => onDelete(score.id)}
|
|
||||||
className="text-red-600 hover:text-red-900"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<tr>
|
|
||||||
<td colSpan="5" className="px-6 py-4 text-center text-gray-500">
|
|
||||||
没有找到成绩记录
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{scores.length > 0 && (
|
|
||||||
<div className="px-4 py-3 border-t border-gray-200">
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
totalPages={totalPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ScoreTable;
|
|
|
@ -1,46 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
const StudentSearch = ({ onSearch }) => {
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
const [searchType, setSearchType] = useState('name'); // 'name', 'class', 'course'
|
|
||||||
|
|
||||||
const handleSearch = () => {
|
|
||||||
onSearch(searchTerm, searchType);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-white p-4 rounded-lg shadow mb-6">
|
|
||||||
<h2 className="text-xl font-semibold mb-4">学生搜索</h2>
|
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
|
||||||
<div className="flex-1">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="输入搜索关键词..."
|
|
||||||
className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-full md:w-48">
|
|
||||||
<select
|
|
||||||
className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
value={searchType}
|
|
||||||
onChange={(e) => setSearchType(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="name">按学生姓名</option>
|
|
||||||
<option value="class">按班级</option>
|
|
||||||
<option value="course">按课程</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-md transition duration-200"
|
|
||||||
onClick={handleSearch}
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StudentSearch;
|
|
|
@ -1,33 +1,79 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
|
import { api } from "@nice/client";
|
||||||
|
import { apiClient } from "@web/src/utils";
|
||||||
|
|
||||||
|
import { Button, Tag } from "antd";
|
||||||
|
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
function HomePage() {
|
||||||
|
// 使用 useQuery 钩子从 API 获取数据
|
||||||
|
|
||||||
|
const { data } = api.staff.findMany.useQuery({
|
||||||
|
take: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义 counter 状态和更新函数
|
||||||
|
const [counter, setCounter] = useState<number>(0);
|
||||||
|
|
||||||
|
// 使用 useMemo 记忆化 counterText,仅在 counter 变化时重新计算
|
||||||
|
const counterText = useMemo(() => {
|
||||||
|
return `当前计数为: ${counter}`;
|
||||||
|
}, [counter]);
|
||||||
|
|
||||||
|
const getData = async()=>{
|
||||||
|
const res = @wait apiClient.get(*/goods/hello*)
|
||||||
|
console.log(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 useEffect 在 data 变化时打印 data
|
||||||
|
useEffect(() => {
|
||||||
|
apiClient.get(“/goods/hello”)
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-2 space-y-2">
|
||||||
|
<Tag>{counterText}</Tag>
|
||||||
|
<div className="space-x-2">
|
||||||
|
<Button type="primary" onClick={() => setCounter(counter + 1)}>
|
||||||
|
加1
|
||||||
|
</Button>
|
||||||
|
<Button danger onClick={() => setCounter(counter - 1)}>
|
||||||
|
减1
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{/* 如果 data 存在,遍历并渲染每个项目的 username */}
|
||||||
|
{data?.map((item) => (
|
||||||
|
<div className="p-2 rounded border shadow" key={item.username}>
|
||||||
|
<Tag>{item.username}</Tag>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomePage;
|
||||||
|
=======
|
||||||
import { api } from "@nice/client"
|
import { api } from "@nice/client"
|
||||||
import { apiClient } from "@web/src/utils"
|
import { apiClient } from "@web/src/utils"
|
||||||
import { Button, Tag } from "antd"
|
import { Button, Tag } from "antd"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
const { data: staffData } = api.staff.findMany.useQuery({
|
const { data } = api.staff.findMany.useQuery({
|
||||||
take: 10
|
take: 10
|
||||||
})
|
})
|
||||||
// 建议为不同的查询结果使用不同的变量名
|
|
||||||
const { data: goodsData } = api.goods.hello.useQuery({
|
|
||||||
name: "123"
|
|
||||||
})
|
|
||||||
|
|
||||||
const [counter, setCounter] = useState<number>(0)
|
const [counter, setCounter] = useState<number>(0)
|
||||||
const counterText = useMemo(() => {
|
const counterText = useMemo(() => {
|
||||||
return `当前计数为:${counter}`
|
return `当前计数为:${counter}`
|
||||||
}, [counter])
|
}, [counter])
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
// 第一种写法
|
const res = await apiClient.get("/goods/hello")
|
||||||
// const res = await apiClient.get("/goods/hello")
|
console.log(res)
|
||||||
// console.log(res)
|
|
||||||
//第二种写法
|
|
||||||
apiClient.get("/goods/hello")
|
|
||||||
.then(res => {
|
|
||||||
console.log(res)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
useEffect(() => { getData() }, [])
|
useEffect(() => {
|
||||||
|
getData()
|
||||||
|
}, [])
|
||||||
return <div className="p-2 space-y-2">
|
return <div className="p-2 space-y-2">
|
||||||
<Tag>{counterText}</Tag>
|
<Tag>{counterText}</Tag>
|
||||||
<div className="space-x-2" >
|
<div className="space-x-2" >
|
||||||
|
@ -41,15 +87,15 @@ function HomePage() {
|
||||||
>减1</Button>
|
>减1</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap">{
|
{
|
||||||
staffData?.map(i => {
|
data?.map(i => {
|
||||||
return <div className="p-2 rounded border shadow" key={i.id}>
|
return <div className="p-2 rounded border shadow">
|
||||||
<Tag>{i.username}</Tag>
|
<Tag>{i.username}</Tag>
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
} </div>
|
}
|
||||||
<Tag className="text-2xl">{goodsData}</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
// export { HomePage }
|
// export { HomePage }
|
||||||
export default HomePage
|
export default HomePage
|
||||||
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { api } from "@nice/client"
|
||||||
|
import { apiClient } from '@web/src/utils';
|
||||||
|
|
||||||
|
function PersonCard(){
|
||||||
|
const {data} = api.staff.findMany.useQuery({
|
||||||
|
take: 10
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
},[data])
|
||||||
|
|
||||||
|
const getdata=async ()=>{
|
||||||
|
const res=await apiClient.get("/goods/hello")
|
||||||
|
console.log(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{data?.map((i, index) => (
|
||||||
|
<div key={index} className="bg-white rounded-lg shadow-sm p-6 hover:shadow-md transition-shadow duration-300">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">{i.username}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PersonCard;
|
|
@ -1,87 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Outlet, Link, useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
const MainLayout: React.FC = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
const pathnames = location.pathname.split('/').filter((x) => x);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen flex">
|
|
||||||
{/* 侧边栏 */}
|
|
||||||
<div className="w-64 bg-gray-800 text-white p-4">
|
|
||||||
<nav className="space-y-2">
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
学生管理
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/car"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
车辆管理
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/club"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
俱乐部管理
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/driver"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
车手管理
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/game"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
比赛管理
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to="/sortie"
|
|
||||||
className="block px-4 py-2 text-sm font-medium rounded-md hover:bg-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
车次管理
|
|
||||||
</Link>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
{/* 主内容区域 */}
|
|
||||||
<div className="flex-1 flex flex-col">
|
|
||||||
{/* 顶部导航栏 */}
|
|
||||||
<div className="bg-blue-600 text-white p-4">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Link to="/" className="hover:text-gray-200">
|
|
||||||
首页
|
|
||||||
</Link>
|
|
||||||
{pathnames.map((value, index) => {
|
|
||||||
const to = `/${pathnames.slice(0, index + 1).join('/')}`;
|
|
||||||
const isLast = index === pathnames.length - 1;
|
|
||||||
return isLast ? (
|
|
||||||
<span key={to} className="font-medium">
|
|
||||||
/ {value}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<Link key={to} to={to} className="hover:text-gray-200">
|
|
||||||
/ {value}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* 主要内容 */}
|
|
||||||
<div className="flex-1 p-4">
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
{/* 底部 */}
|
|
||||||
<div className="bg-gray-800 text-white p-4">
|
|
||||||
Footer
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MainLayout;
|
|
|
@ -1,251 +0,0 @@
|
||||||
'use client';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { Modal, Form, Input, Button } from 'antd';
|
|
||||||
const StudentTestPage = () => {
|
|
||||||
const [studentName, setStudentName] = useState('');
|
|
||||||
const [data, setdata] = useState([])
|
|
||||||
const [editStudentId, setEditStudentId] = useState<number | null>(null);
|
|
||||||
const [editStudentName, setEditStudentName] = useState('');
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: students = [],
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
refetch
|
|
||||||
} = api.student.findMany.useQuery({
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// setdata(students)
|
|
||||||
console.log('students', students);
|
|
||||||
useEffect(() => {
|
|
||||||
setdata(students)
|
|
||||||
}, [students])
|
|
||||||
const filteredStudents = students.filter(student =>
|
|
||||||
student.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
student.id.toString().includes(searchTerm)
|
|
||||||
|
|
||||||
);
|
|
||||||
// const {data:students, isLoading, error} = api.student.findMany.useQuery({
|
|
||||||
// where: {
|
|
||||||
// id: 1
|
|
||||||
// },
|
|
||||||
// select: {
|
|
||||||
// name: true,
|
|
||||||
// id: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log('students', students);
|
|
||||||
|
|
||||||
// const {data:courses, isLoading, error} = api.course.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// scores: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log('courses',courses);
|
|
||||||
|
|
||||||
// const {data:scores, isLoading, error} = api.score.findMany.useQuery({
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// studentId: true,
|
|
||||||
// courseId: true,
|
|
||||||
// score: true,
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// console.log('scores',scores);
|
|
||||||
|
|
||||||
// const handleAddStudent = api.student.create.useMutation({
|
|
||||||
// onSuccess: () => {
|
|
||||||
// console.log('添加学生成功');
|
|
||||||
// },
|
|
||||||
// onError: (error) => {
|
|
||||||
// console.log('添加分数失败', error);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// handleAddStudent.mutate({
|
|
||||||
// data: {
|
|
||||||
// name: '张三',
|
|
||||||
// classId: 10,
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
const deleteStudentMutation = api.student.delete.useMutation({
|
|
||||||
onSuccess: () => {
|
|
||||||
console.log('删除成功');
|
|
||||||
refetch()
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error('删除失败:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const handleDeleteStudent = (studentId: number) => {
|
|
||||||
deleteStudentMutation.mutateAsync({ id: studentId });
|
|
||||||
};
|
|
||||||
const editStudentMutation = api.student.update.useMutation({
|
|
||||||
onSuccess: () => {
|
|
||||||
console.log('编辑成功');
|
|
||||||
refetch()
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error('编辑失败:', error);
|
|
||||||
alert(`编辑失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const handleEditStudent = (studentId?: number, name?: string) => {
|
|
||||||
if (studentId ) {
|
|
||||||
const studentIdNumber = Number(studentId);
|
|
||||||
editStudentMutation.mutate({ where: { id: studentIdNumber }, data: { name: name } });
|
|
||||||
setEditStudentId(studentIdNumber);
|
|
||||||
setEditStudentName(name);
|
|
||||||
setIsModalOpen(true);
|
|
||||||
refetch()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleOk = (studentId?: number, name?: string) => {
|
|
||||||
if (studentId && editStudentName) {
|
|
||||||
const studentIdNumber = Number(editStudentId);
|
|
||||||
editStudentMutation.mutate({
|
|
||||||
where: { id: editStudentId },
|
|
||||||
data: { name: editStudentName }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsModalOpen(false);
|
|
||||||
setEditStudentId(null);
|
|
||||||
setEditStudentName('');
|
|
||||||
refetch()
|
|
||||||
};
|
|
||||||
const handleCancel = () => {
|
|
||||||
setIsModalOpen(false);
|
|
||||||
refetch()
|
|
||||||
};
|
|
||||||
|
|
||||||
// 增加
|
|
||||||
const addStudentMutation = api.student.create.useMutation({
|
|
||||||
onSuccess: (data) => {
|
|
||||||
console.log('添加成功:', data);
|
|
||||||
setStudentName(''); // 清空输入
|
|
||||||
// 刷新列表
|
|
||||||
refetch()
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error('添加失败:', error);
|
|
||||||
alert(`添加失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const handleAddStudent = () => {
|
|
||||||
|
|
||||||
// 正确构造Prisma创建参数
|
|
||||||
addStudentMutation.mutateAsync({
|
|
||||||
data: {
|
|
||||||
name: studentName
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-5">
|
|
||||||
<h1 className="text-xl font-bold mb-4">API测试</h1>
|
|
||||||
{/* 添加搜索框 */}
|
|
||||||
<div className="mb-4">
|
|
||||||
<Input.Search
|
|
||||||
placeholder="搜索学生姓名或ID"
|
|
||||||
allowClear
|
|
||||||
enterButton
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
onSearch={setSearchTerm}
|
|
||||||
className="max-w-md"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-4">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={studentName}
|
|
||||||
onChange={(e) => setStudentName(e.target.value)}
|
|
||||||
placeholder="学生姓名"
|
|
||||||
className="border rounded px-3 py-2 flex-1"
|
|
||||||
/>
|
|
||||||
<button onClick={() => handleAddStudent()} className='bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600'>添加</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isLoading && <div>加载中...</div>}
|
|
||||||
|
|
||||||
{/* 使用过滤后的学生列表 */}
|
|
||||||
{filteredStudents.length > 0 ? (
|
|
||||||
filteredStudents.map((student) => (
|
|
||||||
<div key={student.id} className="mb-3 p-3 border rounded flex justify-between items-center">
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">ID: {student.id}</p>
|
|
||||||
<p>姓名: {student.name}</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button onClick={() => handleEditStudent(student.id, student.name)}
|
|
||||||
className='bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600'>
|
|
||||||
编辑
|
|
||||||
</button>
|
|
||||||
<button onClick={() => handleDeleteStudent(student.id)}
|
|
||||||
className='bg-red-500 text-white px-3 py-1 rounded-md hover:bg-red-600'>
|
|
||||||
删除
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="text-gray-500 my-4">未找到匹配的学生</div>
|
|
||||||
)}
|
|
||||||
{/* {isLoading && <div className="text-gray-600">加载中...</div>}
|
|
||||||
{error && (
|
|
||||||
<div className="bg-red-100 border-l-4 border-red-500 p-4 mb-5">
|
|
||||||
<p className="font-bold">错误</p>
|
|
||||||
<p>{error.message}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!isLoading && !error && (
|
|
||||||
<div>
|
|
||||||
<p className="mb-2">成功获取 {scores.length} 分数:</p>
|
|
||||||
<pre className="bg-gray-100 p-4 rounded overflow-auto max-h-96">
|
|
||||||
{JSON.stringify(scores, null, 2)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
{/* {isLoading && <div>加载中...</div>}
|
|
||||||
{students && students.length > 0 &&
|
|
||||||
students.map((student) => (
|
|
||||||
<div key={student.id}>
|
|
||||||
<p>ID: {student.id}</p>
|
|
||||||
<p>姓名: {student.name}</p>
|
|
||||||
<button onClick={() => handleDeleteStudent(student.id)} className='bg-red-500 text-white px-4 py-2 rounded-md'>删除</button>
|
|
||||||
<div>
|
|
||||||
<button onClick={() => handleEditStudent(student.id,student.name)} className='bg-blue-500 text-white px-4 py-2 rounded-md'>编辑</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
} */}
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="编辑学生"
|
|
||||||
open={isModalOpen}
|
|
||||||
onOk={() => handleOk(editStudentId, editStudentName)}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={editStudentName}
|
|
||||||
onChange={(e) => setEditStudentName(e.target.value)}
|
|
||||||
placeholder="输入学生姓名"
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StudentTestPage;
|
|
|
@ -1,189 +0,0 @@
|
||||||
'use client';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import Header from '../component/Header';
|
|
||||||
import StudentSearch from '../component/StudentSearch';
|
|
||||||
import ScoreForm from '../component/ScoreForm';
|
|
||||||
import ScoreTable from '../component/ScoreTable';
|
|
||||||
import { api } from '@nice/client';
|
|
||||||
const StudentPage = () => {
|
|
||||||
const [scores, setScores] = useState([]);
|
|
||||||
const [filteredScores, setFilteredScores] = useState([]);
|
|
||||||
const [students, setStudents] = useState([]);
|
|
||||||
const [courses, setCourses] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
|
||||||
const [editingScore, setEditingScore] = useState(null);
|
|
||||||
const [searchParams, setSearchParams] = useState({ term: '', type: 'name' });
|
|
||||||
const itemsPerPage = 5;
|
|
||||||
const {
|
|
||||||
data: studentsData = [],
|
|
||||||
isLoading,
|
|
||||||
error } = api.student.findMany.useQuery({
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
classId: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log('学生',studentsData);
|
|
||||||
// 模拟API调用获取数据
|
|
||||||
const {data:coursesData} = api.course.findMany.useQuery({
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
scores: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log('课程',coursesData);
|
|
||||||
const {data:scoresData} = api.score.findMany.useQuery({
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
studentId: true,
|
|
||||||
courseId: true,
|
|
||||||
score: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log('成绩',scoresData);
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchData = async () => {
|
|
||||||
try {
|
|
||||||
// 这里应该替换为实际的API调用
|
|
||||||
setTimeout(async () => {
|
|
||||||
setStudents(studentsData);
|
|
||||||
setCourses(coursesData);
|
|
||||||
setScores(scoresData);
|
|
||||||
setFilteredScores(scoresData);
|
|
||||||
setLoading(false);
|
|
||||||
}, 1000);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取数据失败', error);
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
// 处理搜索
|
|
||||||
const handleSearch = (term, type) => {
|
|
||||||
setSearchParams({ term, type });
|
|
||||||
if (!term.trim()) {
|
|
||||||
setFilteredScores(scores);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let filtered = [];
|
|
||||||
const lowerTerm = term.toLowerCase();
|
|
||||||
switch (type) {
|
|
||||||
case 'name':
|
|
||||||
filtered = scores.filter(score => {
|
|
||||||
const student = students.find(s => s.id === score.studentId);
|
|
||||||
return student && student.name.toLowerCase().includes(lowerTerm);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'class':
|
|
||||||
filtered = scores.filter(score => {
|
|
||||||
const student = students.find(s => s.id === score.studentId);
|
|
||||||
const className = student ? student.classId : null;
|
|
||||||
return className && className.toString().includes(lowerTerm);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'course':
|
|
||||||
filtered = scores.filter(score => {
|
|
||||||
const course = courses.find(c => c.id === score.courseId);
|
|
||||||
return course && course.name.toLowerCase().includes(lowerTerm);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
filtered = scores;
|
|
||||||
}
|
|
||||||
setFilteredScores(filtered);
|
|
||||||
setCurrentPage(1); // 重置到第一页
|
|
||||||
};
|
|
||||||
// // 处理添加/编辑成绩
|
|
||||||
const handleScoreSubmit = (formData) => {
|
|
||||||
if (editingScore) {
|
|
||||||
// 更新成绩
|
|
||||||
const updatedScores = scoresData.map(score =>
|
|
||||||
score.id === editingScore.id ? { ...score, ...formData } : score
|
|
||||||
);
|
|
||||||
setScores(updatedScores);
|
|
||||||
setFilteredScores(
|
|
||||||
filteredScores.map(score =>
|
|
||||||
score.id === editingScore.id ? { ...score, ...formData } : score
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setEditingScore(null);
|
|
||||||
} else {
|
|
||||||
// 添加新成绩
|
|
||||||
const newScore = {
|
|
||||||
id: scoresData.length + 1, // 实际项目中应该由后端生成
|
|
||||||
...formData
|
|
||||||
};
|
|
||||||
setScores([...scoresData, newScore]);
|
|
||||||
setFilteredScores([...filteredScores, newScore]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// // 处理编辑成绩
|
|
||||||
const handleEditScore = (score) => {
|
|
||||||
setEditingScore(score);
|
|
||||||
// 滚动到表单位置
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理删除成绩
|
|
||||||
const handleDeleteScore = (id) => {
|
|
||||||
const updatedScores = scoresData.filter(score => score.id !== id);
|
|
||||||
setScores(updatedScores);
|
|
||||||
setFilteredScores(filteredScores.filter(score => score.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
// 分页相关
|
|
||||||
const indexOfLastItem = currentPage * itemsPerPage;
|
|
||||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
|
||||||
const currentItems = Array.isArray(filteredScores)
|
|
||||||
? filteredScores.slice(indexOfFirstItem, indexOfLastItem)
|
|
||||||
: [];
|
|
||||||
const totalPages = Math.ceil((filteredScores?.length || 0) / itemsPerPage);
|
|
||||||
const handlePageChange = (pageNumber) => {
|
|
||||||
setCurrentPage(pageNumber);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-100">
|
|
||||||
<Header />
|
|
||||||
<div className="container mx-auto px-4 py-8">
|
|
||||||
<div className="flex justify-center items-center h-64">
|
|
||||||
<div className="text-xl text-gray-600">加载中...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-100">
|
|
||||||
<Header />
|
|
||||||
<div className="container mx-auto px-4 py-8">
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
||||||
<div className="lg:col-span-1">
|
|
||||||
<ScoreForm onSubmit={handleScoreSubmit} editingScore={editingScore} students={studentsData} courses={coursesData} />
|
|
||||||
</div>
|
|
||||||
<div className="lg:col-span-2">
|
|
||||||
<StudentSearch onSearch={handleSearch} />
|
|
||||||
<ScoreTable
|
|
||||||
scores={scoresData || []}
|
|
||||||
students={studentsData || []}
|
|
||||||
courses={coursesData || []}
|
|
||||||
currentPage={currentPage}
|
|
||||||
totalPages={totalPages}
|
|
||||||
onPageChange={handlePageChange}
|
|
||||||
onEdit={handleEditScore}
|
|
||||||
onDelete={handleDeleteScore}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StudentPage;
|
|
Binary file not shown.
|
@ -7,61 +7,25 @@ import LoginPage from "../app/login";
|
||||||
import { adminRoute } from "./admin-route";
|
import { adminRoute } from "./admin-route";
|
||||||
import { CustomRouteObject } from "./types";
|
import { CustomRouteObject } from "./types";
|
||||||
import { MainRoute } from "./main-route";
|
import { MainRoute } from "./main-route";
|
||||||
import HomePage from "../app/main/home/page";
|
|
||||||
import StudentPage from "../app/main/student/page";
|
|
||||||
import StudentTestPage from "../app/main/student/StudentTestPage";
|
|
||||||
import CarPage from "../app/main/car/page";
|
|
||||||
import MainLayout from "../app/main/layout/MainLayout";
|
|
||||||
import ClubPage from "../app/main/car/Club";
|
|
||||||
import DriverPage from "../app/main/car/Driver";
|
|
||||||
import GamePage from "../app/main/car/Game";
|
|
||||||
import SortiePage from "../app/main/car/Sortie";
|
|
||||||
export const routes: CustomRouteObject[] = [
|
export const routes: CustomRouteObject[] = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <MainLayout />,
|
errorElement: <ErrorPage />,
|
||||||
|
handle: {
|
||||||
|
crumb() {
|
||||||
|
return <Link to={"/"}>主页</Link>;
|
||||||
|
},
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
MainRoute,
|
||||||
path: "/login",
|
adminRoute,
|
||||||
element: <LoginPage></LoginPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
element: <StudentPage></StudentPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/student-test",
|
|
||||||
element: <StudentTestPage></StudentTestPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/student",
|
|
||||||
element: <StudentPage></StudentPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/car",
|
|
||||||
element: <CarPage></CarPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/club",
|
|
||||||
element: <ClubPage></ClubPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/driver",
|
|
||||||
element: <DriverPage></DriverPage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/game",
|
|
||||||
element: <GamePage></GamePage>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/sortie",
|
|
||||||
element: <SortiePage></SortiePage>,
|
|
||||||
},
|
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
element: <LoginPage></LoginPage>,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const router = createBrowserRouter(routes);
|
export const router = createBrowserRouter(routes);
|
||||||
|
|
|
@ -6,7 +6,7 @@ export const MainRoute: CustomRouteObject = {
|
||||||
element: <MainLayout></MainLayout>,
|
element: <MainLayout></MainLayout>,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true, //默认为主页,app进入第一次进入这个页面
|
||||||
element: <HomePage />,
|
element: <HomePage />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -14,7 +14,7 @@ done
|
||||||
# Check if the index.html file exists before processing
|
# Check if the index.html file exists before processing
|
||||||
if [ -f "/usr/share/nginx/html/index.html" ]; then
|
if [ -f "/usr/share/nginx/html/index.html" ]; then
|
||||||
# Use envsubst to replace environment variable placeholders
|
# Use envsubst to replace environment variable placeholders
|
||||||
envsubst < /usr/share/nginx/html/index.template > /usr/share/nginx/html/index.html.tmp
|
envsubst < /usr/share/nginx/html/index.html > /usr/share/nginx/html/index.html.tmp
|
||||||
mv /usr/share/nginx/html/index.html.tmp /usr/share/nginx/html/index.html
|
mv /usr/share/nginx/html/index.html.tmp /usr/share/nginx/html/index.html
|
||||||
else
|
else
|
||||||
echo "Info: /usr/share/nginx/html/index.html does not exist , skip replace env"
|
echo "Info: /usr/share/nginx/html/index.html does not exist , skip replace env"
|
||||||
|
|
|
@ -35,7 +35,7 @@ services:
|
||||||
pgadmin:
|
pgadmin:
|
||||||
image: dpage/pgadmin4
|
image: dpage/pgadmin4
|
||||||
ports:
|
ports:
|
||||||
- "8082:8090"
|
- "8082:80"
|
||||||
environment:
|
environment:
|
||||||
- PGADMIN_DEFAULT_EMAIL=insiinc@outlook.com
|
- PGADMIN_DEFAULT_EMAIL=insiinc@outlook.com
|
||||||
- PGADMIN_DEFAULT_PASSWORD=Letusdoit000
|
- PGADMIN_DEFAULT_PASSWORD=Letusdoit000
|
||||||
|
@ -64,7 +64,7 @@ services:
|
||||||
# extra_hosts:
|
# extra_hosts:
|
||||||
# - "host.docker.internal:host-gateway"
|
# - "host.docker.internal:host-gateway"
|
||||||
nginx:
|
nginx:
|
||||||
image: nice-nginx:2.0
|
image: nice-nginx:latest
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -74,8 +74,7 @@ services:
|
||||||
- ./web-dist:/usr/share/nginx/html # 添加前端构建文件的挂载
|
- ./web-dist:/usr/share/nginx/html # 添加前端构建文件的挂载
|
||||||
- ./config/nginx/entrypoint.sh:/docker-entrypoint.sh
|
- ./config/nginx/entrypoint.sh:/docker-entrypoint.sh
|
||||||
environment:
|
environment:
|
||||||
- SERVER_IP=192.168.0.14
|
- SERVER_IP=host.docker.internal
|
||||||
- SERVER_NAME=nice-playground
|
|
||||||
entrypoint: ["/docker-entrypoint.sh"]
|
entrypoint: ["/docker-entrypoint.sh"]
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
|
@ -132,4 +131,4 @@ services:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
name: nice-network
|
name: remooc
|
||||||
|
|
|
@ -245,8 +245,8 @@ model PostAncestry {
|
||||||
|
|
||||||
// 复合索引优化
|
// 复合索引优化
|
||||||
// 索引建议
|
// 索引建议
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
model Message {
|
model Message {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
url String?
|
url String?
|
||||||
|
@ -323,157 +323,55 @@ model Resource {
|
||||||
@@index([type])
|
@@index([type])
|
||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@map("resource")
|
@@map("resource")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
|
=======
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
>>>>>>> de6e632ec69dd408a6c4e85d5cda01a1aa8e8276
|
||||||
|
|
||||||
|
>>>>>>> dc75314e36f86056790758574c3426f379c16a87
|
||||||
//商品表
|
//商品表
|
||||||
model Goods {
|
model Goods {
|
||||||
id String @id @default(cuid()) // 商品ID
|
id String @id @default(cuid()) // 商品ID
|
||||||
name String @unique // 商品名称
|
name String @unique // 商品名称
|
||||||
description String? // 商品描述
|
description String? // 商品描述
|
||||||
price Float @default(0.0) // 商品价格
|
price Float @default(0.0) // 商品价格
|
||||||
images String[] @default([]) // 商品图片
|
images String[] @default([]) // 商品图片
|
||||||
tags Tag[] @relation("GoodsTags") // 多对多关系
|
tags Tag[] @relation("GoodsTags") // 多对多关系
|
||||||
reviews Review[] // 一对多关系
|
reviews Review[] // 一对多关系
|
||||||
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") // 删除时间,可为空
|
deletedAt DateTime? @map("deleted_at") // 删除时间,可为空
|
||||||
|
|
||||||
@@index([name])
|
@@index([name])
|
||||||
@@map("goods")
|
@@map("goods")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标签表
|
// 标签表
|
||||||
model Tag {
|
model Tag {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String @unique
|
||||||
goods Goods[] @relation("GoodsTags")
|
goods Goods[] @relation("GoodsTags")
|
||||||
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")
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
@@map("tag")
|
@@map("tag")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Review {
|
model Review {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
content String
|
content String
|
||||||
rating Int @default(0)
|
rating Int @default(0)
|
||||||
goodsId String @map("goods_id")
|
goodsId String @map("goods_id")
|
||||||
goods Goods @relation(fields: [goodsId], references: [id])
|
goods Goods @relation(fields: [goodsId], references: [id])
|
||||||
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")
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
@@index([goodsId])
|
@@index([goodsId])
|
||||||
@@index([rating])
|
@@index([rating])
|
||||||
@@map("review")
|
@@map("review")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Club {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String? @unique
|
|
||||||
introduce String?
|
|
||||||
car Car[]
|
|
||||||
driver Driver[]
|
|
||||||
Game Game[] @relation("ClubGame")
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
|
|
||||||
@@index([name])
|
|
||||||
@@map("club")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Driver {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String?
|
|
||||||
sex String?
|
|
||||||
username String?
|
|
||||||
password String?
|
|
||||||
club Club? @relation(fields: [clubId], references: [id])
|
|
||||||
clubId Int? @map("club_id")
|
|
||||||
parentId Int?
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
|
|
||||||
@@map("driver")
|
|
||||||
@@index([name])
|
|
||||||
}
|
|
||||||
|
|
||||||
model Car {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
clubId Int?
|
|
||||||
name String? @unique
|
|
||||||
type String?
|
|
||||||
club Club? @relation(fields: [clubId], references: [id])
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
|
|
||||||
@@map("car")
|
|
||||||
@@index([name])
|
|
||||||
}
|
|
||||||
|
|
||||||
model Sortie {
|
|
||||||
totaltime Float @unique
|
|
||||||
carId Int?
|
|
||||||
driverId Int?
|
|
||||||
score Float? @unique
|
|
||||||
car String?
|
|
||||||
driver String?
|
|
||||||
|
|
||||||
@@map("sortie")
|
|
||||||
@@index([driverId])
|
|
||||||
}
|
|
||||||
|
|
||||||
model Game {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String
|
|
||||||
club Club[] @relation("ClubGame")
|
|
||||||
clubId Int?
|
|
||||||
courseId Int?
|
|
||||||
score Float?
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
|
|
||||||
@@map("game")
|
|
||||||
@@index([name])
|
|
||||||
}
|
|
||||||
model Class {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String @unique
|
|
||||||
students Student[]
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model Student {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String?
|
|
||||||
class Class? @relation(fields: [classId], references: [id])
|
|
||||||
classId Int?
|
|
||||||
scores Score[]
|
|
||||||
parentId Int?
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model Course {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String? @unique
|
|
||||||
scores Score[]
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model Score {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
student Student? @relation(fields: [studentId], references: [id])
|
|
||||||
studentId Int?
|
|
||||||
course Course? @relation(fields: [courseId], references: [id])
|
|
||||||
courseId Int?
|
|
||||||
score Float?
|
|
||||||
semester String?
|
|
||||||
createdAt DateTime? @default(now())
|
|
||||||
updatedAt DateTime? @updatedAt
|
|
||||||
|
|
||||||
@@unique([studentId, courseId, semester])
|
|
||||||
}
|
|
||||||
|
|
|
@ -46,14 +46,6 @@ export enum ObjectType {
|
||||||
POST = "post",
|
POST = "post",
|
||||||
VISIT = "visit",
|
VISIT = "visit",
|
||||||
RESOURCE = "resource",
|
RESOURCE = "resource",
|
||||||
STUDENT="student",
|
|
||||||
COURSE="course",
|
|
||||||
SCORE="score",
|
|
||||||
CAR="car",
|
|
||||||
DRIVER="driver",
|
|
||||||
CLUB="club",
|
|
||||||
GAME="game",
|
|
||||||
SORTIE="sortie"
|
|
||||||
}
|
}
|
||||||
export enum RolePerms {
|
export enum RolePerms {
|
||||||
// Create Permissions 创建权限
|
// Create Permissions 创建权限
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<script>
|
|
||||||
window.env = {
|
|
||||||
VITE_APP_SERVER_IP: "",
|
|
||||||
VITE_APP_SERVER_PORT: "",
|
|
||||||
VITE_APP_UPLOAD_PORT: "",
|
|
||||||
VITE_APP_APP_NAME: "",
|
|
||||||
VITE_APP_VERSION: "",
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<title>信箱</title>
|
|
||||||
<script type="module" crossorigin src="/assets/index-swVR_5uH.js"></script>
|
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CMaZkOCT.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script src="browserCheck.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue