Merge branch 'main' of http://113.45.157.195:3003/insiinc/re-mooc
This commit is contained in:
commit
dc7917f75d
|
@ -1,21 +1,29 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { TrpcService } from '@server/trpc/trpc.service';
|
import { TrpcService } from '@server/trpc/trpc.service';
|
||||||
import { DepartmentService } from './department.service'; // assuming it's in the same directory
|
import { DepartmentService } from './department.service'; // assuming it's in the same directory
|
||||||
import { DepartmentMethodSchema, Prisma, UpdateOrderSchema } from '@nice/common';
|
import {
|
||||||
|
DepartmentMethodSchema,
|
||||||
|
Prisma,
|
||||||
|
UpdateOrderSchema,
|
||||||
|
} from '@nice/common';
|
||||||
import { z, ZodType } from 'zod';
|
import { z, ZodType } from 'zod';
|
||||||
import { DepartmentRowService } from './department.row.service';
|
import { DepartmentRowService } from './department.row.service';
|
||||||
|
|
||||||
const DepartmentCreateArgsSchema: ZodType<Prisma.DepartmentCreateArgs> = z.any()
|
const DepartmentCreateArgsSchema: ZodType<Prisma.DepartmentCreateArgs> =
|
||||||
const DepartmentUpdateArgsSchema: ZodType<Prisma.DepartmentUpdateArgs> = z.any()
|
z.any();
|
||||||
const DepartmentFindFirstArgsSchema: ZodType<Prisma.DepartmentFindFirstArgs> = z.any()
|
const DepartmentUpdateArgsSchema: ZodType<Prisma.DepartmentUpdateArgs> =
|
||||||
const DepartmentFindManyArgsSchema: ZodType<Prisma.DepartmentFindManyArgs> = z.any()
|
z.any();
|
||||||
|
const DepartmentFindFirstArgsSchema: ZodType<Prisma.DepartmentFindFirstArgs> =
|
||||||
|
z.any();
|
||||||
|
const DepartmentFindManyArgsSchema: ZodType<Prisma.DepartmentFindManyArgs> =
|
||||||
|
z.any();
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DepartmentRouter {
|
export class DepartmentRouter {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly trpc: TrpcService,
|
private readonly trpc: TrpcService,
|
||||||
private readonly departmentService: DepartmentService, // 注入 DepartmentService
|
private readonly departmentService: DepartmentService, // 注入 DepartmentService
|
||||||
private readonly departmentRowService: DepartmentRowService
|
private readonly departmentRowService: DepartmentRowService,
|
||||||
) { }
|
) {}
|
||||||
router = this.trpc.router({
|
router = this.trpc.router({
|
||||||
// 创建部门
|
// 创建部门
|
||||||
create: this.trpc.protectProcedure
|
create: this.trpc.protectProcedure
|
||||||
|
@ -36,9 +44,11 @@ export class DepartmentRouter {
|
||||||
return this.departmentService.softDeleteByIds(input.ids);
|
return this.departmentService.softDeleteByIds(input.ids);
|
||||||
}),
|
}),
|
||||||
// 更新部门顺序
|
// 更新部门顺序
|
||||||
updateOrder: this.trpc.protectProcedure.input(UpdateOrderSchema).mutation(async ({ input }) => {
|
updateOrder: this.trpc.protectProcedure
|
||||||
return this.departmentService.updateOrder(input)
|
.input(UpdateOrderSchema)
|
||||||
}),
|
.mutation(async ({ input }) => {
|
||||||
|
return this.departmentService.updateOrder(input);
|
||||||
|
}),
|
||||||
// 查询多个部门
|
// 查询多个部门
|
||||||
findMany: this.trpc.procedure
|
findMany: this.trpc.procedure
|
||||||
.input(DepartmentFindManyArgsSchema) // 假设 StaffMethodSchema.findMany 是根据关键字查找员工的 Zod schema
|
.input(DepartmentFindManyArgsSchema) // 假设 StaffMethodSchema.findMany 是根据关键字查找员工的 Zod schema
|
||||||
|
@ -53,13 +63,15 @@ export class DepartmentRouter {
|
||||||
}),
|
}),
|
||||||
// 获取子部门的简单树结构
|
// 获取子部门的简单树结构
|
||||||
getChildSimpleTree: this.trpc.procedure
|
getChildSimpleTree: this.trpc.procedure
|
||||||
.input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => {
|
.input(DepartmentMethodSchema.getSimpleTree)
|
||||||
return await this.departmentService.getChildSimpleTree(input)
|
.query(async ({ input }) => {
|
||||||
|
return await this.departmentService.getChildSimpleTree(input);
|
||||||
}),
|
}),
|
||||||
// 获取父部门的简单树结构
|
// 获取父部门的简单树结构
|
||||||
getParentSimpleTree: this.trpc.procedure
|
getParentSimpleTree: this.trpc.procedure
|
||||||
.input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => {
|
.input(DepartmentMethodSchema.getSimpleTree)
|
||||||
return await this.departmentService.getParentSimpleTree(input)
|
.query(async ({ input }) => {
|
||||||
|
return await this.departmentService.getParentSimpleTree(input);
|
||||||
}),
|
}),
|
||||||
// 获取部门行数据
|
// 获取部门行数据
|
||||||
getRows: this.trpc.protectProcedure
|
getRows: this.trpc.protectProcedure
|
||||||
|
|
|
@ -61,6 +61,18 @@ export class StaffRouter {
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
return await this.staffService.findMany(input);
|
return await this.staffService.findMany(input);
|
||||||
}),
|
}),
|
||||||
|
findManyWithPagination: this.trpc.procedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
page: z.number().optional(),
|
||||||
|
pageSize: z.number().optional(),
|
||||||
|
where: StaffWhereInputSchema.optional(),
|
||||||
|
select: StaffSelectSchema.optional(),
|
||||||
|
}),
|
||||||
|
) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
return await this.staffService.findManyWithPagination(input);
|
||||||
|
}),
|
||||||
getRows: this.trpc.protectProcedure
|
getRows: this.trpc.protectProcedure
|
||||||
.input(StaffMethodSchema.getRows)
|
.input(StaffMethodSchema.getRows)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
ObjectType,
|
ObjectType,
|
||||||
StaffMethodSchema,
|
StaffMethodSchema,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
RolePerms,
|
RolePerms,
|
||||||
ResPerm,
|
ResPerm,
|
||||||
Staff,
|
Staff,
|
||||||
RowModelRequest,
|
RowModelRequest,
|
||||||
} from '@nice/common';
|
} from '@nice/common';
|
||||||
import { DepartmentService } from '../department/department.service';
|
import { DepartmentService } from '../department/department.service';
|
||||||
import { RowCacheService } from '../base/row-cache.service';
|
import { RowCacheService } from '../base/row-cache.service';
|
||||||
|
@ -15,121 +15,116 @@ import { z } from 'zod';
|
||||||
import { isFieldCondition } from '../base/sql-builder';
|
import { isFieldCondition } from '../base/sql-builder';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StaffRowService extends RowCacheService {
|
export class StaffRowService extends RowCacheService {
|
||||||
constructor(
|
constructor(private readonly departmentService: DepartmentService) {
|
||||||
private readonly departmentService: DepartmentService,
|
super(ObjectType.STAFF, false);
|
||||||
) {
|
}
|
||||||
super(ObjectType.STAFF, false);
|
createUnGroupingRowSelect(request?: RowModelRequest): string[] {
|
||||||
|
const result = super
|
||||||
|
.createUnGroupingRowSelect(request)
|
||||||
|
.concat([
|
||||||
|
`${this.tableName}.id AS id`,
|
||||||
|
`${this.tableName}.username AS username`,
|
||||||
|
`${this.tableName}.showname AS showname`,
|
||||||
|
`${this.tableName}.avatar AS avatar`,
|
||||||
|
`${this.tableName}.officer_id AS officer_id`,
|
||||||
|
`${this.tableName}.phone_number AS phone_number`,
|
||||||
|
`${this.tableName}.order AS order`,
|
||||||
|
`${this.tableName}.enabled AS enabled`,
|
||||||
|
'dept.name AS dept_name',
|
||||||
|
'domain.name AS domain_name',
|
||||||
|
]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
createJoinSql(request?: RowModelRequest): string[] {
|
||||||
|
return [
|
||||||
|
`LEFT JOIN department dept ON ${this.tableName}.dept_id = dept.id`,
|
||||||
|
`LEFT JOIN department domain ON ${this.tableName}.domain_id = domain.id`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
protected createGetRowsFilters(
|
||||||
|
request: z.infer<typeof StaffMethodSchema.getRows>,
|
||||||
|
staff: UserProfile,
|
||||||
|
) {
|
||||||
|
const condition = super.createGetRowsFilters(request);
|
||||||
|
const { domainId, includeDeleted = false } = request;
|
||||||
|
if (isFieldCondition(condition)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
createUnGroupingRowSelect(request?: RowModelRequest): string[] {
|
if (domainId) {
|
||||||
const result = super.createUnGroupingRowSelect(request).concat([
|
condition.AND.push({
|
||||||
`${this.tableName}.id AS id`,
|
field: `${this.tableName}.domain_id`,
|
||||||
`${this.tableName}.username AS username`,
|
value: domainId,
|
||||||
`${this.tableName}.showname AS showname`,
|
op: 'equals',
|
||||||
`${this.tableName}.avatar AS avatar`,
|
});
|
||||||
`${this.tableName}.officer_id AS officer_id`,
|
} else {
|
||||||
`${this.tableName}.phone_number AS phone_number`,
|
condition.AND.push({
|
||||||
`${this.tableName}.order AS order`,
|
field: `${this.tableName}.domain_id`,
|
||||||
`${this.tableName}.enabled AS enabled`,
|
op: 'blank',
|
||||||
'dept.name AS dept_name',
|
});
|
||||||
'domain.name AS domain_name',
|
|
||||||
]);
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
createJoinSql(request?: RowModelRequest): string[] {
|
if (!includeDeleted) {
|
||||||
return [
|
condition.AND.push({
|
||||||
`LEFT JOIN department dept ON ${this.tableName}.dept_id = dept.id`,
|
field: `${this.tableName}.deleted_at`,
|
||||||
`LEFT JOIN department domain ON ${this.tableName}.domain_id = domain.id`,
|
type: 'date',
|
||||||
];
|
op: 'blank',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
protected createGetRowsFilters(
|
condition.OR = [];
|
||||||
request: z.infer<typeof StaffMethodSchema.getRows>,
|
if (!staff.permissions.includes(RolePerms.MANAGE_ANY_STAFF)) {
|
||||||
staff: UserProfile,
|
if (staff.permissions.includes(RolePerms.MANAGE_DOM_STAFF)) {
|
||||||
) {
|
condition.OR.push({
|
||||||
const condition = super.createGetRowsFilters(request);
|
field: 'dept.id',
|
||||||
const { domainId, includeDeleted = false } = request;
|
value: staff.domainId,
|
||||||
if (isFieldCondition(condition)) {
|
op: 'equals',
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (domainId) {
|
|
||||||
condition.AND.push({
|
|
||||||
field: `${this.tableName}.domain_id`,
|
|
||||||
value: domainId,
|
|
||||||
op: 'equals',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
condition.AND.push({
|
|
||||||
field: `${this.tableName}.domain_id`,
|
|
||||||
op: 'blank',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!includeDeleted) {
|
|
||||||
condition.AND.push({
|
|
||||||
field: `${this.tableName}.deleted_at`,
|
|
||||||
type: 'date',
|
|
||||||
op: 'blank',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
condition.OR = [];
|
|
||||||
if (!staff.permissions.includes(RolePerms.MANAGE_ANY_STAFF)) {
|
|
||||||
if (staff.permissions.includes(RolePerms.MANAGE_DOM_STAFF)) {
|
|
||||||
condition.OR.push({
|
|
||||||
field: 'dept.id',
|
|
||||||
value: staff.domainId,
|
|
||||||
op: 'equals',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPermissionContext(id: string, staff: UserProfile) {
|
|
||||||
const data = await db.staff.findUnique({
|
|
||||||
where: { id },
|
|
||||||
select: {
|
|
||||||
deptId: true,
|
|
||||||
domainId: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const deptId = data?.deptId;
|
}
|
||||||
const isFromSameDept = staff.deptIds?.includes(deptId);
|
|
||||||
const domainChildDeptIds = await this.departmentService.getDescendantIds(
|
|
||||||
staff.domainId, true
|
|
||||||
);
|
|
||||||
const belongsToDomain = domainChildDeptIds.includes(
|
|
||||||
deptId,
|
|
||||||
);
|
|
||||||
return { isFromSameDept, belongsToDomain };
|
|
||||||
}
|
|
||||||
protected async setResPermissions(
|
|
||||||
data: Staff,
|
|
||||||
staff: UserProfile,
|
|
||||||
) {
|
|
||||||
const permissions: ResPerm = {};
|
|
||||||
const { isFromSameDept, belongsToDomain } = await this.getPermissionContext(
|
|
||||||
data.id,
|
|
||||||
staff,
|
|
||||||
);
|
|
||||||
const setManagePermissions = (permissions: ResPerm) => {
|
|
||||||
Object.assign(permissions, {
|
|
||||||
read: true,
|
|
||||||
delete: true,
|
|
||||||
edit: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
staff.permissions.forEach((permission) => {
|
|
||||||
switch (permission) {
|
|
||||||
case RolePerms.MANAGE_ANY_STAFF:
|
|
||||||
setManagePermissions(permissions);
|
|
||||||
break;
|
|
||||||
case RolePerms.MANAGE_DOM_STAFF:
|
|
||||||
if (belongsToDomain) {
|
|
||||||
setManagePermissions(permissions);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { ...data, perm: permissions };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPermissionContext(id: string, staff: UserProfile) {
|
||||||
|
const data = await db.staff.findUnique({
|
||||||
|
where: { id },
|
||||||
|
select: {
|
||||||
|
deptId: true,
|
||||||
|
domainId: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const deptId = data?.deptId;
|
||||||
|
const isFromSameDept = staff.deptIds?.includes(deptId);
|
||||||
|
const domainChildDeptIds = await this.departmentService.getDescendantIds(
|
||||||
|
staff.domainId,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
const belongsToDomain = domainChildDeptIds.includes(deptId);
|
||||||
|
return { isFromSameDept, belongsToDomain };
|
||||||
|
}
|
||||||
|
protected async setResPermissions(data: Staff, staff: UserProfile) {
|
||||||
|
const permissions: ResPerm = {};
|
||||||
|
const { isFromSameDept, belongsToDomain } = await this.getPermissionContext(
|
||||||
|
data.id,
|
||||||
|
staff,
|
||||||
|
);
|
||||||
|
const setManagePermissions = (permissions: ResPerm) => {
|
||||||
|
Object.assign(permissions, {
|
||||||
|
read: true,
|
||||||
|
delete: true,
|
||||||
|
edit: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
staff.permissions.forEach((permission) => {
|
||||||
|
switch (permission) {
|
||||||
|
case RolePerms.MANAGE_ANY_STAFF:
|
||||||
|
setManagePermissions(permissions);
|
||||||
|
break;
|
||||||
|
case RolePerms.MANAGE_DOM_STAFF:
|
||||||
|
if (belongsToDomain) {
|
||||||
|
setManagePermissions(permissions);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { ...data, perm: permissions };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import EventBus, { CrudOperation } from '@server/utils/event-bus';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
|
|
||||||
constructor(private readonly departmentService: DepartmentService) {
|
constructor(private readonly departmentService: DepartmentService) {
|
||||||
super(db, ObjectType.STAFF, true);
|
super(db, ObjectType.STAFF, true);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +24,10 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
*/
|
*/
|
||||||
async findByDept(data: z.infer<typeof StaffMethodSchema.findByDept>) {
|
async findByDept(data: z.infer<typeof StaffMethodSchema.findByDept>) {
|
||||||
const { deptId, domainId } = data;
|
const { deptId, domainId } = data;
|
||||||
const childDepts = await this.departmentService.getDescendantIds(deptId, true);
|
const childDepts = await this.departmentService.getDescendantIds(
|
||||||
|
deptId,
|
||||||
|
true,
|
||||||
|
);
|
||||||
const result = await db.staff.findMany({
|
const result = await db.staff.findMany({
|
||||||
where: {
|
where: {
|
||||||
deptId: { in: childDepts },
|
deptId: { in: childDepts },
|
||||||
|
@ -50,7 +52,9 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
await this.validateUniqueFields(data, where.id);
|
await this.validateUniqueFields(data, where.id);
|
||||||
const updateData = {
|
const updateData = {
|
||||||
...data,
|
...data,
|
||||||
...(data.password && { password: await argon2.hash(data.password as string) })
|
...(data.password && {
|
||||||
|
password: await argon2.hash(data.password as string),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
const result = await super.update({ ...args, data: updateData });
|
const result = await super.update({ ...args, data: updateData });
|
||||||
this.emitDataChangedEvent(result, CrudOperation.UPDATED);
|
this.emitDataChangedEvent(result, CrudOperation.UPDATED);
|
||||||
|
@ -58,17 +62,26 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
}
|
}
|
||||||
private async validateUniqueFields(data: any, excludeId?: string) {
|
private async validateUniqueFields(data: any, excludeId?: string) {
|
||||||
const uniqueFields = [
|
const uniqueFields = [
|
||||||
{ field: 'officerId', errorMsg: (val: string) => `证件号为${val}的用户已存在` },
|
{
|
||||||
{ field: 'phoneNumber', errorMsg: (val: string) => `手机号为${val}的用户已存在` },
|
field: 'officerId',
|
||||||
{ field: 'username', errorMsg: (val: string) => `帐号为${val}的用户已存在` }
|
errorMsg: (val: string) => `证件号为${val}的用户已存在`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'phoneNumber',
|
||||||
|
errorMsg: (val: string) => `手机号为${val}的用户已存在`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'username',
|
||||||
|
errorMsg: (val: string) => `帐号为${val}的用户已存在`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
for (const { field, errorMsg } of uniqueFields) {
|
for (const { field, errorMsg } of uniqueFields) {
|
||||||
if (data[field]) {
|
if (data[field]) {
|
||||||
const count = await db.staff.count({
|
const count = await db.staff.count({
|
||||||
where: {
|
where: {
|
||||||
[field]: data[field],
|
[field]: data[field],
|
||||||
...(excludeId && { id: { not: excludeId } })
|
...(excludeId && { id: { not: excludeId } }),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
throw new Error(errorMsg(data[field]));
|
throw new Error(errorMsg(data[field]));
|
||||||
|
@ -77,9 +90,8 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
private emitDataChangedEvent(data: any, operation: CrudOperation) {
|
||||||
EventBus.emit("dataChanged", {
|
EventBus.emit('dataChanged', {
|
||||||
type: this.objectType,
|
type: this.objectType,
|
||||||
operation,
|
operation,
|
||||||
data,
|
data,
|
||||||
|
@ -87,12 +99,12 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新员工DomainId
|
* 更新员工DomainId
|
||||||
* @param data 包含domainId对象
|
* @param data 包含domainId对象
|
||||||
* @returns 更新后的员工记录
|
* @returns 更新后的员工记录
|
||||||
*/
|
*/
|
||||||
async updateUserDomain(data: { domainId?: string }, staff?: UserProfile) {
|
async updateUserDomain(data: { domainId?: string }, staff?: UserProfile) {
|
||||||
let { domainId } = data;
|
const { domainId } = data;
|
||||||
if (staff.domainId !== domainId) {
|
if (staff.domainId !== domainId) {
|
||||||
const result = await this.update({
|
const result = await this.update({
|
||||||
where: { id: staff.id },
|
where: { id: staff.id },
|
||||||
|
@ -107,7 +119,6 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * 根据关键词或ID集合查找员工
|
// * 根据关键词或ID集合查找员工
|
||||||
// * @param data 包含关键词、域ID和ID集合的对象
|
// * @param data 包含关键词、域ID和ID集合的对象
|
||||||
|
@ -176,5 +187,4 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
||||||
|
|
||||||
// return combinedResults;
|
// return combinedResults;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,10 +107,10 @@ export async function updatePostViewCount(id: string, type: VisitType) {
|
||||||
where: { id: course.id },
|
where: { id: course.id },
|
||||||
data: {
|
data: {
|
||||||
[metaFieldMap[type]]: courseViews._sum.views || 0,
|
[metaFieldMap[type]]: courseViews._sum.views || 0,
|
||||||
meta: {
|
// meta: {
|
||||||
...((post?.meta as any) || {}),
|
// ...((post?.meta as any) || {}),
|
||||||
[metaFieldMap[type]]: courseViews._sum.views || 0,
|
// [metaFieldMap[type]]: courseViews._sum.views || 0,
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -127,10 +127,10 @@ export async function updatePostViewCount(id: string, type: VisitType) {
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
[metaFieldMap[type]]: totalViews._sum.views || 0,
|
[metaFieldMap[type]]: totalViews._sum.views || 0,
|
||||||
meta: {
|
// meta: {
|
||||||
...((post?.meta as any) || {}),
|
// ...((post?.meta as any) || {}),
|
||||||
[metaFieldMap[type]]: totalViews._sum.views || 0,
|
// [metaFieldMap[type]]: totalViews._sum.views || 0,
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,11 @@ import { useTusUpload } from "@web/src/hooks/useTusUpload";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useAuth } from "@web/src/providers/auth-provider";
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
import { MIND_OPTIONS } from "./constant";
|
import { MIND_OPTIONS } from "./constant";
|
||||||
import { SaveOutlined } from "@ant-design/icons";
|
import { LinkOutlined, SaveOutlined } from "@ant-design/icons";
|
||||||
import JoinButton from "../../models/course/detail/CourseOperationBtns/JoinButton";
|
import JoinButton from "../../models/course/detail/CourseOperationBtns/JoinButton";
|
||||||
import { CourseDetailContext } from "../../models/course/detail/PostDetailContext";
|
import { CourseDetailContext } from "../../models/course/detail/PostDetailContext";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
export default function MindEditor({ id }: { id?: string }) {
|
export default function MindEditor({ id }: { id?: string }) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const {
|
const {
|
||||||
|
@ -57,6 +59,17 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
});
|
});
|
||||||
const { handleFileUpload } = useTusUpload();
|
const { handleFileUpload } = useTusUpload();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const CustomLinkIconPlugin = (mind) => {
|
||||||
|
mind.bus.addListener('operation', async () => {
|
||||||
|
const hyperLinkElement = await document.querySelectorAll('.hyper-link');
|
||||||
|
console.log('hyperLinkElement', hyperLinkElement);
|
||||||
|
hyperLinkElement.forEach((item) => {
|
||||||
|
const hyperLinkDom = createRoot(item)
|
||||||
|
hyperLinkDom.render(<LinkOutlined />)
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (post?.id && id) {
|
if (post?.id && id) {
|
||||||
read.mutateAsync({
|
read.mutateAsync({
|
||||||
|
@ -98,6 +111,7 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
nodeMenu: canEdit, // 禁用节点右键菜单
|
nodeMenu: canEdit, // 禁用节点右键菜单
|
||||||
keypress: canEdit, // 禁用键盘快捷键
|
keypress: canEdit, // 禁用键盘快捷键
|
||||||
});
|
});
|
||||||
|
mind.install(CustomLinkIconPlugin);
|
||||||
mind.init(MindElixir.new("新思维导图"));
|
mind.init(MindElixir.new("新思维导图"));
|
||||||
containerRef.current.hidden = true;
|
containerRef.current.hidden = true;
|
||||||
//挂载实例
|
//挂载实例
|
||||||
|
@ -173,16 +187,13 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
}
|
}
|
||||||
console.log(result);
|
console.log(result);
|
||||||
},
|
},
|
||||||
(error) => {},
|
(error) => { },
|
||||||
`mind-thumb-${new Date().toString()}`
|
`mind-thumb-${new Date().toString()}`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`;
|
containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`;
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(()=>{
|
|
||||||
console.log(canEdit,user?.id,post?.author?.id)
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<div className={` flex-col flex `}>
|
<div className={` flex-col flex `}>
|
||||||
{taxonomies && (
|
{taxonomies && (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// components/CourseDetailDisplayArea.tsx
|
// components/CourseDetailDisplayArea.tsx
|
||||||
import { motion, useScroll, useTransform } from "framer-motion";
|
import { motion, useScroll, useTransform } from "framer-motion";
|
||||||
import React, { useContext, useRef, useState } from "react";
|
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer";
|
import { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer";
|
||||||
import { CourseDetailDescription } from "./CourseDetailDescription";
|
import { CourseDetailDescription } from "./CourseDetailDescription";
|
||||||
import { Course, LectureType, PostType } from "@nice/common";
|
import { Course, LectureType, PostType } from "@nice/common";
|
||||||
|
@ -40,7 +40,7 @@ export const CourseDetailDisplayArea: React.FC = () => {
|
||||||
opacity: videoOpacity,
|
opacity: videoOpacity,
|
||||||
}}
|
}}
|
||||||
className="w-full bg-black rounded-lg ">
|
className="w-full bg-black rounded-lg ">
|
||||||
<div className=" w-full ">
|
<div className=" w-full cursor-pointer">
|
||||||
<VideoPlayer src={lecture?.meta?.videoUrl} />
|
<VideoPlayer src={lecture?.meta?.videoUrl} />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
@ -28,6 +28,7 @@ export default function PostList({
|
||||||
renderItem,
|
renderItem,
|
||||||
}: PostListProps) {
|
}: PostListProps) {
|
||||||
const [currentPage, setCurrentPage] = useState<number>(params?.page || 1);
|
const [currentPage, setCurrentPage] = useState<number>(params?.page || 1);
|
||||||
|
|
||||||
const { data, isLoading }: PostPagnationProps =
|
const { data, isLoading }: PostPagnationProps =
|
||||||
api.post.findManyWithPagination.useQuery({
|
api.post.findManyWithPagination.useQuery({
|
||||||
select: courseDetailSelect,
|
select: courseDetailSelect,
|
||||||
|
|
|
@ -91,6 +91,7 @@ export default function PostSelect({
|
||||||
dropdownStyle={{
|
dropdownStyle={{
|
||||||
minWidth: 200, // 设置合适的最小宽度
|
minWidth: 200, // 设置合适的最小宽度
|
||||||
}}
|
}}
|
||||||
|
autoClearSearchValue
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
|
|
|
@ -25,6 +25,7 @@ export const VideoDisplay: React.FC<VideoDisplayProps> = ({
|
||||||
isDragging,
|
isDragging,
|
||||||
setIsDragging,
|
setIsDragging,
|
||||||
progressRef,
|
progressRef,
|
||||||
|
isPlaying
|
||||||
} = useContext(VideoPlayerContext);
|
} = useContext(VideoPlayerContext);
|
||||||
|
|
||||||
// 处理进度条拖拽
|
// 处理进度条拖拽
|
||||||
|
@ -191,9 +192,20 @@ export const VideoDisplay: React.FC<VideoDisplayProps> = ({
|
||||||
};
|
};
|
||||||
}, [src, onError, autoPlay]);
|
}, [src, onError, autoPlay]);
|
||||||
|
|
||||||
|
const handleVideoClick = () => {
|
||||||
|
if (videoRef.current && isPlaying) {
|
||||||
|
videoRef.current.pause();
|
||||||
|
setIsPlaying(false);
|
||||||
|
}else if (videoRef.current && !isPlaying) {
|
||||||
|
videoRef.current.play();
|
||||||
|
setIsPlaying(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full aspect-video">
|
<div className="relative w-full aspect-video" >
|
||||||
<video
|
<video
|
||||||
|
onClick={handleVideoClick}
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
poster={poster}
|
poster={poster}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Staff } from "packages/common/dist";
|
||||||
|
import { api } from "packages/client/dist";
|
||||||
|
export const StaffCard = (staff: Staff) => {
|
||||||
|
const { data } = api.staff.findManyWithPagination.useQuery({
|
||||||
|
page
|
||||||
|
});
|
||||||
|
return(
|
||||||
|
<div className="staff-card">
|
||||||
|
<div className="staff-card__username">${username}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { api } from "packages/client/dist";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
export default function StaffList({ renderItem }: { renderItem: ReactNode }) {
|
||||||
|
const { data } = api.staff.findManyWithPagination.useQuery({});
|
||||||
|
data.items.map((staff) => renderItem);
|
||||||
|
}
|
|
@ -72,9 +72,7 @@ export const routes: CustomRouteObject[] = [
|
||||||
{
|
{
|
||||||
path: "editor/:id?",
|
path: "editor/:id?",
|
||||||
element: (
|
element: (
|
||||||
<WithAuth>
|
<PathEditorPage></PathEditorPage>
|
||||||
<PathEditorPage></PathEditorPage>
|
|
||||||
</WithAuth>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,7 +2,7 @@ server {
|
||||||
# 监听80端口
|
# 监听80端口
|
||||||
listen 80;
|
listen 80;
|
||||||
# 服务器域名/IP地址,使用环境变量
|
# 服务器域名/IP地址,使用环境变量
|
||||||
server_name 192.168.252.77;
|
server_name host.docker.internal;
|
||||||
|
|
||||||
# 基础性能优化配置
|
# 基础性能优化配置
|
||||||
# 启用tcp_nopush以优化数据发送
|
# 启用tcp_nopush以优化数据发送
|
||||||
|
@ -100,7 +100,7 @@ server {
|
||||||
# 仅供内部使用
|
# 仅供内部使用
|
||||||
internal;
|
internal;
|
||||||
# 代理到认证服务
|
# 代理到认证服务
|
||||||
proxy_pass http://192.168.252.77:3000/auth/file;
|
proxy_pass http://host.docker.internal:3000/auth/file;
|
||||||
|
|
||||||
# 请求优化:不传递请求体
|
# 请求优化:不传递请求体
|
||||||
proxy_pass_request_body off;
|
proxy_pass_request_body off;
|
||||||
|
|
|
@ -15,7 +15,7 @@ done
|
||||||
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
|
||||||
echo "Processing /usr/share/nginx/html/index.html"
|
echo "Processing /usr/share/nginx/html/index.html"
|
||||||
envsubst < /usr/share/nginx/html/index.template > /usr/share/nginx/html/index.html.tmp
|
envsubst < /usr/share/nginx/html/index.temp > /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
|
||||||
echo "Processed content:"
|
echo "Processed content:"
|
||||||
cat /usr/share/nginx/html/index.html
|
cat /usr/share/nginx/html/index.html
|
||||||
|
|
Loading…
Reference in New Issue