diff --git a/.gitignore b/.gitignore index 2e6bac7..4954c82 100755 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ backup # dependencies **/node_modules/ volumes +web-dist /.pnp .pnp.js *.tar @@ -69,4 +70,4 @@ yarn-error.log* **/.idea/ uploads packages/mind-elixir-core -config/nginx/conf.d/web.conf \ No newline at end of file +config/nginx/conf.d/web.conf diff --git a/Dockerfile b/Dockerfile index 0a51235..3d15a41 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,23 @@ # 基础镜像 FROM node:18.17-alpine as base -# 更改 apk 镜像源为阿里云 -# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories - -# 使用阿里云镜像源 + 完整仓库声明 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories -# 安装最新稳定版 PostgreSQL 客户端(15.11) -RUN apk update --no-cache && \ - apk add --no-cache \ - postgresql15-client \ - libpq \ - readline - - RUN apk add --no-cache ffmpeg - -# 设置 npm 镜像源 RUN yarn config set registry https://registry.npmmirror.com - -# 全局安装 pnpm 并设置其镜像源 RUN yarn global add pnpm && pnpm config set registry https://registry.npmmirror.com - -# 设置工作目录 WORKDIR /app - -# 复制 pnpm workspace 配置文件 COPY pnpm-workspace.yaml ./ - -# 首先复制 package.json, package-lock.json 和 pnpm-lock.yaml 文件 COPY package*.json pnpm-lock.yaml* ./ - COPY tsconfig.base.json . -# 利用 Docker 缓存机制,如果依赖没有改变则不会重新执行 pnpm install -#100-500 5-40 + FROM base As server-build WORKDIR /app -COPY packages/common /app/packages/common -COPY packages/tus /app/packages/tus -COPY apps/server /app/apps/server -RUN pnpm config set registry https://registry.npmmirror.com/ -RUN pnpm install --filter common -RUN pnpm install --filter tus -RUN pnpm install --filter server -RUN pnpm --filter common generate && pnpm --filter common build:cjs +COPY . . +RUN pnpm install +RUN pnpm --filter common build RUN pnpm --filter tus build +RUN pnpm --filter utils build RUN pnpm --filter server build FROM base As server-prod-dep @@ -55,6 +27,7 @@ COPY packages/tus /app/packages/tus COPY apps/server /app/apps/server RUN pnpm install --filter common --prod RUN pnpm install --filter tus --prod +RUN pnpm install --filter utils --prod RUN pnpm install --filter server --prod @@ -66,10 +39,8 @@ COPY --from=server-build /app/packages/common/dist ./packages/common/dist COPY --from=server-build /app/packages/tus/dist ./packages/tus/dist COPY --from=server-build /app/apps/server/dist ./apps/server/dist COPY apps/server/entrypoint.sh ./apps/server/entrypoint.sh - RUN chmod +x ./apps/server/entrypoint.sh -RUN apk add --no-cache postgresql-client - +RUN apk add --no-cache ffmpeg EXPOSE 3000 @@ -108,21 +79,15 @@ CMD ["/usr/bin/entrypoint.sh"] # 使用 Nginx 的 Alpine 版本作为基础镜像 FROM nginx:stable-alpine as nginx - # 替换 Alpine 的软件源为阿里云镜像 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories - # 设置工作目录 WORKDIR /usr/share/nginx/html - # 设置环境变量 ENV NODE_ENV production - # 安装 envsubst 和 inotify-tools RUN apk add --no-cache gettext inotify-tools - # 创建 /data/uploads 目录 RUN mkdir -p /data/uploads - # 暴露 80 端口 EXPOSE 80 \ No newline at end of file diff --git a/apps/server/src/models/department/department.router.ts b/apps/server/src/models/department/department.router.ts index 2cd168c..edec18e 100755 --- a/apps/server/src/models/department/department.router.ts +++ b/apps/server/src/models/department/department.router.ts @@ -1,21 +1,29 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; 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 { DepartmentRowService } from './department.row.service'; -const DepartmentCreateArgsSchema: ZodType = z.any() -const DepartmentUpdateArgsSchema: ZodType = z.any() -const DepartmentFindFirstArgsSchema: ZodType = z.any() -const DepartmentFindManyArgsSchema: ZodType = z.any() +const DepartmentCreateArgsSchema: ZodType = + z.any(); +const DepartmentUpdateArgsSchema: ZodType = + z.any(); +const DepartmentFindFirstArgsSchema: ZodType = + z.any(); +const DepartmentFindManyArgsSchema: ZodType = + z.any(); @Injectable() export class DepartmentRouter { constructor( private readonly trpc: TrpcService, private readonly departmentService: DepartmentService, // 注入 DepartmentService - private readonly departmentRowService: DepartmentRowService - ) { } + private readonly departmentRowService: DepartmentRowService, + ) {} router = this.trpc.router({ // 创建部门 create: this.trpc.protectProcedure @@ -36,9 +44,11 @@ export class DepartmentRouter { return this.departmentService.softDeleteByIds(input.ids); }), // 更新部门顺序 - updateOrder: this.trpc.protectProcedure.input(UpdateOrderSchema).mutation(async ({ input }) => { - return this.departmentService.updateOrder(input) - }), + updateOrder: this.trpc.protectProcedure + .input(UpdateOrderSchema) + .mutation(async ({ input }) => { + return this.departmentService.updateOrder(input); + }), // 查询多个部门 findMany: this.trpc.procedure .input(DepartmentFindManyArgsSchema) // 假设 StaffMethodSchema.findMany 是根据关键字查找员工的 Zod schema @@ -53,13 +63,15 @@ export class DepartmentRouter { }), // 获取子部门的简单树结构 getChildSimpleTree: this.trpc.procedure - .input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => { - return await this.departmentService.getChildSimpleTree(input) + .input(DepartmentMethodSchema.getSimpleTree) + .query(async ({ input }) => { + return await this.departmentService.getChildSimpleTree(input); }), // 获取父部门的简单树结构 getParentSimpleTree: this.trpc.procedure - .input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => { - return await this.departmentService.getParentSimpleTree(input) + .input(DepartmentMethodSchema.getSimpleTree) + .query(async ({ input }) => { + return await this.departmentService.getParentSimpleTree(input); }), // 获取部门行数据 getRows: this.trpc.protectProcedure diff --git a/apps/server/src/models/staff/staff.router.ts b/apps/server/src/models/staff/staff.router.ts index 4768b16..9f227a4 100755 --- a/apps/server/src/models/staff/staff.router.ts +++ b/apps/server/src/models/staff/staff.router.ts @@ -61,6 +61,18 @@ export class StaffRouter { .query(async ({ 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 .input(StaffMethodSchema.getRows) .query(async ({ input, ctx }) => { diff --git a/apps/server/src/models/staff/staff.row.service.ts b/apps/server/src/models/staff/staff.row.service.ts index c2e92c2..7347725 100755 --- a/apps/server/src/models/staff/staff.row.service.ts +++ b/apps/server/src/models/staff/staff.row.service.ts @@ -1,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { - db, - ObjectType, - StaffMethodSchema, - UserProfile, - RolePerms, - ResPerm, - Staff, - RowModelRequest, + db, + ObjectType, + StaffMethodSchema, + UserProfile, + RolePerms, + ResPerm, + Staff, + RowModelRequest, } from '@nice/common'; import { DepartmentService } from '../department/department.service'; import { RowCacheService } from '../base/row-cache.service'; @@ -15,121 +15,116 @@ import { z } from 'zod'; import { isFieldCondition } from '../base/sql-builder'; @Injectable() export class StaffRowService extends RowCacheService { - constructor( - private readonly departmentService: DepartmentService, - ) { - super(ObjectType.STAFF, false); + constructor(private readonly departmentService: DepartmentService) { + 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, + staff: UserProfile, + ) { + const condition = super.createGetRowsFilters(request); + const { domainId, includeDeleted = false } = request; + if (isFieldCondition(condition)) { + return; } - 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 + 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', + }); } - 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`, - ]; + if (!includeDeleted) { + condition.AND.push({ + field: `${this.tableName}.deleted_at`, + type: 'date', + op: 'blank', + }); } - protected createGetRowsFilters( - request: z.infer, - staff: UserProfile, - ) { - const condition = super.createGetRowsFilters(request); - const { domainId, includeDeleted = false } = request; - if (isFieldCondition(condition)) { - 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, - }, + 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', }); - 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 }; + } } diff --git a/apps/server/src/models/staff/staff.service.ts b/apps/server/src/models/staff/staff.service.ts index cf37549..57cddd2 100755 --- a/apps/server/src/models/staff/staff.service.ts +++ b/apps/server/src/models/staff/staff.service.ts @@ -14,7 +14,6 @@ import EventBus, { CrudOperation } from '@server/utils/event-bus'; @Injectable() export class StaffService extends BaseService { - constructor(private readonly departmentService: DepartmentService) { super(db, ObjectType.STAFF, true); } @@ -25,7 +24,10 @@ export class StaffService extends BaseService { */ async findByDept(data: z.infer) { 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({ where: { deptId: { in: childDepts }, @@ -50,7 +52,9 @@ export class StaffService extends BaseService { await this.validateUniqueFields(data, where.id); const updateData = { ...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 }); this.emitDataChangedEvent(result, CrudOperation.UPDATED); @@ -58,17 +62,26 @@ export class StaffService extends BaseService { } private async validateUniqueFields(data: any, excludeId?: string) { const uniqueFields = [ - { field: 'officerId', errorMsg: (val: string) => `证件号为${val}的用户已存在` }, - { field: 'phoneNumber', errorMsg: (val: string) => `手机号为${val}的用户已存在` }, - { field: 'username', errorMsg: (val: string) => `帐号为${val}的用户已存在` } + { + field: 'officerId', + errorMsg: (val: string) => `证件号为${val}的用户已存在`, + }, + { + field: 'phoneNumber', + errorMsg: (val: string) => `手机号为${val}的用户已存在`, + }, + { + field: 'username', + errorMsg: (val: string) => `帐号为${val}的用户已存在`, + }, ]; for (const { field, errorMsg } of uniqueFields) { if (data[field]) { const count = await db.staff.count({ where: { [field]: data[field], - ...(excludeId && { id: { not: excludeId } }) - } + ...(excludeId && { id: { not: excludeId } }), + }, }); if (count > 0) { throw new Error(errorMsg(data[field])); @@ -77,9 +90,8 @@ export class StaffService extends BaseService { } } - private emitDataChangedEvent(data: any, operation: CrudOperation) { - EventBus.emit("dataChanged", { + EventBus.emit('dataChanged', { type: this.objectType, operation, data, @@ -87,12 +99,12 @@ export class StaffService extends BaseService { } /** - * 更新员工DomainId - * @param data 包含domainId对象 - * @returns 更新后的员工记录 - */ + * 更新员工DomainId + * @param data 包含domainId对象 + * @returns 更新后的员工记录 + */ async updateUserDomain(data: { domainId?: string }, staff?: UserProfile) { - let { domainId } = data; + const { domainId } = data; if (staff.domainId !== domainId) { const result = await this.update({ where: { id: staff.id }, @@ -107,7 +119,6 @@ export class StaffService extends BaseService { } } - // /** // * 根据关键词或ID集合查找员工 // * @param data 包含关键词、域ID和ID集合的对象 @@ -176,5 +187,4 @@ export class StaffService extends BaseService { // return combinedResults; // } - } diff --git a/apps/server/src/queue/models/post/utils.ts b/apps/server/src/queue/models/post/utils.ts index 7e3c3f1..932d4b3 100755 --- a/apps/server/src/queue/models/post/utils.ts +++ b/apps/server/src/queue/models/post/utils.ts @@ -107,10 +107,10 @@ export async function updatePostViewCount(id: string, type: VisitType) { where: { id: course.id }, data: { [metaFieldMap[type]]: courseViews._sum.views || 0, - meta: { - ...((post?.meta as any) || {}), - [metaFieldMap[type]]: courseViews._sum.views || 0, - }, + // meta: { + // ...((post?.meta as any) || {}), + // [metaFieldMap[type]]: courseViews._sum.views || 0, + // }, }, }); } @@ -127,10 +127,10 @@ export async function updatePostViewCount(id: string, type: VisitType) { where: { id }, data: { [metaFieldMap[type]]: totalViews._sum.views || 0, - meta: { - ...((post?.meta as any) || {}), - [metaFieldMap[type]]: totalViews._sum.views || 0, - }, + // meta: { + // ...((post?.meta as any) || {}), + // [metaFieldMap[type]]: totalViews._sum.views || 0, + // }, }, }); } diff --git a/apps/server/src/tasks/init/init.service.ts b/apps/server/src/tasks/init/init.service.ts index 8d6a955..b4f94b1 100755 --- a/apps/server/src/tasks/init/init.service.ts +++ b/apps/server/src/tasks/init/init.service.ts @@ -41,12 +41,11 @@ export class InitService { const existingTaxonomies = await db.taxonomy.findMany(); const existingTaxonomyMap = new Map( - existingTaxonomies.map((taxonomy) => [taxonomy.name, taxonomy]), + existingTaxonomies.map((taxonomy) => [taxonomy.slug, taxonomy]), ); for (const [index, taxonomy] of InitTaxonomies.entries()) { - const existingTaxonomy = existingTaxonomyMap.get(taxonomy.name); - + const existingTaxonomy = existingTaxonomyMap.get(taxonomy.slug); if (!existingTaxonomy) { // Create new taxonomy await db.taxonomy.create({ @@ -56,12 +55,12 @@ export class InitService { }, }); this.logger.log(`Created new taxonomy: ${taxonomy.name}`); - } else { + } else if(process.env.NODE_ENV === 'development'){ // Check for differences and update if necessary const differences = Object.keys(taxonomy).filter( (key) => taxonomy[key] !== existingTaxonomy[key], ); - + if (differences.length > 0) { await db.taxonomy.update({ where: { id: existingTaxonomy.id }, diff --git a/apps/web/.env.example b/apps/web/.env.example index 4d30872..826fc8f 100755 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -1,4 +1,4 @@ -VITE_APP_SERVER_IP=192.168.252.239 +VITE_APP_SERVER_IP=localhost VITE_APP_SERVER_PORT=3000 VITE_APP_FILE_PORT=80 VITE_APP_VERSION=0.3.0 diff --git a/apps/web/index.html b/apps/web/index.html index 6199b0c..4af329b 100755 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -7,12 +7,14 @@ - fhmooc + $APP_NAME diff --git a/apps/web/package.json b/apps/web/package.json index 909aa90..b484901 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -34,7 +34,6 @@ "@nice/common": "workspace:^", "@nice/config": "workspace:^", "@nice/iconer": "workspace:^", - "@nice/ui": "workspace:^", "@nice/utils": "workspace:^", "@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/react-query": "^5.51.21", diff --git a/apps/web/public/placeholder.webp b/apps/web/public/placeholder.webp old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/home/components/HeroSection.tsx b/apps/web/src/app/main/home/components/HeroSection.tsx index 74643fb..f4c21fd 100755 --- a/apps/web/src/app/main/home/components/HeroSection.tsx +++ b/apps/web/src/app/main/home/components/HeroSection.tsx @@ -18,16 +18,6 @@ import type { CarouselRef } from "antd/es/carousel"; import { useAppConfig } from "@nice/client"; import { useNavigate } from "react-router-dom"; -const { Title, Text } = Typography; - -interface CarouselItem { - title: string; - desc: string; - image: string; - action: string; - color: string; -} - interface PlatformStat { icon: React.ReactNode; value: number; diff --git a/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx b/apps/web/src/app/main/layout/BasePost/BasePostLayout.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx b/apps/web/src/app/main/layout/BasePost/SearchModeRadio.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx b/apps/web/src/app/main/my-duty-path/components/MyDutyPathContainer.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/my-duty/components/MyDutyListContainer.tsx b/apps/web/src/app/main/my-duty/components/MyDutyListContainer.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/my-learning/components/MyLearningListContainer.tsx b/apps/web/src/app/main/my-learning/components/MyLearningListContainer.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/my-path/components/MyPathListContainer.tsx b/apps/web/src/app/main/my-path/components/MyPathListContainer.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/path/components/DeptInfo.tsx b/apps/web/src/app/main/path/components/DeptInfo.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/path/components/TermInfo.tsx b/apps/web/src/app/main/path/components/TermInfo.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/app/main/search/components/SearchContainer.tsx b/apps/web/src/app/main/search/components/SearchContainer.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 985e320..9518d4f 100755 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -20,9 +20,11 @@ import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; 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 { CourseDetailContext } from "../../models/course/detail/PostDetailContext"; +import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const { @@ -57,6 +59,17 @@ export default function MindEditor({ id }: { id?: string }) { }); const { handleFileUpload } = useTusUpload(); 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() + }); + + }); + }; useEffect(() => { if (post?.id && id) { read.mutateAsync({ @@ -98,6 +111,7 @@ export default function MindEditor({ id }: { id?: string }) { nodeMenu: canEdit, // 禁用节点右键菜单 keypress: canEdit, // 禁用键盘快捷键 }); + mind.install(CustomLinkIconPlugin); mind.init(MindElixir.new("新思维导图")); containerRef.current.hidden = true; //挂载实例 @@ -173,16 +187,13 @@ export default function MindEditor({ id }: { id?: string }) { } console.log(result); }, - (error) => {}, + (error) => { }, `mind-thumb-${new Date().toString()}` ); }; useEffect(() => { containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`; }, []); - useEffect(()=>{ - console.log(canEdit,user?.id,post?.author?.id) - }) return (
{taxonomies && ( @@ -214,7 +225,7 @@ export default function MindEditor({ id }: { id?: string }) { multiple /> - + {post && id ? : <>}
{canEdit && ( diff --git a/apps/web/src/components/common/editor/constant.ts b/apps/web/src/components/common/editor/constant.ts old mode 100644 new mode 100755 diff --git a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx index f95c29d..6231436 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx @@ -1,6 +1,6 @@ // components/CourseDetailDisplayArea.tsx 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 { CourseDetailDescription } from "./CourseDetailDescription"; import { Course, LectureType, PostType } from "@nice/common"; @@ -40,7 +40,7 @@ export const CourseDetailDisplayArea: React.FC = () => { opacity: videoOpacity, }} className="w-full bg-black rounded-lg "> -
+
diff --git a/apps/web/src/components/models/course/detail/CourseOperationBtns/JoinButton.tsx b/apps/web/src/components/models/course/detail/CourseOperationBtns/JoinButton.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/components/models/course/list/PostList.tsx b/apps/web/src/components/models/course/list/PostList.tsx index ce9da18..a276060 100755 --- a/apps/web/src/components/models/course/list/PostList.tsx +++ b/apps/web/src/components/models/course/list/PostList.tsx @@ -28,6 +28,7 @@ export default function PostList({ renderItem, }: PostListProps) { const [currentPage, setCurrentPage] = useState(params?.page || 1); + const { data, isLoading }: PostPagnationProps = api.post.findManyWithPagination.useQuery({ select: courseDetailSelect, diff --git a/apps/web/src/components/models/post/PostCard.tsx b/apps/web/src/components/models/post/PostCard.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/components/models/post/PostSelect.tsx b/apps/web/src/components/models/post/PostSelect.tsx new file mode 100755 index 0000000..d5f5324 --- /dev/null +++ b/apps/web/src/components/models/post/PostSelect.tsx @@ -0,0 +1,12 @@ +import { api } from "@nice/client"; + +export default function PostSelect() { + const { data } = api.post.findMany.useQuery({ + where: { + title: { + contains: "" + } + } + }) + +} \ No newline at end of file diff --git a/apps/web/src/components/models/post/PostSelect/PostSelect.tsx b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx index 63f4058..f97d148 100644 --- a/apps/web/src/components/models/post/PostSelect/PostSelect.tsx +++ b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx @@ -91,6 +91,7 @@ export default function PostSelect({ dropdownStyle={{ minWidth: 200, // 设置合适的最小宽度 }} + autoClearSearchValue placeholder={placeholder} onChange={onChange} filterOption={false} diff --git a/apps/web/src/components/models/post/SubPost/CourseCard.tsx b/apps/web/src/components/models/post/SubPost/CourseCard.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/components/models/post/SubPost/PathCard.tsx b/apps/web/src/components/models/post/SubPost/PathCard.tsx old mode 100644 new mode 100755 diff --git a/apps/web/src/components/presentation/video-player/VideoDisplay.tsx b/apps/web/src/components/presentation/video-player/VideoDisplay.tsx index 32b1109..7260304 100755 --- a/apps/web/src/components/presentation/video-player/VideoDisplay.tsx +++ b/apps/web/src/components/presentation/video-player/VideoDisplay.tsx @@ -25,6 +25,7 @@ export const VideoDisplay: React.FC = ({ isDragging, setIsDragging, progressRef, + isPlaying } = useContext(VideoPlayerContext); // 处理进度条拖拽 @@ -191,9 +192,20 @@ export const VideoDisplay: React.FC = ({ }; }, [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 ( -
+