@qlover/create-app 0.10.0 → 0.10.2
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/CHANGELOG.md +145 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/config/IOCIdentifier.ts +2 -2
- package/dist/templates/next-app/config/Identifier/common/common.ts +14 -0
- package/dist/templates/next-app/config/Identifier/pages/index.ts +1 -0
- package/dist/templates/next-app/config/Identifier/pages/page.about.ts +20 -0
- package/dist/templates/next-app/config/common.ts +1 -1
- package/dist/templates/next-app/config/cookies.ts +23 -0
- package/dist/templates/next-app/config/i18n/AboutI18n.ts +14 -0
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +3 -1
- package/dist/templates/next-app/config/i18n/index.ts +1 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +8 -0
- package/dist/templates/next-app/config/theme.ts +4 -0
- package/dist/templates/next-app/eslint.config.mjs +4 -1
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
- package/dist/templates/next-app/next.config.ts +1 -0
- package/dist/templates/next-app/package.json +15 -7
- package/dist/templates/next-app/public/locales/en.json +5 -0
- package/dist/templates/next-app/public/locales/zh.json +5 -0
- package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +37 -0
- package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +30 -6
- package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/layout.tsx +47 -10
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +22 -10
- package/dist/templates/next-app/src/app/[locale]/page.tsx +23 -8
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +21 -9
- package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +7 -28
- package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +7 -34
- package/dist/templates/next-app/src/app/api/admin/locales/route.ts +12 -34
- package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +7 -26
- package/dist/templates/next-app/src/app/api/admin/users/route.ts +14 -33
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +13 -25
- package/dist/templates/next-app/src/app/api/user/login/route.ts +6 -46
- package/dist/templates/next-app/src/app/api/user/logout/route.ts +5 -24
- package/dist/templates/next-app/src/app/api/user/register/route.ts +6 -45
- package/dist/templates/next-app/src/app/manifest.ts +16 -0
- package/dist/templates/next-app/src/app/robots.txt +2 -0
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
- package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +12 -2
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
- package/dist/templates/next-app/src/base/cases/RouterService.ts +5 -5
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
- package/dist/templates/next-app/src/base/port/AppApiInterface.ts +22 -0
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
- package/dist/templates/next-app/src/base/port/IOCInterface.ts +9 -0
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
- package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
- package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
- package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
- package/dist/templates/next-app/src/base/types/{PageProps.ts → AppPageRouter.ts} +4 -1
- package/dist/templates/next-app/src/base/types/PagesRouter.ts +9 -0
- package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +19 -5
- package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +2 -2
- package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -1
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +33 -11
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +8 -5
- package/dist/templates/next-app/src/core/globals.ts +2 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +29 -10
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +6 -7
- package/dist/templates/next-app/src/i18n/loadMessages.ts +103 -0
- package/dist/templates/next-app/src/i18n/request.ts +3 -22
- package/dist/templates/next-app/src/pages/[locale]/about.tsx +61 -0
- package/dist/templates/next-app/src/pages/_app.tsx +50 -0
- package/dist/templates/next-app/src/pages/_document.tsx +13 -0
- package/dist/templates/next-app/src/{middleware.ts → proxy.ts} +2 -1
- package/dist/templates/next-app/src/server/AppPageRouteParams.ts +94 -0
- package/dist/templates/next-app/src/server/NextApiServer.ts +53 -0
- package/dist/templates/next-app/src/server/PagesRouteParams.ts +136 -0
- package/dist/templates/next-app/src/server/{sqlBridges/SupabaseBridge.ts → SupabaseBridge.ts} +2 -0
- package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
- package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +74 -0
- package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +39 -0
- package/dist/templates/next-app/src/server/controllers/LocalesController.ts +33 -0
- package/dist/templates/next-app/src/server/controllers/UserController.ts +77 -0
- package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +8 -0
- package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +21 -0
- package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +11 -0
- package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +10 -0
- package/dist/templates/next-app/src/server/port/{ParamsHandlerInterface.ts → RouteParamsnHandlerInterface.ts} +9 -2
- package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
- package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +8 -0
- package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +1 -1
- package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +2 -2
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +8 -2
- package/dist/templates/next-app/src/{base → server}/services/AdminLocalesService.ts +2 -2
- package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +25 -10
- package/dist/templates/next-app/src/server/services/ApiUserService.ts +5 -2
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +4 -2
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +1 -1
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +10 -10
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -44
- package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -44
- package/dist/templates/next-app/src/styles/css/index.css +1 -1
- package/dist/templates/next-app/src/styles/css/scrollbar.css +34 -0
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +34 -11
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +69 -39
- package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +64 -0
- package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +42 -0
- package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +34 -0
- package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
- package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +17 -0
- package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +112 -0
- package/dist/templates/next-app/src/uikit/{components → components-app}/LanguageSwitcher.tsx +15 -19
- package/dist/templates/next-app/src/uikit/{components → components-app}/ThemeSwitcher.tsx +53 -52
- package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +98 -0
- package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +93 -0
- package/dist/templates/next-app/src/uikit/context/IOCContext.ts +16 -4
- package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +32 -0
- package/dist/templates/next-app/tsconfig.json +3 -2
- package/dist/templates/react-app/tsconfig.app.json +1 -3
- package/dist/templates/react-app/tsconfig.node.json +0 -4
- package/dist/templates/react-app/tsconfig.test.json +1 -3
- package/package.json +1 -1
- package/dist/templates/next-app/src/server/PageParams.ts +0 -66
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +0 -80
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +0 -65
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +0 -58
- package/dist/templates/next-app/src/uikit/components/NextIntlProvider.tsx +0 -21
- /package/dist/templates/next-app/{src/app/[locale] → public}/favicon.ico +0 -0
- /package/dist/templates/next-app/src/uikit/{components → components-app}/LogoutButton.tsx +0 -0
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
localesSchema,
|
|
11
11
|
type LocalesSchema
|
|
12
12
|
} from '@migrations/schema/LocalesSchema';
|
|
13
|
-
import {
|
|
13
|
+
import { SupabaseBridge } from '../SupabaseBridge';
|
|
14
14
|
import type {
|
|
15
15
|
LocalesRepositoryInterface,
|
|
16
16
|
UpsertResult
|
|
@@ -23,7 +23,7 @@ export class LocalesRepository implements LocalesRepositoryInterface {
|
|
|
23
23
|
protected safeFields = Object.keys(localesSchema.shape);
|
|
24
24
|
|
|
25
25
|
constructor(
|
|
26
|
-
@inject(
|
|
26
|
+
@inject(SupabaseBridge) protected dbBridge: DBBridgeInterface,
|
|
27
27
|
@inject(Datetime) protected datetime: Datetime
|
|
28
28
|
) {}
|
|
29
29
|
|
|
@@ -70,6 +70,12 @@ export class LocalesRepository implements LocalesRepositoryInterface {
|
|
|
70
70
|
id: number,
|
|
71
71
|
params: Partial<Omit<LocalesSchema, 'id' | 'created_at'>>
|
|
72
72
|
): Promise<void> {
|
|
73
|
+
if (!id || typeof id !== 'number') {
|
|
74
|
+
throw new Error(
|
|
75
|
+
'ID is required and must be a number for updateById operation'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
const data = {
|
|
74
80
|
...params,
|
|
75
81
|
updated_at: this.datetime.timestampz()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ResourceStore } from '@qlover/corekit-bridge';
|
|
2
2
|
import { inject, injectable } from 'inversify';
|
|
3
|
+
import { ResourceState } from '@/base/cases/ResourceState';
|
|
4
|
+
import { AdminLocalesApi } from '@/base/services/adminApi/AdminLocalesApi';
|
|
3
5
|
import { ResourceService } from '@/base/services/ResourceService';
|
|
4
6
|
import type { LocalesSchema } from '@migrations/schema/LocalesSchema';
|
|
5
|
-
import { AdminLocalesApi } from './adminApi/AdminLocalesApi';
|
|
6
|
-
import { ResourceState } from '../cases/ResourceState';
|
|
7
7
|
|
|
8
8
|
@injectable()
|
|
9
9
|
export class AdminLocalesService extends ResourceService<
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
|
+
import { omit } from 'lodash';
|
|
2
3
|
import { revalidateTag } from 'next/cache';
|
|
3
4
|
import type { LocalesSchema } from '@migrations/schema/LocalesSchema';
|
|
4
5
|
import type { LocaleType } from '@config/i18n';
|
|
@@ -20,6 +21,12 @@ export type ImportLocalesData = {
|
|
|
20
21
|
};
|
|
21
22
|
};
|
|
22
23
|
|
|
24
|
+
export type GetLocalesParams = {
|
|
25
|
+
page: number;
|
|
26
|
+
pageSize: number;
|
|
27
|
+
orderBy?: BridgeOrderBy;
|
|
28
|
+
};
|
|
29
|
+
|
|
23
30
|
@injectable()
|
|
24
31
|
export class ApiLocaleService {
|
|
25
32
|
constructor(
|
|
@@ -45,11 +52,9 @@ export class ApiLocaleService {
|
|
|
45
52
|
);
|
|
46
53
|
}
|
|
47
54
|
|
|
48
|
-
async getLocales(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
orderBy?: BridgeOrderBy;
|
|
52
|
-
}): Promise<PaginationInterface<LocalesSchema>> {
|
|
55
|
+
async getLocales(
|
|
56
|
+
params: GetLocalesParams
|
|
57
|
+
): Promise<PaginationInterface<LocalesSchema>> {
|
|
53
58
|
return this.localesRepository.pagination({
|
|
54
59
|
page: params.page,
|
|
55
60
|
pageSize: params.pageSize,
|
|
@@ -58,14 +63,20 @@ export class ApiLocaleService {
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
async update(data: Partial<LocalesSchema>): Promise<void> {
|
|
66
|
+
if (!data.id || typeof data.id !== 'number') {
|
|
67
|
+
throw new Error(
|
|
68
|
+
'ID is required and must be a number for update operation'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
await this.localesRepository.updateById(
|
|
62
|
-
data.id
|
|
63
|
-
data
|
|
73
|
+
data.id,
|
|
74
|
+
omit(data, ['id', 'created_at'])
|
|
64
75
|
);
|
|
65
76
|
|
|
66
77
|
// 清除所有支持的语言的缓存
|
|
67
78
|
const revalidatePromises = i18nConfig.supportedLngs.map(async (locale) => {
|
|
68
|
-
await revalidateTag(`i18n-${locale}
|
|
79
|
+
await revalidateTag(`i18n-${locale}`, 'default');
|
|
69
80
|
});
|
|
70
81
|
await Promise.all(revalidatePromises);
|
|
71
82
|
}
|
|
@@ -75,7 +86,7 @@ export class ApiLocaleService {
|
|
|
75
86
|
|
|
76
87
|
// 清除所有支持的语言的缓存
|
|
77
88
|
const revalidatePromises = i18nConfig.supportedLngs.map(async (locale) => {
|
|
78
|
-
await revalidateTag(`i18n-${locale}
|
|
89
|
+
await revalidateTag(`i18n-${locale}`, 'default');
|
|
79
90
|
});
|
|
80
91
|
await Promise.all(revalidatePromises);
|
|
81
92
|
}
|
|
@@ -94,6 +105,10 @@ export class ApiLocaleService {
|
|
|
94
105
|
result[key]!['value'] = key;
|
|
95
106
|
result[key]![locale] = value;
|
|
96
107
|
|
|
108
|
+
if (locale === i18nConfig.fallbackLng) {
|
|
109
|
+
result[key]!['description'] = value;
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
const { namespace: namespace2 } = splitI18nKey(key);
|
|
98
113
|
result[key]!['namespace'] = namespace2 || namespace;
|
|
99
114
|
});
|
|
@@ -111,7 +126,7 @@ export class ApiLocaleService {
|
|
|
111
126
|
if (upsertResult.successCount > 0) {
|
|
112
127
|
const revalidatePromises = i18nConfig.supportedLngs.map(
|
|
113
128
|
async (locale) => {
|
|
114
|
-
await revalidateTag(`i18n-${locale}
|
|
129
|
+
await revalidateTag(`i18n-${locale}`, 'default');
|
|
115
130
|
}
|
|
116
131
|
);
|
|
117
132
|
await Promise.all(revalidatePromises);
|
|
@@ -2,7 +2,10 @@ import { inject, injectable } from 'inversify';
|
|
|
2
2
|
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
3
3
|
import type { UserSchema } from '@migrations/schema/UserSchema';
|
|
4
4
|
import { UserRepository } from '../repositorys/UserRepository';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type PaginationParams,
|
|
7
|
+
PaginationValidator
|
|
8
|
+
} from '../validators/PaginationValidator';
|
|
6
9
|
import type { UserRepositoryInterface } from '../port/UserRepositoryInterface';
|
|
7
10
|
import type { ValidatorInterface } from '../port/ValidatorInterface';
|
|
8
11
|
|
|
@@ -12,7 +15,7 @@ export class ApiUserService {
|
|
|
12
15
|
@inject(UserRepository)
|
|
13
16
|
protected userRepository: UserRepositoryInterface,
|
|
14
17
|
@inject(PaginationValidator)
|
|
15
|
-
protected paginationValidator: ValidatorInterface
|
|
18
|
+
protected paginationValidator: ValidatorInterface<PaginationParams>
|
|
16
19
|
) {}
|
|
17
20
|
|
|
18
21
|
async getUsers(params: {
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
import type { ExtendedExecutorError } from './ExtendedExecutorError';
|
|
12
12
|
import type { ImportLocalesData } from '../services/ApiLocaleService';
|
|
13
13
|
|
|
14
|
-
export class LocalesValidator implements ValidatorInterface
|
|
14
|
+
export class LocalesValidator implements ValidatorInterface<
|
|
15
|
+
Omit<LocalesSchema, 'id' | 'created_at' | 'updated_at'>
|
|
16
|
+
> {
|
|
15
17
|
validate(data: unknown): void | ValidationFaildResult {
|
|
16
18
|
if (typeof data !== 'object' || data === null) {
|
|
17
19
|
return {
|
|
@@ -44,7 +46,7 @@ export class LocalesValidator implements ValidatorInterface {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
export class LocalesImportValidator implements ValidatorInterface {
|
|
49
|
+
export class LocalesImportValidator implements ValidatorInterface<ImportLocalesData> {
|
|
48
50
|
getHasAnyFilesLocale(
|
|
49
51
|
values: FormData
|
|
50
52
|
): { language: LocaleType; value: FormDataEntryValue }[] {
|
|
@@ -26,7 +26,7 @@ const passwordSchema = z
|
|
|
26
26
|
.max(50, { message: V_PASSWORD_MAX_LENGTH })
|
|
27
27
|
.regex(/^\S+$/, { message: V_PASSWORD_SPECIAL_CHARS });
|
|
28
28
|
|
|
29
|
-
export class LoginValidator implements ValidatorInterface {
|
|
29
|
+
export class LoginValidator implements ValidatorInterface<LoginValidatorData> {
|
|
30
30
|
validateEmail(data: unknown): void | ValidationFaildResult {
|
|
31
31
|
const emailResult = emailSchema.safeParse(data);
|
|
32
32
|
if (!emailResult.success) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type { PaginationInterface } from '@/server/port/PaginationInterface';
|
|
3
2
|
import { API_PAGE_INVALID } from '@config/Identifier';
|
|
4
3
|
import {
|
|
5
4
|
type ValidationFaildResult,
|
|
@@ -22,7 +21,13 @@ const paginationSchema = z.object({
|
|
|
22
21
|
order: z.string().optional().default('0')
|
|
23
22
|
});
|
|
24
23
|
|
|
25
|
-
export
|
|
24
|
+
export type PaginationParams = {
|
|
25
|
+
page: number;
|
|
26
|
+
pageSize: number;
|
|
27
|
+
orders?: BridgeOrderBy;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class PaginationValidator implements ValidatorInterface<PaginationParams> {
|
|
26
31
|
protected defaultPageSize = 10;
|
|
27
32
|
|
|
28
33
|
validate(data: unknown): void | ValidationFaildResult {
|
|
@@ -39,12 +44,7 @@ export class PaginationValidator implements ValidatorInterface {
|
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
getThrow(data: unknown):
|
|
43
|
-
PaginationInterface<unknown>,
|
|
44
|
-
'page' | 'pageSize'
|
|
45
|
-
> & {
|
|
46
|
-
orders: BridgeOrderBy;
|
|
47
|
-
} {
|
|
47
|
+
getThrow<T>(data: unknown): T {
|
|
48
48
|
const result = paginationSchema.safeParse(data);
|
|
49
49
|
if (!result.success) {
|
|
50
50
|
throw new Error(result.error.issues[0].message);
|
|
@@ -58,7 +58,7 @@ export class PaginationValidator implements ValidatorInterface {
|
|
|
58
58
|
orders: [
|
|
59
59
|
result.data.orderBy || 'updated_at',
|
|
60
60
|
order == '0' || order == '1' ? (+order as 0 | 1) : 0
|
|
61
|
-
]
|
|
62
|
-
};
|
|
61
|
+
] as BridgeOrderBy
|
|
62
|
+
} as T;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -277,48 +277,4 @@ html,
|
|
|
277
277
|
--fe-layout-light-trigger-bg: rgb(var(--color-bg-base));
|
|
278
278
|
--fe-layout-light-trigger-color: rgb(var(--text-primary));
|
|
279
279
|
}
|
|
280
|
-
|
|
281
|
-
/* 滚动条样式变量 */
|
|
282
|
-
--fe-scrollbar-size: 6px;
|
|
283
|
-
--fe-scrollbar-thumb-bg: rgba(15 23 42 / 0.2);
|
|
284
|
-
--fe-scrollbar-thumb-hover-bg: rgba(15 23 42 / 0.3);
|
|
285
|
-
--fe-scrollbar-track-bg: transparent;
|
|
286
|
-
|
|
287
|
-
/* 滚动条样式 */
|
|
288
|
-
::-webkit-scrollbar {
|
|
289
|
-
width: var(--fe-scrollbar-size);
|
|
290
|
-
height: var(--fe-scrollbar-size);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
::-webkit-scrollbar-track {
|
|
294
|
-
background: var(--fe-scrollbar-track-bg);
|
|
295
|
-
border-radius: var(--fe-scrollbar-size);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
::-webkit-scrollbar-thumb {
|
|
299
|
-
background: var(--fe-scrollbar-thumb-bg);
|
|
300
|
-
border-radius: var(--fe-scrollbar-size);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
::-webkit-scrollbar-thumb:hover {
|
|
304
|
-
background: var(--fe-scrollbar-thumb-hover-bg);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
::-webkit-scrollbar-corner {
|
|
308
|
-
background: transparent;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/* 可选的细滚动条样式 */
|
|
312
|
-
.scrollbar-thin {
|
|
313
|
-
--fe-scrollbar-size: 4px;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/* 隐藏滚动条但保持可滚动 */
|
|
317
|
-
.scrollbar-hidden {
|
|
318
|
-
-ms-overflow-style: none;
|
|
319
|
-
scrollbar-width: none;
|
|
320
|
-
&::-webkit-scrollbar {
|
|
321
|
-
display: none;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
280
|
}
|
|
@@ -230,48 +230,4 @@
|
|
|
230
230
|
--fe-layout-light-trigger-bg: rgb(30 41 59);
|
|
231
231
|
--fe-layout-light-trigger-color: rgb(var(--text-primary));
|
|
232
232
|
}
|
|
233
|
-
|
|
234
|
-
/* 滚动条样式变量 */
|
|
235
|
-
--fe-scrollbar-size: 6px;
|
|
236
|
-
--fe-scrollbar-thumb-bg: rgba(255 255 255 / 0.2);
|
|
237
|
-
--fe-scrollbar-thumb-hover-bg: rgba(255 255 255 / 0.3);
|
|
238
|
-
--fe-scrollbar-track-bg: transparent;
|
|
239
|
-
|
|
240
|
-
/* 滚动条样式 */
|
|
241
|
-
::-webkit-scrollbar {
|
|
242
|
-
width: var(--fe-scrollbar-size);
|
|
243
|
-
height: var(--fe-scrollbar-size);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
::-webkit-scrollbar-track {
|
|
247
|
-
background: var(--fe-scrollbar-track-bg);
|
|
248
|
-
border-radius: var(--fe-scrollbar-size);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
::-webkit-scrollbar-thumb {
|
|
252
|
-
background: var(--fe-scrollbar-thumb-bg);
|
|
253
|
-
border-radius: var(--fe-scrollbar-size);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
::-webkit-scrollbar-thumb:hover {
|
|
257
|
-
background: var(--fe-scrollbar-thumb-hover-bg);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
::-webkit-scrollbar-corner {
|
|
261
|
-
background: transparent;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/* 可选的细滚动条样式 */
|
|
265
|
-
.scrollbar-thin {
|
|
266
|
-
--fe-scrollbar-size: 4px;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/* 隐藏滚动条但保持可滚动 */
|
|
270
|
-
.scrollbar-hidden {
|
|
271
|
-
-ms-overflow-style: none;
|
|
272
|
-
scrollbar-width: none;
|
|
273
|
-
&::-webkit-scrollbar {
|
|
274
|
-
display: none;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
233
|
}
|
|
@@ -243,48 +243,4 @@
|
|
|
243
243
|
--fe-layout-light-trigger-bg: var(--ant-color-bg-container);
|
|
244
244
|
--fe-layout-light-trigger-color: var(--ant-color-text);
|
|
245
245
|
}
|
|
246
|
-
|
|
247
|
-
/* 滚动条样式变量 */
|
|
248
|
-
--fe-scrollbar-size: 6px;
|
|
249
|
-
--fe-scrollbar-thumb-bg: rgba(244 114 182 / 0.3);
|
|
250
|
-
--fe-scrollbar-thumb-hover-bg: rgba(244 114 182 / 0.4);
|
|
251
|
-
--fe-scrollbar-track-bg: transparent;
|
|
252
|
-
|
|
253
|
-
/* 滚动条样式 */
|
|
254
|
-
::-webkit-scrollbar {
|
|
255
|
-
width: var(--fe-scrollbar-size);
|
|
256
|
-
height: var(--fe-scrollbar-size);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
::-webkit-scrollbar-track {
|
|
260
|
-
background: var(--fe-scrollbar-track-bg);
|
|
261
|
-
border-radius: var(--fe-scrollbar-size);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
::-webkit-scrollbar-thumb {
|
|
265
|
-
background: var(--fe-scrollbar-thumb-bg);
|
|
266
|
-
border-radius: var(--fe-scrollbar-size);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
::-webkit-scrollbar-thumb:hover {
|
|
270
|
-
background: var(--fe-scrollbar-thumb-hover-bg);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
::-webkit-scrollbar-corner {
|
|
274
|
-
background: transparent;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/* 可选的细滚动条样式 */
|
|
278
|
-
.scrollbar-thin {
|
|
279
|
-
--fe-scrollbar-size: 4px;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/* 隐藏滚动条但保持可滚动 */
|
|
283
|
-
.scrollbar-hidden {
|
|
284
|
-
-ms-overflow-style: none;
|
|
285
|
-
scrollbar-width: none;
|
|
286
|
-
&::-webkit-scrollbar {
|
|
287
|
-
display: none;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
246
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
html,
|
|
2
|
+
.fe-theme {
|
|
3
|
+
--scrollbar-track-color: var(--color-secondary);
|
|
4
|
+
--scrollbar-thumb-color: var(--color-text-secondary);
|
|
5
|
+
--scrollbar-thumb-hover-color: var(--color-brand-hover);
|
|
6
|
+
|
|
7
|
+
/* Webkit browsers (Chrome, Safari, Edge) - 全局样式,使用最高优先级 */
|
|
8
|
+
body *::-webkit-scrollbar,
|
|
9
|
+
*::-webkit-scrollbar {
|
|
10
|
+
width: 10px;
|
|
11
|
+
height: 10px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body *::-webkit-scrollbar-track,
|
|
15
|
+
*::-webkit-scrollbar-track {
|
|
16
|
+
background: var(--scrollbar-track-color);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body *::-webkit-scrollbar-thumb,
|
|
20
|
+
*::-webkit-scrollbar-thumb {
|
|
21
|
+
background-color: var(--scrollbar-thumb-color);
|
|
22
|
+
border-radius: 6px;
|
|
23
|
+
border: 1px solid var(--scrollbar-track-color);
|
|
24
|
+
transition: background-color 0.2s ease;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
body *::-webkit-scrollbar-thumb:hover {
|
|
28
|
+
background-color: var(--scrollbar-thumb-hover-color) !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
32
|
+
background-color: var(--scrollbar-thumb-hover-color) !important;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -13,12 +13,13 @@ import { clsx } from 'clsx';
|
|
|
13
13
|
import { usePathname } from 'next/navigation';
|
|
14
14
|
import React, { useMemo, type HTMLAttributes } from 'react';
|
|
15
15
|
import { AdminPageManager } from '@/base/cases/AdminPageManager';
|
|
16
|
-
import {
|
|
17
|
-
import { LanguageSwitcher } from './LanguageSwitcher';
|
|
16
|
+
import { COMMON_ADMIN_TITLE } from '@config/Identifier';
|
|
18
17
|
import { LocaleLink } from './LocaleLink';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
18
|
+
import { LanguageSwitcher } from '../components-app/LanguageSwitcher';
|
|
19
|
+
import { LogoutButton } from '../components-app/LogoutButton';
|
|
20
|
+
import { ThemeSwitcher } from '../components-app/ThemeSwitcher';
|
|
21
21
|
import { useIOC } from '../hook/useIOC';
|
|
22
|
+
import { useWarnTranslations } from '../hook/useWarnTranslations';
|
|
22
23
|
import type { ItemType } from 'antd/es/menu/interface';
|
|
23
24
|
|
|
24
25
|
const { Sider } = Layout;
|
|
@@ -41,10 +42,13 @@ export function AdminLayout(props: AdminLayoutProps) {
|
|
|
41
42
|
const page = useIOC(AdminPageManager);
|
|
42
43
|
const collapsedSidebar = useStore(page, page.selectors.collapsedSidebar);
|
|
43
44
|
const navItems = useStore(page, page.selectors.navItems);
|
|
45
|
+
const t = useWarnTranslations();
|
|
46
|
+
|
|
47
|
+
const title = useMemo(() => t(COMMON_ADMIN_TITLE), [t]);
|
|
44
48
|
|
|
45
49
|
const selectedKey = useMemo(() => {
|
|
46
50
|
// 移除语言前缀,例如 /en/admin/users -> /admin/users
|
|
47
|
-
const normalizedPath = pathname
|
|
51
|
+
const normalizedPath = pathname?.replace(/^\/[^/]+/, '') ?? '';
|
|
48
52
|
return navItems.find((item) => item.pathname === normalizedPath)?.key || '';
|
|
49
53
|
}, [pathname, navItems]);
|
|
50
54
|
|
|
@@ -81,7 +85,7 @@ export function AdminLayout(props: AdminLayoutProps) {
|
|
|
81
85
|
|
|
82
86
|
return (
|
|
83
87
|
<Layout data-testid="AdminLayout" className={className} {...rest}>
|
|
84
|
-
<div className="overflow-y-auto overflow-x-hidden h-screen sticky top-0 bottom-0
|
|
88
|
+
<div className="overflow-y-auto overflow-x-hidden h-screen sticky top-0 bottom-0">
|
|
85
89
|
<Sider
|
|
86
90
|
className="h-full relative"
|
|
87
91
|
onCollapse={() => page.toggleSidebar()}
|
|
@@ -111,11 +115,30 @@ export function AdminLayout(props: AdminLayoutProps) {
|
|
|
111
115
|
</div>
|
|
112
116
|
|
|
113
117
|
<Layout>
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
className="
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
<header
|
|
119
|
+
data-testid="AdminLayoutHeader"
|
|
120
|
+
className="h-14 bg-secondary border-b border-c-border sticky top-0 z-50"
|
|
121
|
+
>
|
|
122
|
+
<div
|
|
123
|
+
className={clsx(
|
|
124
|
+
'flex items-center justify-between h-full px-4 mx-auto max-w-7xl',
|
|
125
|
+
className
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
<div className="flex items-center">
|
|
129
|
+
<LocaleLink
|
|
130
|
+
href="/admin"
|
|
131
|
+
title={title}
|
|
132
|
+
className="flex items-center hover:opacity-80 transition-opacity"
|
|
133
|
+
>
|
|
134
|
+
<span className="text-lg font-semibold text-text">{title}</span>
|
|
135
|
+
</LocaleLink>
|
|
136
|
+
</div>
|
|
137
|
+
<div className="flex items-center gap-2 md:gap-4">
|
|
138
|
+
{rightActions}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</header>
|
|
119
142
|
<main
|
|
120
143
|
className={clsx('p-2 bg-primary text-text flex-1', mainClassName)}
|
|
121
144
|
>
|
|
@@ -1,45 +1,75 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import '@ant-design/v5-patch-for-react-19';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import { useEffect } from 'react';
|
|
6
|
-
import { NavigateBridge } from '@/base/cases/NavigateBridge';
|
|
7
|
-
import type { I18nServiceLocale } from '@/base/port/I18nServiceInterface';
|
|
3
|
+
|
|
4
|
+
import { useState } from 'react';
|
|
8
5
|
import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { IOCContext } from '../context/IOCContext';
|
|
12
|
-
import { useWarnTranslations } from '../hook/useWarnTranslations';
|
|
6
|
+
import { useIOC } from '../hook/useIOC';
|
|
7
|
+
import { useStrictEffect } from '../hook/useStrictEffect';
|
|
13
8
|
|
|
14
9
|
export function BootstrapsProvider(props: { children: React.ReactNode }) {
|
|
15
|
-
const IOC =
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
10
|
+
const IOC = useIOC();
|
|
11
|
+
|
|
12
|
+
const [, setIocMounted] = useState(false);
|
|
13
|
+
|
|
14
|
+
useStrictEffect(() => {
|
|
15
|
+
// clientIOC.register({
|
|
16
|
+
// appConfig: appConfig
|
|
17
|
+
// });
|
|
18
|
+
BootstrapClient.main({
|
|
19
|
+
root: window,
|
|
20
|
+
pathname: window.location.pathname,
|
|
21
|
+
IOC: IOC
|
|
22
|
+
}).then(() => {
|
|
23
|
+
setIocMounted(true);
|
|
24
|
+
});
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
// useEffect(() => {
|
|
28
|
+
// const register = new ClientIOCRegister({
|
|
29
|
+
// appConfig: appConfig
|
|
30
|
+
// });
|
|
31
|
+
|
|
32
|
+
// // 启动前注册所有依赖
|
|
33
|
+
// // clientIOC.register(register);
|
|
34
|
+
|
|
35
|
+
// BootstrapClient.main({
|
|
36
|
+
// root: window,
|
|
37
|
+
// pathname: window.location.pathname,
|
|
38
|
+
// IOC: IOC,
|
|
39
|
+
// register: register
|
|
40
|
+
// }).then(() => {
|
|
41
|
+
// setIocMounted(true);
|
|
42
|
+
// });
|
|
43
|
+
// }, []);
|
|
44
|
+
|
|
45
|
+
// if (!register) {
|
|
46
|
+
// return <div data-testid="BootstrapsProviderLoading">Loading...</div>;
|
|
47
|
+
// }
|
|
48
|
+
|
|
49
|
+
// const IOC = clientIOC.create();
|
|
50
|
+
// const locale = useLocale();
|
|
51
|
+
// const router = useRouter();
|
|
52
|
+
// const t = useWarnTranslations();
|
|
53
|
+
|
|
54
|
+
// useEffect(() => {
|
|
55
|
+
// IOC(I.RouterServiceInterface).setLocale(locale);
|
|
56
|
+
// IOC(NavigateBridge).setUIBridge(router);
|
|
57
|
+
// }, [locale, router, IOC]);
|
|
58
|
+
|
|
59
|
+
// useEffect(() => {
|
|
60
|
+
// IOC(I.I18nServiceInterface).changeLanguage(locale as I18nServiceLocale);
|
|
61
|
+
// IOC(I.I18nServiceInterface).setTranslator(t);
|
|
62
|
+
// }, [t, IOC, locale]);
|
|
63
|
+
|
|
64
|
+
// useEffect(() => {
|
|
65
|
+
// if (typeof window !== 'undefined') {
|
|
66
|
+
// BootstrapClient.main({
|
|
67
|
+
// root: window,
|
|
68
|
+
// pathname: window.location.pathname,
|
|
69
|
+
// IOC: IOC
|
|
70
|
+
// });
|
|
71
|
+
// }
|
|
72
|
+
// }, [IOC]);
|
|
73
|
+
|
|
74
|
+
return props.children;
|
|
45
75
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import '@ant-design/v5-patch-for-react-19';
|
|
3
|
+
import { AntdRegistry } from '@ant-design/nextjs-registry';
|
|
4
|
+
import { AntdThemeProvider } from '@brain-toolkit/antd-theme-override/react';
|
|
5
|
+
import { ThemeProvider } from 'next-themes';
|
|
6
|
+
import { I } from '@config/IOCIdentifier';
|
|
7
|
+
import type { CommonThemeConfig } from '@config/theme';
|
|
8
|
+
import { useIOC } from '../hook/useIOC';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ClientRootProvider is a provider for the client components
|
|
12
|
+
*
|
|
13
|
+
* - ThemeProvider
|
|
14
|
+
* - AntdProvider
|
|
15
|
+
*
|
|
16
|
+
* TODO: 存在问题:
|
|
17
|
+
*
|
|
18
|
+
* 1. antd 样式存在闪烁问题, 目前没有解决, 可能是因为 cssinjs 的技术性问题
|
|
19
|
+
*
|
|
20
|
+
* 目前能将完美解决的就是完全使用客户端渲染,也就是引入 useMountedClient 当客户端渲染时才渲染, 这样就不会出现闪烁问题
|
|
21
|
+
* 但是他会导致国际化切换闪烁问题
|
|
22
|
+
*
|
|
23
|
+
* 可能需要等待 antd 官方解决这个问题
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
*
|
|
27
|
+
* ```tsx
|
|
28
|
+
* const mounted = useMountedClient();
|
|
29
|
+
*
|
|
30
|
+
* return mounted && children;
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
*
|
|
34
|
+
* @param themeConfig - The theme config
|
|
35
|
+
* @param children - The children components
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
export function ClientRootProvider(props: {
|
|
39
|
+
themeConfig: CommonThemeConfig;
|
|
40
|
+
children: React.ReactNode;
|
|
41
|
+
}) {
|
|
42
|
+
const { themeConfig, children } = props;
|
|
43
|
+
|
|
44
|
+
const IOC = useIOC();
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<AntdThemeProvider
|
|
48
|
+
data-testid="ComboProvider"
|
|
49
|
+
theme={themeConfig.antdTheme}
|
|
50
|
+
staticApi={IOC(I.DialogHandler)}
|
|
51
|
+
>
|
|
52
|
+
<ThemeProvider
|
|
53
|
+
themes={themeConfig.supportedThemes as unknown as string[]}
|
|
54
|
+
attribute={themeConfig.domAttribute}
|
|
55
|
+
defaultTheme={themeConfig.defaultTheme}
|
|
56
|
+
enableSystem={themeConfig.enableSystem}
|
|
57
|
+
enableColorScheme={false}
|
|
58
|
+
storageKey={themeConfig.storageKey}
|
|
59
|
+
>
|
|
60
|
+
<AntdRegistry>{children}</AntdRegistry>
|
|
61
|
+
</ThemeProvider>
|
|
62
|
+
</AntdThemeProvider>
|
|
63
|
+
);
|
|
64
|
+
}
|