@gadmin2n/schematics 0.0.64 → 0.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +40 -26
- package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +30 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/example.prisma +33 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +163 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +70 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Game.ts +6 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/ITActivityDay.ts +70 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Log.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Role.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +20 -9
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env.local +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.eslintrc.js +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/.prettierignore +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/README.md +2 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/gadmin-cli.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/migrate-between-pg-schemas.js +1232 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +65 -38
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma/.generator.prisma +4 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/games.ts +1 -71
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +17 -21
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +278 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/seedDataMngtPages.ts +258 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/users.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/alias.config.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.ts +151 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +29 -13
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.service.ts +151 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/auth.guard.ts +87 -41
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/http-cache.interceptor.ts +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/logger.ts +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/safe-log.util.ts +176 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/{yufuid.ts → taihu.ts} +49 -34
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/tracing.ts +174 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/trim.pipe.ts +51 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/utils.ts +91 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/woaAuth.ts +25 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +22 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.ts +190 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +338 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.ts +83 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.ts +188 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.ts +83 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.ts +250 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.ts +1051 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.module.ts +13 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.ts +219 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.ts +199 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.ts +210 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.module.ts +12 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.ts +849 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/roles-refresher.service.ts +133 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.ts +201 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.ts +196 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.ts +216 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +198 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +104 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +130 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/tsconfig.json +18 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/index.html +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +34 -42
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/postcss.config.cjs +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +111 -185
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/auditLogProvider.ts +5 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +49 -51
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/custom-avatar.tsx +38 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +4 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/{header/index.tsx → header.tsx} +22 -31
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/index.ts +3 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/layout.tsx +32 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/logo.tsx +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +331 -166
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/pagination-total.tsx +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/tags/role-tag.tsx +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/text.tsx +74 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +258 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/audit-log.enum.ts +13 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/enums/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/login.ts +22 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +211 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useFetchData.ts +33 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useRoles.ts +30 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useUserPageAccess.ts +339 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/i18n.ts +8 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/index.tsx +3 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/en/common.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/{public → src}/locales/zh_CN/common.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.css +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.tsx +134 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +213 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/AssignRolesModal.tsx +168 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/CreatePageModal.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/EditPageModal.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageDetailDrawer.tsx +101 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/PageFormModal.tsx +731 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/hooks/usePageManagement.ts +36 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +1113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/queries.ts +17 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/types.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/pageResource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permission-readme/index.tsx +1088 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/CreateModal.tsx +25 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/EditModal.tsx +28 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/ResourceDetailDrawer.tsx +160 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/modal.tsx +202 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.ts +9 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +184 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/queries.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/types.ts +9 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/CreateModal.tsx +30 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/EditModal.tsx +47 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/RoleDetailDrawer.tsx +56 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/modal.tsx +302 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/hooks/useRolePage.ts +35 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +382 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/queries.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/types.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/rolePages/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/list.tsx +243 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/roleResource/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/create-modal.tsx +17 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/edit-modal.tsx +19 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +188 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/index.ts +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/role-tag.tsx +48 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/show-drawer.tsx +140 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/create.tsx +113 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/edit.tsx +122 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/index.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +342 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/queries.ts +14 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/show.tsx +61 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +132 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/fc.css +58 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +128 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-drawer.module.css +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-page.module.css +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/audit-log.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/index.ts +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/role.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/types/user.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-name-initials.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/get-random-color.ts +27 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/utils.tsx +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/vite-env.d.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/tsconfig.json +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/vite.config.ts +31 -0
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/sample.prisma +0 -65
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Source.ts +0 -76
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Tasklog.ts +0 -76
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/roles.ts +0 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/craco.config.js +0 -27
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/public/index.html +0 -53
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/styles.ts +0 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/react-app-env.d.ts +0 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/reportWebVitals.ts +0 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.less +0 -79
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/VanillaJSONEditor/{index.js → index.jsx} +0 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import type { PageMenuTreeNode, PageResourceAssignment, ResourceDefinition } from "../types";
|
|
3
|
+
import { useModalForm } from "@refinedev/antd";
|
|
4
|
+
import { type HttpError, type RedirectAction } from "@refinedev/core";
|
|
5
|
+
import {
|
|
6
|
+
Form,
|
|
7
|
+
Input,
|
|
8
|
+
Modal,
|
|
9
|
+
Space,
|
|
10
|
+
Tag,
|
|
11
|
+
TreeSelect,
|
|
12
|
+
Typography,
|
|
13
|
+
Transfer,
|
|
14
|
+
Checkbox,
|
|
15
|
+
Table,
|
|
16
|
+
Divider,
|
|
17
|
+
} from "antd";
|
|
18
|
+
import type { TransferProps } from "antd";
|
|
19
|
+
import { PageFieldSelector } from "../queries";
|
|
20
|
+
|
|
21
|
+
type PageFormFieldValues = {
|
|
22
|
+
name?: string;
|
|
23
|
+
zhName?: string;
|
|
24
|
+
enName?: string;
|
|
25
|
+
code?: string;
|
|
26
|
+
path?: string | null;
|
|
27
|
+
isVirtual?: boolean;
|
|
28
|
+
parentId?: number | null;
|
|
29
|
+
description?: string;
|
|
30
|
+
iframeUrl?: string;
|
|
31
|
+
externalRedirectUrl?: string;
|
|
32
|
+
selectedResourceIds?: number[];
|
|
33
|
+
resourcePermissionAssignments?: Record<number, string[]>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const AVAILABLE_PERMISSION_ACTIONS = ["readAny", "createAny", "updateAny", "deleteAny"];
|
|
37
|
+
|
|
38
|
+
const SPECIAL_CHARACTER_VALIDATION_PATTERN = /[^\u4e00-\u9fa5a-zA-Z0-9\s\-_/]/;
|
|
39
|
+
|
|
40
|
+
interface PageFormModalProps {
|
|
41
|
+
formActionType: "create" | "edit";
|
|
42
|
+
editingPageId?: number;
|
|
43
|
+
redirectAfterSubmit?: RedirectAction;
|
|
44
|
+
onFormSubmitSuccess?: () => void;
|
|
45
|
+
onModalCancel?: () => void;
|
|
46
|
+
parentPageTreeOptions?: PageMenuTreeNode[];
|
|
47
|
+
allAvailableResources?: ResourceDefinition[];
|
|
48
|
+
currentResourceAssignments?: PageResourceAssignment[];
|
|
49
|
+
allPagesData?: any[]; // All pages data for duplication check
|
|
50
|
+
defaultParentId?: number | null; // Default parent ID for create mode
|
|
51
|
+
defaultPathPrefix?: string; // Default path prefix for create mode
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const buildPageTreeNodeLookupMap = (
|
|
55
|
+
treeNodes?: PageMenuTreeNode[],
|
|
56
|
+
): Map<string, PageMenuTreeNode> => {
|
|
57
|
+
const nodeLookupMap = new Map<string, PageMenuTreeNode>();
|
|
58
|
+
|
|
59
|
+
const traverseAndAddToMap = (nodes?: PageMenuTreeNode[]) => {
|
|
60
|
+
nodes?.forEach(node => {
|
|
61
|
+
nodeLookupMap.set(String(node.value), node);
|
|
62
|
+
if (node.children?.length) {
|
|
63
|
+
traverseAndAddToMap(node.children);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
traverseAndAddToMap(treeNodes);
|
|
69
|
+
return nodeLookupMap;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const PageFormModal: React.FC<PageFormModalProps> = ({
|
|
73
|
+
formActionType,
|
|
74
|
+
editingPageId,
|
|
75
|
+
redirectAfterSubmit,
|
|
76
|
+
onModalCancel,
|
|
77
|
+
onFormSubmitSuccess,
|
|
78
|
+
parentPageTreeOptions,
|
|
79
|
+
allAvailableResources = [],
|
|
80
|
+
currentResourceAssignments = [],
|
|
81
|
+
allPagesData = [],
|
|
82
|
+
defaultParentId,
|
|
83
|
+
defaultPathPrefix = "",
|
|
84
|
+
}) => {
|
|
85
|
+
const { formProps, modalProps, onFinish, close } = useModalForm<
|
|
86
|
+
any,
|
|
87
|
+
HttpError,
|
|
88
|
+
PageFormFieldValues
|
|
89
|
+
>({
|
|
90
|
+
resource: "page",
|
|
91
|
+
action: formActionType,
|
|
92
|
+
id: editingPageId,
|
|
93
|
+
defaultVisible: true,
|
|
94
|
+
redirect: redirectAfterSubmit,
|
|
95
|
+
onMutationSuccess: () => {
|
|
96
|
+
onFormSubmitSuccess?.();
|
|
97
|
+
},
|
|
98
|
+
meta: {
|
|
99
|
+
select: PageFieldSelector,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Watch form values for conditional field visibility
|
|
104
|
+
const isVirtual = Form.useWatch("isVirtual", formProps.form);
|
|
105
|
+
const iframeUrl = Form.useWatch("iframeUrl", formProps.form);
|
|
106
|
+
const externalRedirectUrl = Form.useWatch("externalRedirectUrl", formProps.form);
|
|
107
|
+
const hasExternalUrl = !!(
|
|
108
|
+
(iframeUrl && /^(https?:\/\/|\/\w)/.test(iframeUrl.trim())) ||
|
|
109
|
+
(externalRedirectUrl && /^https?:\/\//.test(externalRedirectUrl.trim()))
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const [selectedResourceIds, setSelectedResourceIds] = useState<string[]>([]);
|
|
113
|
+
const [resourcePermissionMap, setResourcePermissionMap] = useState<Record<number, string[]>>({});
|
|
114
|
+
const [permissionValidationError, setPermissionValidationError] = useState<string>("");
|
|
115
|
+
const [initialResourceAssignments, setInitialResourceAssignments] = useState<{
|
|
116
|
+
resourceIds: string[];
|
|
117
|
+
permissionMap: Record<number, string[]>;
|
|
118
|
+
}>({ resourceIds: [], permissionMap: {} });
|
|
119
|
+
const [initialFormValues, setInitialFormValues] = useState<PageFormFieldValues>({});
|
|
120
|
+
const [isCodeManuallyEdited, setIsCodeManuallyEdited] = useState(false);
|
|
121
|
+
const [isPathManuallyEdited, setIsPathManuallyEdited] = useState(false);
|
|
122
|
+
|
|
123
|
+
// Set default values for create mode with parent
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (formActionType === "create" && formProps.form) {
|
|
126
|
+
const initialValues: Partial<PageFormFieldValues> = {};
|
|
127
|
+
|
|
128
|
+
if (defaultParentId) {
|
|
129
|
+
initialValues.parentId = defaultParentId;
|
|
130
|
+
}
|
|
131
|
+
if (defaultPathPrefix) {
|
|
132
|
+
initialValues.path = defaultPathPrefix;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (Object.keys(initialValues).length > 0) {
|
|
136
|
+
formProps.form.setFieldsValue(initialValues);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, [formActionType, defaultParentId, defaultPathPrefix, formProps.form]);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (formActionType === "edit" && currentResourceAssignments.length > 0) {
|
|
143
|
+
const initialSelectedIds = currentResourceAssignments.map(assignment =>
|
|
144
|
+
String(assignment.resourceId),
|
|
145
|
+
);
|
|
146
|
+
setSelectedResourceIds(initialSelectedIds);
|
|
147
|
+
|
|
148
|
+
const initialPermissionMap: Record<number, string[]> = {};
|
|
149
|
+
currentResourceAssignments.forEach(assignment => {
|
|
150
|
+
initialPermissionMap[assignment.resourceId] = [...assignment.assignedPermissions];
|
|
151
|
+
});
|
|
152
|
+
setResourcePermissionMap(initialPermissionMap);
|
|
153
|
+
|
|
154
|
+
// Store initial state for comparison
|
|
155
|
+
setInitialResourceAssignments({
|
|
156
|
+
resourceIds: [...initialSelectedIds],
|
|
157
|
+
permissionMap: JSON.parse(JSON.stringify(initialPermissionMap)),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}, [formActionType, currentResourceAssignments]);
|
|
161
|
+
|
|
162
|
+
// Store initial form values when form is loaded in edit mode
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (formActionType === "edit" && formProps.form) {
|
|
165
|
+
const currentValues = formProps.form.getFieldsValue();
|
|
166
|
+
setInitialFormValues({
|
|
167
|
+
name: currentValues.name,
|
|
168
|
+
zhName: currentValues.zhName,
|
|
169
|
+
enName: currentValues.enName,
|
|
170
|
+
code: currentValues.code,
|
|
171
|
+
path: currentValues.path,
|
|
172
|
+
isVirtual: currentValues.isVirtual,
|
|
173
|
+
parentId: currentValues.parentId,
|
|
174
|
+
description: currentValues.description,
|
|
175
|
+
iframeUrl: currentValues.iframeUrl,
|
|
176
|
+
externalRedirectUrl: currentValues.externalRedirectUrl,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}, [formActionType, formProps.form, formProps.initialValues]);
|
|
180
|
+
|
|
181
|
+
const resourceTransferDataSource = useMemo(() => {
|
|
182
|
+
return allAvailableResources.map(resource => ({
|
|
183
|
+
key: String(resource.id),
|
|
184
|
+
title: resource.name,
|
|
185
|
+
description: resource.description || "",
|
|
186
|
+
}));
|
|
187
|
+
}, [allAvailableResources]);
|
|
188
|
+
|
|
189
|
+
const handleResourceTransferChange: TransferProps["onChange"] = newTargetKeys => {
|
|
190
|
+
setSelectedResourceIds(newTargetKeys as string[]);
|
|
191
|
+
|
|
192
|
+
const newResourceIds = new Set(newTargetKeys.map(key => Number(key)));
|
|
193
|
+
setResourcePermissionMap(prevMap => {
|
|
194
|
+
const updatedMap: Record<number, string[]> = {};
|
|
195
|
+
Object.keys(prevMap).forEach(resourceIdStr => {
|
|
196
|
+
const resourceId = Number(resourceIdStr);
|
|
197
|
+
if (newResourceIds.has(resourceId)) {
|
|
198
|
+
updatedMap[resourceId] = prevMap[resourceId];
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
newTargetKeys.forEach(key => {
|
|
202
|
+
const resourceId = Number(key);
|
|
203
|
+
if (!updatedMap[resourceId]) {
|
|
204
|
+
updatedMap[resourceId] = []; // Initialize with empty array, user must select at least one permission
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return updatedMap;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Clear validation error when user changes resource selection
|
|
211
|
+
if (permissionValidationError) {
|
|
212
|
+
setPermissionValidationError("");
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const handleResourcePermissionChange = (
|
|
217
|
+
resourceId: number,
|
|
218
|
+
permissionAction: string,
|
|
219
|
+
isChecked: boolean,
|
|
220
|
+
) => {
|
|
221
|
+
setResourcePermissionMap(prevMap => {
|
|
222
|
+
const currentPermissions = prevMap[resourceId] || [];
|
|
223
|
+
let updatedPermissions: string[];
|
|
224
|
+
|
|
225
|
+
if (isChecked) {
|
|
226
|
+
updatedPermissions = [...currentPermissions, permissionAction];
|
|
227
|
+
} else {
|
|
228
|
+
updatedPermissions = currentPermissions.filter(action => action !== permissionAction);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
...prevMap,
|
|
233
|
+
[resourceId]: updatedPermissions,
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Clear validation error when user makes changes
|
|
238
|
+
if (permissionValidationError) {
|
|
239
|
+
setPermissionValidationError("");
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const filteredParentPageTreeOptions = useMemo(() => {
|
|
244
|
+
if (formActionType !== "edit" || !editingPageId || !parentPageTreeOptions) {
|
|
245
|
+
return parentPageTreeOptions;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const filterOutCurrentPage = (nodes: PageMenuTreeNode[]): PageMenuTreeNode[] => {
|
|
249
|
+
return nodes
|
|
250
|
+
.filter(node => String(node.value) !== String(editingPageId))
|
|
251
|
+
.map(node => ({
|
|
252
|
+
...node,
|
|
253
|
+
children: node.children ? filterOutCurrentPage(node.children) : undefined,
|
|
254
|
+
}));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return filterOutCurrentPage(parentPageTreeOptions);
|
|
258
|
+
}, [parentPageTreeOptions, formActionType, editingPageId]);
|
|
259
|
+
|
|
260
|
+
formProps.onFinish = values => {
|
|
261
|
+
// Clear path when external URL is configured (iframe or redirect)
|
|
262
|
+
if (!hasExternalUrl) {
|
|
263
|
+
values.path = null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Validate that each selected resource has at least one permission
|
|
267
|
+
const resourcesWithoutPermissions: string[] = [];
|
|
268
|
+
selectedResourceIds.forEach(resourceIdStr => {
|
|
269
|
+
const resourceId = Number(resourceIdStr);
|
|
270
|
+
const permissions = resourcePermissionMap[resourceId] || [];
|
|
271
|
+
if (permissions.length === 0) {
|
|
272
|
+
const resource = allAvailableResources.find(r => r.id === resourceId);
|
|
273
|
+
resourcesWithoutPermissions.push(resource?.name || `Resource ${resourceId}`);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
if (resourcesWithoutPermissions.length > 0) {
|
|
278
|
+
setPermissionValidationError(
|
|
279
|
+
`The following resources must have at least one permission selected: ${resourcesWithoutPermissions.join(", ")}`,
|
|
280
|
+
);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// In edit mode, check if anything has changed
|
|
285
|
+
if (formActionType === "edit") {
|
|
286
|
+
// Check if form fields have changed
|
|
287
|
+
const formFieldsChanged =
|
|
288
|
+
values.name !== initialFormValues.name ||
|
|
289
|
+
values.zhName !== initialFormValues.zhName ||
|
|
290
|
+
values.enName !== initialFormValues.enName ||
|
|
291
|
+
values.code !== initialFormValues.code ||
|
|
292
|
+
values.path !== initialFormValues.path ||
|
|
293
|
+
values.description !== initialFormValues.description ||
|
|
294
|
+
values.isVirtual !== initialFormValues.isVirtual ||
|
|
295
|
+
values.parentId !== initialFormValues.parentId ||
|
|
296
|
+
values.iframeUrl !== initialFormValues.iframeUrl ||
|
|
297
|
+
values.externalRedirectUrl !== initialFormValues.externalRedirectUrl;
|
|
298
|
+
|
|
299
|
+
// Check if resource assignments have changed
|
|
300
|
+
const resourceIdsChanged =
|
|
301
|
+
selectedResourceIds.length !== initialResourceAssignments.resourceIds.length ||
|
|
302
|
+
selectedResourceIds.some(id => !initialResourceAssignments.resourceIds.includes(id));
|
|
303
|
+
|
|
304
|
+
let permissionsChanged = false;
|
|
305
|
+
if (!resourceIdsChanged) {
|
|
306
|
+
// Check if permissions for any resource have changed
|
|
307
|
+
for (const resourceIdStr of selectedResourceIds) {
|
|
308
|
+
const resourceId = Number(resourceIdStr);
|
|
309
|
+
const currentPermissions = resourcePermissionMap[resourceId] || [];
|
|
310
|
+
const initialPermissions = initialResourceAssignments.permissionMap[resourceId] || [];
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
currentPermissions.length !== initialPermissions.length ||
|
|
314
|
+
currentPermissions.some(p => !initialPermissions.includes(p))
|
|
315
|
+
) {
|
|
316
|
+
permissionsChanged = true;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// If nothing has changed, don't submit
|
|
323
|
+
if (!formFieldsChanged && !resourceIdsChanged && !permissionsChanged) {
|
|
324
|
+
Modal.info({
|
|
325
|
+
title: "No Changes Detected",
|
|
326
|
+
content:
|
|
327
|
+
"No changes have been made to the form. The form will be closed without submitting.",
|
|
328
|
+
});
|
|
329
|
+
close();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Build the submit payload with only changed data
|
|
334
|
+
const formattedValuesForSubmit: any = {
|
|
335
|
+
...values,
|
|
336
|
+
parentId: values.parentId ? Number(values.parentId) : null,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Only include resourceAssignments if they changed
|
|
340
|
+
if (resourceIdsChanged || permissionsChanged) {
|
|
341
|
+
formattedValuesForSubmit.resourceAssignments = selectedResourceIds.map(resourceIdStr => {
|
|
342
|
+
const resourceId = Number(resourceIdStr);
|
|
343
|
+
const resource = allAvailableResources.find(r => r.id === resourceId);
|
|
344
|
+
return {
|
|
345
|
+
resourceId,
|
|
346
|
+
resourceName: resource?.name || "",
|
|
347
|
+
assignedPermissions: resourcePermissionMap[resourceId] || [],
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
onFinish(formattedValuesForSubmit);
|
|
352
|
+
} else {
|
|
353
|
+
// In create mode, always submit all data
|
|
354
|
+
const resourceAssignmentsForSubmit = selectedResourceIds.map(resourceIdStr => {
|
|
355
|
+
const resourceId = Number(resourceIdStr);
|
|
356
|
+
const resource = allAvailableResources.find(r => r.id === resourceId);
|
|
357
|
+
return {
|
|
358
|
+
resourceId,
|
|
359
|
+
resourceName: resource?.name || "",
|
|
360
|
+
assignedPermissions: resourcePermissionMap[resourceId] || [],
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const formattedValuesForSubmit = {
|
|
365
|
+
...values,
|
|
366
|
+
parentId: values.parentId ? Number(values.parentId) : null,
|
|
367
|
+
resourceAssignments: resourceAssignmentsForSubmit,
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
onFinish(formattedValuesForSubmit);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const resourcePermissionTableColumns = [
|
|
375
|
+
{
|
|
376
|
+
title: "Resource Name",
|
|
377
|
+
dataIndex: "resourceName",
|
|
378
|
+
key: "resourceName",
|
|
379
|
+
width: "30%",
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
title: "Permissions",
|
|
383
|
+
key: "permissions",
|
|
384
|
+
render: (_: any, record: { resourceId: number; resourceName: string }) => (
|
|
385
|
+
<Space>
|
|
386
|
+
{AVAILABLE_PERMISSION_ACTIONS.map(permissionAction => (
|
|
387
|
+
<Checkbox
|
|
388
|
+
key={`${record.resourceId}-${permissionAction}`}
|
|
389
|
+
checked={resourcePermissionMap[record.resourceId]?.includes(permissionAction)}
|
|
390
|
+
onChange={e =>
|
|
391
|
+
handleResourcePermissionChange(
|
|
392
|
+
record.resourceId,
|
|
393
|
+
permissionAction,
|
|
394
|
+
e.target.checked,
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
>
|
|
398
|
+
{permissionAction}
|
|
399
|
+
</Checkbox>
|
|
400
|
+
))}
|
|
401
|
+
</Space>
|
|
402
|
+
),
|
|
403
|
+
},
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
const selectedResourcesForPermissionTable = useMemo(() => {
|
|
407
|
+
return selectedResourceIds.map(resourceIdStr => {
|
|
408
|
+
const resourceId = Number(resourceIdStr);
|
|
409
|
+
const resource = allAvailableResources.find(r => r.id === resourceId);
|
|
410
|
+
return {
|
|
411
|
+
resourceId,
|
|
412
|
+
resourceName: resource?.name || `Resource ${resourceId}`,
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
}, [selectedResourceIds, allAvailableResources]);
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<Modal
|
|
419
|
+
{...modalProps}
|
|
420
|
+
width={800}
|
|
421
|
+
onCancel={() => {
|
|
422
|
+
if (onModalCancel) {
|
|
423
|
+
onModalCancel();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
close();
|
|
427
|
+
}}
|
|
428
|
+
title={formActionType === "create" ? "Create New Page" : "Edit Page"}
|
|
429
|
+
>
|
|
430
|
+
<Form {...formProps} layout="vertical">
|
|
431
|
+
<Form.Item
|
|
432
|
+
rules={[
|
|
433
|
+
{ required: true, message: "Page name is required" },
|
|
434
|
+
{
|
|
435
|
+
validator: (_, value) => {
|
|
436
|
+
if (!value || !SPECIAL_CHARACTER_VALIDATION_PATTERN.test(value)) {
|
|
437
|
+
return Promise.resolve();
|
|
438
|
+
}
|
|
439
|
+
return Promise.reject(
|
|
440
|
+
new Error("Special characters are not allowed in page name."),
|
|
441
|
+
);
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
validator: async (_, value) => {
|
|
446
|
+
if (!value) {
|
|
447
|
+
return Promise.resolve();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const trimmedValue = value.trim();
|
|
451
|
+
|
|
452
|
+
// Check if page name already exists (case-insensitive)
|
|
453
|
+
const isDuplicate = allPagesData.some((page: any) => {
|
|
454
|
+
// Skip current page when editing
|
|
455
|
+
if (formActionType === "edit" && page.id === editingPageId) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
return page.name?.toLowerCase() === trimmedValue.toLowerCase();
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
if (isDuplicate) {
|
|
462
|
+
return Promise.reject(
|
|
463
|
+
new Error(
|
|
464
|
+
"⚠️ Warning: Page Name already exists. It's recommended to use a unique name.",
|
|
465
|
+
),
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return Promise.resolve();
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
]}
|
|
473
|
+
name="name"
|
|
474
|
+
label="Page Name"
|
|
475
|
+
>
|
|
476
|
+
<Input
|
|
477
|
+
placeholder="Enter the page name (e.g., User Management)."
|
|
478
|
+
onChange={e => {
|
|
479
|
+
const nameValue = e.target.value;
|
|
480
|
+
// Auto-generate code from name if code hasn't been manually edited
|
|
481
|
+
if (!isCodeManuallyEdited && nameValue) {
|
|
482
|
+
const generatedCode = nameValue
|
|
483
|
+
.trim()
|
|
484
|
+
.toLowerCase()
|
|
485
|
+
.replace(/[\s-]+/g, "_") // Replace spaces and hyphens with underscores
|
|
486
|
+
.replace(/[^a-z0-9_]/g, ""); // Remove invalid characters
|
|
487
|
+
formProps.form?.setFieldsValue({ code: generatedCode });
|
|
488
|
+
}
|
|
489
|
+
// Auto-generate path from name if path hasn't been manually edited (only in create mode)
|
|
490
|
+
if (!isPathManuallyEdited && nameValue && formActionType === "create") {
|
|
491
|
+
const generatedPathSegment = nameValue
|
|
492
|
+
.trim()
|
|
493
|
+
.toLowerCase()
|
|
494
|
+
.replace(/[\s_]+/g, "-") // Replace spaces and underscores with hyphens
|
|
495
|
+
.replace(/[^a-z0-9-/]/g, ""); // Remove invalid characters
|
|
496
|
+
const newPath = defaultPathPrefix + generatedPathSegment;
|
|
497
|
+
formProps.form?.setFieldsValue({ path: newPath });
|
|
498
|
+
}
|
|
499
|
+
}}
|
|
500
|
+
/>
|
|
501
|
+
</Form.Item>
|
|
502
|
+
<Form.Item
|
|
503
|
+
rules={[
|
|
504
|
+
{ required: true, message: "Page Code is required" },
|
|
505
|
+
{
|
|
506
|
+
pattern: /^[a-zA-Z0-9_]+$/,
|
|
507
|
+
message: "Page Code can only contain letters, numbers and underscores",
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
validator: async (_, value) => {
|
|
511
|
+
if (!value) {
|
|
512
|
+
return Promise.resolve();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const trimmedValue = value.trim();
|
|
516
|
+
|
|
517
|
+
// Check if page code already exists (case-insensitive)
|
|
518
|
+
const isDuplicate = allPagesData.some((page: any) => {
|
|
519
|
+
// Skip current page when editing
|
|
520
|
+
if (formActionType === "edit" && page.id === editingPageId) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
return page.code?.toLowerCase() === trimmedValue.toLowerCase();
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (isDuplicate) {
|
|
527
|
+
return Promise.reject(
|
|
528
|
+
new Error("Page Code already exists. Please use a different code."),
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return Promise.resolve();
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
]}
|
|
536
|
+
name="code"
|
|
537
|
+
label="Page Code(建议使用根据Page Name自动生成的值。一旦保存,不建议修改,因为前端会使用它作为菜单标识)"
|
|
538
|
+
>
|
|
539
|
+
<Input
|
|
540
|
+
placeholder="Enter the page code (e.g., user_management)"
|
|
541
|
+
onChange={() => {
|
|
542
|
+
// Mark as manually edited when user types in this field
|
|
543
|
+
if (!isCodeManuallyEdited) {
|
|
544
|
+
setIsCodeManuallyEdited(true);
|
|
545
|
+
}
|
|
546
|
+
}}
|
|
547
|
+
/>
|
|
548
|
+
</Form.Item>
|
|
549
|
+
<Form.Item name="isVirtual" valuePropName="checked">
|
|
550
|
+
<Checkbox>Is Virtual Page</Checkbox>
|
|
551
|
+
</Form.Item>
|
|
552
|
+
{!isVirtual && (
|
|
553
|
+
<>
|
|
554
|
+
<Form.Item name="parentId" label="Parent Page (Menu Position)">
|
|
555
|
+
{filteredParentPageTreeOptions?.length ? (
|
|
556
|
+
<TreeSelect
|
|
557
|
+
style={{ width: "100%" }}
|
|
558
|
+
treeData={filteredParentPageTreeOptions}
|
|
559
|
+
treeDefaultExpandAll
|
|
560
|
+
allowClear
|
|
561
|
+
placeholder="Select parent page (leave empty for root level)"
|
|
562
|
+
/>
|
|
563
|
+
) : (
|
|
564
|
+
<>
|
|
565
|
+
<Typography.Text type="secondary">
|
|
566
|
+
No parent pages available. This page will be created at root level.
|
|
567
|
+
</Typography.Text>
|
|
568
|
+
<Input type="hidden" />
|
|
569
|
+
</>
|
|
570
|
+
)}
|
|
571
|
+
</Form.Item>
|
|
572
|
+
<Form.Item
|
|
573
|
+
name="zhName"
|
|
574
|
+
label="Chinese Name (中文菜单名)"
|
|
575
|
+
tooltip="用于菜单显示的中文名称"
|
|
576
|
+
>
|
|
577
|
+
<Input placeholder="输入中文菜单名 (例如: 用户管理)" />
|
|
578
|
+
</Form.Item>
|
|
579
|
+
<Form.Item
|
|
580
|
+
name="enName"
|
|
581
|
+
label="English Name (英文菜单名)"
|
|
582
|
+
tooltip="Menu display name in English"
|
|
583
|
+
>
|
|
584
|
+
<Input placeholder="Enter English menu name (e.g., User Management)" />
|
|
585
|
+
</Form.Item>
|
|
586
|
+
<Form.Item
|
|
587
|
+
name="iframeUrl"
|
|
588
|
+
label={"Iframe URL"}
|
|
589
|
+
tooltip={{
|
|
590
|
+
title: (
|
|
591
|
+
<div>
|
|
592
|
+
<p style={{ marginBottom: 8 }}>
|
|
593
|
+
设置后,页面将以 iframe 形式加载该 URL,而非内部组件渲染。
|
|
594
|
+
</p>
|
|
595
|
+
<p style={{ marginBottom: 4 }}>
|
|
596
|
+
<strong>支持的模板变量:</strong>
|
|
597
|
+
</p>
|
|
598
|
+
<ul style={{ margin: 0, paddingLeft: 16 }}>
|
|
599
|
+
<li>
|
|
600
|
+
<code>{"{{locale}}"}</code> → 当前语言 (en/zh)
|
|
601
|
+
</li>
|
|
602
|
+
</ul>
|
|
603
|
+
<p style={{ marginTop: 8 }}>
|
|
604
|
+
支持完整 URL 或相对路径(如 /path/to/page)
|
|
605
|
+
</p>
|
|
606
|
+
</div>
|
|
607
|
+
),
|
|
608
|
+
overlayStyle: { maxWidth: 320 },
|
|
609
|
+
}}
|
|
610
|
+
dependencies={["externalRedirectUrl"]}
|
|
611
|
+
rules={[
|
|
612
|
+
{
|
|
613
|
+
validator: (_, value) => {
|
|
614
|
+
if (value?.trim() && externalRedirectUrl?.trim()) {
|
|
615
|
+
return Promise.reject(new Error("不能同时填写 Iframe URL 和 External Redirect URL,请只填其中一个"));
|
|
616
|
+
}
|
|
617
|
+
return Promise.resolve();
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
]}
|
|
621
|
+
>
|
|
622
|
+
<Input placeholder="e.g. https://example.com/page or /relative/path" />
|
|
623
|
+
</Form.Item>
|
|
624
|
+
<Form.Item
|
|
625
|
+
name="externalRedirectUrl"
|
|
626
|
+
dependencies={["iframeUrl"]}
|
|
627
|
+
label={"External Redirect URL"}
|
|
628
|
+
tooltip={{
|
|
629
|
+
title: <p style={{ marginBottom: 8 }}>设置后,页面将打开一个新标签并跳转到指定的外部链接。</p>,
|
|
630
|
+
overlayStyle: { maxWidth: 320 },
|
|
631
|
+
}}
|
|
632
|
+
rules={[
|
|
633
|
+
{
|
|
634
|
+
type: "url",
|
|
635
|
+
message: "Please enter a valid URL",
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
validator: (_, value) => {
|
|
639
|
+
if (value?.trim() && iframeUrl?.trim()) {
|
|
640
|
+
return Promise.reject(new Error("不能同时填写 Iframe URL 和 External Redirect URL,请只填其中一个"));
|
|
641
|
+
}
|
|
642
|
+
return Promise.resolve();
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
]}
|
|
646
|
+
>
|
|
647
|
+
<Input placeholder="e.g. https://oit-erp.woa.com" />
|
|
648
|
+
</Form.Item>
|
|
649
|
+
{hasExternalUrl && (
|
|
650
|
+
<Form.Item
|
|
651
|
+
rules={[
|
|
652
|
+
{
|
|
653
|
+
validator: (_, value) => {
|
|
654
|
+
if (!value || !SPECIAL_CHARACTER_VALIDATION_PATTERN.test(value)) {
|
|
655
|
+
return Promise.resolve();
|
|
656
|
+
}
|
|
657
|
+
return Promise.reject(
|
|
658
|
+
new Error("Special characters are not allowed in path.")
|
|
659
|
+
);
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
]}
|
|
663
|
+
name="path"
|
|
664
|
+
label="Page Path"
|
|
665
|
+
tooltip="仅当前端 routeRegistry 中未注册该 page code 时需要填写(如 iframe/外部链接页面)。前端已注册的页面会自动匹配路径,无需手动填写。"
|
|
666
|
+
>
|
|
667
|
+
<Input
|
|
668
|
+
placeholder="Enter the URL path (e.g., /administration/page-management)"
|
|
669
|
+
onChange={() => {
|
|
670
|
+
// Mark as manually edited when user types in this field
|
|
671
|
+
if (!isPathManuallyEdited) {
|
|
672
|
+
setIsPathManuallyEdited(true);
|
|
673
|
+
}
|
|
674
|
+
}}
|
|
675
|
+
/>
|
|
676
|
+
</Form.Item>
|
|
677
|
+
)}
|
|
678
|
+
</>
|
|
679
|
+
)}
|
|
680
|
+
<Form.Item name="description" label="Page Description">
|
|
681
|
+
<Input.TextArea rows={2} placeholder="Enter a brief description of this page's purpose" />
|
|
682
|
+
</Form.Item>
|
|
683
|
+
<Divider>Resource Assignment & Permissions</Divider>
|
|
684
|
+
<Form.Item label="Select Resources to Assign">
|
|
685
|
+
<Transfer
|
|
686
|
+
dataSource={resourceTransferDataSource}
|
|
687
|
+
titles={["Available Resources", "Assigned Resources"]}
|
|
688
|
+
targetKeys={selectedResourceIds}
|
|
689
|
+
onChange={handleResourceTransferChange}
|
|
690
|
+
render={item => item.title}
|
|
691
|
+
showSearch
|
|
692
|
+
filterOption={(inputValue, item) =>
|
|
693
|
+
item.title.toLowerCase().includes(inputValue.toLowerCase()) ||
|
|
694
|
+
item.description.toLowerCase().includes(inputValue.toLowerCase())
|
|
695
|
+
}
|
|
696
|
+
listStyle={{ width: 280, height: 250 }}
|
|
697
|
+
/>
|
|
698
|
+
</Form.Item>
|
|
699
|
+
{selectedResourceIds.length > 0 && (
|
|
700
|
+
<Form.Item
|
|
701
|
+
label="Configure Permissions for Each Resource"
|
|
702
|
+
validateStatus={permissionValidationError ? "error" : ""}
|
|
703
|
+
help={permissionValidationError || ""}
|
|
704
|
+
>
|
|
705
|
+
<Table
|
|
706
|
+
dataSource={selectedResourcesForPermissionTable}
|
|
707
|
+
columns={resourcePermissionTableColumns}
|
|
708
|
+
rowKey="resourceId"
|
|
709
|
+
pagination={false}
|
|
710
|
+
size="small"
|
|
711
|
+
bordered
|
|
712
|
+
/>
|
|
713
|
+
</Form.Item>
|
|
714
|
+
)}
|
|
715
|
+
{formActionType === "edit" && currentResourceAssignments.length > 0 && (
|
|
716
|
+
<Form.Item label="Current Permission Overview">
|
|
717
|
+
<Space wrap>
|
|
718
|
+
{Array.from(
|
|
719
|
+
new Set(currentResourceAssignments.flatMap(a => a.assignedPermissions)),
|
|
720
|
+
).map(permissionAction => (
|
|
721
|
+
<Tag key={permissionAction} color="blue">
|
|
722
|
+
{permissionAction}
|
|
723
|
+
</Tag>
|
|
724
|
+
))}
|
|
725
|
+
</Space>
|
|
726
|
+
</Form.Item>
|
|
727
|
+
)}
|
|
728
|
+
</Form>
|
|
729
|
+
</Modal>
|
|
730
|
+
);
|
|
731
|
+
};
|