From 83f007d20e69ae2358b49f8ce29aef99e67fb175 Mon Sep 17 00:00:00 2001 From: aid Date: Tue, 31 Mar 2026 15:39:14 +0800 Subject: [PATCH] =?UTF-8?q?Day5:=20=E6=9C=BA=E6=9E=84=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=BC=98=E5=8C=96=20=E2=80=94=20=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E5=86=85=E9=83=A8=E7=A7=9F=E6=88=B7+=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E6=90=9C=E7=B4=A2+=E5=BF=AB=E6=8D=B7=E6=93=8D?= =?UTF-8?q?=E4=BD=9C+=E6=96=B0=E5=BB=BA=E5=BC=95=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端过滤系统内部租户(super/public/school等),列表只展示真实机构 - 搜索改为后端分页查询(keyword+tenantType参数),去掉前端过滤 - 表格新增登录地址列,一键复制完整URL - 新增停用/启用快捷按钮(PATCH /tenants/:id/status) - 新建机构成功后弹出引导,可直接跳转创建管理员账号 - 修复编辑弹窗因模板访问window导致的渲染崩溃 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/src/tenants/tenants.controller.ts | 13 +- backend/src/tenants/tenants.service.ts | 49 ++- docs/design/README.md | 1 + docs/design/super-admin/org-management.md | 42 +++ frontend/src/api/tenants.ts | 6 + frontend/src/views/system/tenants/Index.vue | 316 +++++++++++--------- 6 files changed, 277 insertions(+), 150 deletions(-) create mode 100644 docs/design/super-admin/org-management.md diff --git a/backend/src/tenants/tenants.controller.ts b/backend/src/tenants/tenants.controller.ts index d1a254d..d02d08f 100644 --- a/backend/src/tenants/tenants.controller.ts +++ b/backend/src/tenants/tenants.controller.ts @@ -35,10 +35,17 @@ export class TenantsController { @RequirePermission('tenant:read') findAll( @Query('page', new ParseIntPipe({ optional: true })) page: number = 1, - @Query('pageSize', new ParseIntPipe({ optional: true })) - pageSize: number = 10, + @Query('pageSize', new ParseIntPipe({ optional: true })) pageSize: number = 10, + @Query('keyword') keyword?: string, + @Query('tenantType') tenantType?: string, ) { - return this.tenantsService.findAll(page, pageSize); + return this.tenantsService.findAll({ page, pageSize, keyword, tenantType }); + } + + @Patch(':id/status') + @RequirePermission('tenant:update') + toggleStatus(@Param('id', ParseIntPipe) id: number, @Request() req) { + return this.tenantsService.toggleStatus(id, req.user?.tenantId); } @Get(':id') diff --git a/backend/src/tenants/tenants.service.ts b/backend/src/tenants/tenants.service.ts index 5532ea7..f2344ce 100644 --- a/backend/src/tenants/tenants.service.ts +++ b/backend/src/tenants/tenants.service.ts @@ -84,18 +84,33 @@ export class TenantsService { }); } - async findAll(page: number = 1, pageSize: number = 10) { + // 系统内部租户编码(不在机构列表中展示) + private readonly INTERNAL_TENANT_CODES = ['super', 'public', 'school', 'teacher', 'student', 'judge']; + + async findAll(params: { page?: number; pageSize?: number; keyword?: string; tenantType?: string } = {}) { + const { page = 1, pageSize = 10, keyword, tenantType } = params; const skip = (page - 1) * pageSize; + + const where: any = { + code: { notIn: this.INTERNAL_TENANT_CODES }, + validState: { not: undefined }, + }; + if (keyword) { + where.OR = [ + { name: { contains: keyword } }, + { code: { contains: keyword } }, + ]; + } + if (tenantType) { + where.tenantType = tenantType; + } + const [list, total] = await Promise.all([ this.prisma.tenant.findMany({ + where, skip, take: pageSize, include: { - menus: { - include: { - menu: true, - }, - }, _count: { select: { users: true, @@ -107,15 +122,23 @@ export class TenantsService { createTime: 'desc', }, }), - this.prisma.tenant.count(), + this.prisma.tenant.count({ where }), ]); - return { - list, - total, - page, - pageSize, - }; + return { list, total, page, pageSize }; + } + + /** 切换租户启用/停用状态 */ + async toggleStatus(id: number, currentTenantId?: number) { + await this.checkSuperTenant(currentTenantId); + const tenant = await this.prisma.tenant.findUnique({ where: { id } }); + if (!tenant) throw new NotFoundException('租户不存在'); + if (tenant.isSuper === 1) throw new BadRequestException('不能停用超级租户'); + + return this.prisma.tenant.update({ + where: { id }, + data: { validState: tenant.validState === 1 ? 2 : 1 }, + }); } async findOne(id: number) { diff --git a/docs/design/README.md b/docs/design/README.md index a749151..373b14d 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -11,6 +11,7 @@ | [评审进度优化](./super-admin/review-progress-optimization.md) | 活动监管 | 已实现(待验收) | 2026-03-27 | | [成果发布优化](./super-admin/results-publish-optimization.md) | 活动监管 | 已实现(待验收) | 2026-03-27 | | [内容管理模块](./super-admin/content-management.md) | 内容管理 | P0 已实现并优化 | 2026-03-31 | +| [机构管理优化](./super-admin/org-management.md) | 机构管理 | 已优化 | 2026-03-31 | ## 机构管理端 diff --git a/docs/design/super-admin/org-management.md b/docs/design/super-admin/org-management.md new file mode 100644 index 0000000..2ac7bcf --- /dev/null +++ b/docs/design/super-admin/org-management.md @@ -0,0 +1,42 @@ +# 超管端机构管理 — 优化记录 + +> 所属端:超管端 +> 状态:已优化 +> 创建日期:2026-03-31 +> 最后更新:2026-03-31 + +--- + +## 模块说明 + +超管端「机构管理」菜单,管理平台接入的外部机构(图书馆、学校、幼儿园等)。每个机构对应一个租户,拥有独立的用户、角色、菜单权限。 + +## Day5 (2026-03-31) — 优化内容 + +### 1. 隐藏系统内部租户 +- 后端列表查询过滤 super/public/school/teacher/student/judge 等系统内部编码 +- 列表只展示真实外部机构 + +### 2. 搜索改为后端分页 +- keyword(名称/编码)和 tenantType 参数传后端查询 +- 去掉前端 computed 过滤,支持大数据量 +- 类型下拉选择后自动触发查询 + +### 3. 新增登录地址列 +- 表格新增「登录地址」列,显示 `/:code/login` +- 旁边有复制按钮,一键复制完整 URL,方便运营发给机构管理员 + +### 4. 停用/启用快捷操作 +- 操作列新增停用/启用按钮(二次确认提示影响) +- 后端新增 `PATCH /tenants/:id/status` 接口 + +### 5. 新建后引导 +- 创建机构成功后弹出引导弹窗 +- 提供「为该机构创建管理员账号」按钮,跳转用户管理页 +- 避免创建机构后不知道下一步做什么 + +### 新增 API +``` +PATCH /api/tenants/:id/status — 切换租户启用/停用状态 +GET /api/tenants (新增参数) — keyword 关键词搜索 + tenantType 类型筛选 +``` diff --git a/frontend/src/api/tenants.ts b/frontend/src/api/tenants.ts index dd63a86..5ab4887 100644 --- a/frontend/src/api/tenants.ts +++ b/frontend/src/api/tenants.ts @@ -87,6 +87,11 @@ export async function getTenantMenus(id: number): Promise { return response; } +// 切换租户启用/停用 +export async function toggleTenantStatus(id: number): Promise { + return await request.patch(`/tenants/${id}/status`); +} + // 兼容性导出:保留 tenantsApi 对象 export const tenantsApi = { getList: getTenantsList, @@ -95,4 +100,5 @@ export const tenantsApi = { update: updateTenant, delete: deleteTenant, getTenantMenus: getTenantMenus, + toggleStatus: toggleTenantStatus, }; diff --git a/frontend/src/views/system/tenants/Index.vue b/frontend/src/views/system/tenants/Index.vue index a8ef765..d73b2e5 100644 --- a/frontend/src/views/system/tenants/Index.vue +++ b/frontend/src/views/system/tenants/Index.vue @@ -1,6 +1,6 @@