generator-mico-cli 0.2.29 → 0.2.30
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/README.md +2 -0
- package/generators/micro-react/index.js +17 -1
- package/generators/micro-react/templates/CICD/start_dev.sh +11 -0
- package/generators/micro-react/templates/CICD/start_local.sh +9 -0
- package/generators/micro-react/templates/CICD/start_prod.sh +13 -0
- package/generators/micro-react/templates/CICD/start_test.sh +11 -0
- package/generators/micro-react/templates/apps/layout/config/config.dev.ts +9 -3
- package/generators/micro-react/templates/apps/layout/config/config.prod.development.ts +10 -0
- package/generators/micro-react/templates/apps/layout/config/config.prod.testing.ts +10 -0
- package/generators/micro-react/templates/apps/layout/config/config.prod.ts +12 -0
- package/generators/micro-react/templates/apps/layout/docs/feature-/345/233/275/351/231/205/345/214/226.md +121 -0
- package/generators/micro-react/templates/apps/layout/docs/feature-/345/276/256/345/211/215/347/253/257/346/250/241/345/274/217.md +8 -0
- package/generators/micro-react/templates/apps/layout/mock/menus.ts +14 -0
- package/generators/micro-react/templates/apps/layout/mock/pages.ts +22 -2
- package/generators/micro-react/templates/apps/layout/package.json +3 -2
- package/generators/micro-react/templates/apps/layout/src/app.tsx +58 -5
- package/generators/micro-react/templates/apps/layout/src/common/auth/index.ts +3 -0
- package/generators/micro-react/templates/apps/layout/src/common/auth/tenant.ts +25 -0
- package/generators/micro-react/templates/apps/layout/src/common/auth/type.ts +28 -2
- package/generators/micro-react/templates/apps/layout/src/common/intl/formatLayoutMessage.ts +30 -0
- package/generators/micro-react/templates/apps/layout/src/common/intl/index.ts +6 -0
- package/generators/micro-react/templates/apps/layout/src/common/intl/intlRuntime.ts +14 -0
- package/generators/micro-react/templates/apps/layout/src/common/intl/localeMapping.ts +30 -0
- package/generators/micro-react/templates/apps/layout/src/common/intl/types.ts +14 -0
- package/generators/micro-react/templates/apps/layout/src/common/intl/useLayoutIntl.ts +40 -0
- package/generators/micro-react/templates/apps/layout/src/common/logger.ts +3 -4
- package/generators/micro-react/templates/apps/layout/src/common/menu/types.ts +27 -0
- package/generators/micro-react/templates/apps/layout/src/common/micro/types.ts +23 -0
- package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +74 -15
- package/generators/micro-react/templates/apps/layout/src/common/request/token-refresh.ts +2 -0
- package/generators/micro-react/templates/apps/layout/src/components/MicroAppLoader/index.tsx +28 -6
- package/generators/micro-react/templates/apps/layout/src/components/RightContent/TenantDropdown.tsx +76 -0
- package/generators/micro-react/templates/apps/layout/src/components/RightContent/index.ts +1 -0
- package/generators/micro-react/templates/apps/layout/src/components/RightContent/tenant-dropdown.less +48 -0
- package/generators/micro-react/templates/apps/layout/src/constants/index.ts +1 -0
- package/generators/micro-react/templates/apps/layout/src/hooks/index.ts +1 -0
- package/generators/micro-react/templates/apps/layout/src/hooks/useMenuState.ts +18 -0
- package/generators/micro-react/templates/apps/layout/src/hooks/useTenant.ts +41 -0
- package/generators/micro-react/templates/apps/layout/src/layouts/components/header/index.tsx +4 -1
- package/generators/micro-react/templates/apps/layout/src/layouts/components/menu/index.tsx +18 -6
- package/generators/micro-react/templates/apps/layout/src/locales/en-US.ts +11 -0
- package/generators/micro-react/templates/apps/layout/src/locales/zh-CN.ts +10 -0
- package/generators/micro-react/templates/apps/layout/src/pages/Home/index.less +27 -0
- package/generators/micro-react/templates/apps/layout/src/pages/Home/index.tsx +108 -12
- package/generators/micro-react/templates/apps/layout/src/requestErrorConfig.ts +2 -1
- package/generators/micro-react/templates/apps/layout/src/services/user.ts +53 -2
- package/generators/micro-react/templates/apps/layout/typings.d.ts +16 -0
- package/generators/micro-react/templates/docs/package-shared.md +189 -0
- package/generators/micro-react/templates/package.json +1 -1
- package/generators/micro-react/templates/packages/common-intl/README.md +3 -1
- package/generators/micro-react/templates/packages/common-intl/package.json +1 -1
- package/generators/micro-react/templates/packages/common-intl/src/index.ts +4 -2
- package/generators/micro-react/templates/packages/common-intl/src/intl.ts +104 -8
- package/generators/micro-react/templates/packages/common-intl/src/umiLocaleBridge.ts +101 -0
- package/generators/micro-react/templates/packages/shared/README.md +120 -0
- package/generators/micro-react/templates/packages/shared/package.json +26 -0
- package/generators/micro-react/templates/packages/shared/services/common/index.ts +43 -0
- package/generators/micro-react/templates/packages/shared/services/index.ts +21 -0
- package/generators/micro-react/templates/packages/shared/services/request.ts +43 -0
- package/generators/micro-react/templates/packages/shared/timezone/index.ts +228 -0
- package/generators/micro-react/templates/packages/shared/tsconfig.json +20 -0
- package/generators/micro-react/templates/scripts/apply-sentry-plugin.ts +6 -1
- package/generators/micro-react/templates/turbo.json +9 -1
- package/generators/subapp-react/templates/homepage/config/config.ts +10 -0
- package/generators/subapp-react/templates/homepage/docs/feature-/345/233/275/351/231/205/345/214/226.md +124 -0
- package/generators/subapp-react/templates/homepage/package.json +2 -1
- package/generators/subapp-react/templates/homepage/src/app.tsx +100 -5
- package/generators/subapp-react/templates/homepage/src/common/intl/index.ts +15 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/intlRuntime.ts +14 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/localeMapping.ts +24 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/subappIntlConfig.ts +28 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/subappLocale.ts +18 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/subappOwnIntl.ts +63 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/types.ts +14 -0
- package/generators/subapp-react/templates/homepage/src/common/intl/useSubappIntl.ts +61 -0
- package/generators/subapp-react/templates/homepage/src/common/locale.ts +80 -0
- package/generators/subapp-react/templates/homepage/src/common/mainApp.ts +2 -0
- package/generators/subapp-react/templates/homepage/src/locales/en-US.ts +6 -0
- package/generators/subapp-react/templates/homepage/src/locales/zh-CN.ts +6 -0
- package/generators/subapp-react/templates/homepage/src/pages/index.less +10 -0
- package/generators/subapp-react/templates/homepage/src/pages/index.tsx +51 -0
- package/generators/subapp-react/templates/homepage/typings.d.ts +12 -0
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
// 上游全量导出(含 initIntl、addIntl 等);子应用需与主应用不同 tag 时可再次 initIntl,见 README「不同 tag」
|
|
1
2
|
export * from '@common-web/common-intl';
|
|
2
3
|
|
|
3
|
-
//
|
|
4
|
-
export {
|
|
4
|
+
// 默认 intl、fetchMultilingualData、i18n(均绑定 intl.ts 中的默认 tag);主/子应用入口可 configureLocale
|
|
5
|
+
export type { LocaleBridge } from './intl';
|
|
6
|
+
export { configureLocale, default as intl, fetchMultilingualData, i18n } from './intl';
|
|
@@ -1,14 +1,88 @@
|
|
|
1
|
-
import { initIntl, addIntl } from '@common-web/common-intl';
|
|
1
|
+
import { initIntl, addIntl, type ILang } from '@common-web/common-intl';
|
|
2
|
+
import {
|
|
3
|
+
getCurrentLocaleForIntl,
|
|
4
|
+
ilangToUmiLocale,
|
|
5
|
+
setLocaleToStorageForIntl,
|
|
6
|
+
umiLocaleToILang,
|
|
7
|
+
} from './umiLocaleBridge';
|
|
2
8
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
/** 与 layout 注入的 __MICO_CONFIG__.intl / commonIntl 对齐(本包不依赖 layout 全局类型) */
|
|
10
|
+
type MicoIntlSlice = {
|
|
11
|
+
tag?: string;
|
|
12
|
+
app_name?: string;
|
|
13
|
+
indexedDBParams?: {
|
|
14
|
+
dbName?: string;
|
|
15
|
+
dbVersion?: number;
|
|
16
|
+
storeName?: string;
|
|
17
|
+
keyPathKey?: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** 构建期默认;运行时由 window.__MICO_CONFIG__.intl(或 commonIntl)覆盖 */
|
|
22
|
+
const DEFAULT_INTL_OPTIONS = {
|
|
6
23
|
tag: '<%= intlTag %>',
|
|
7
24
|
app_name: 'middle', // 多语言中台的应用名。这个一般不需要改,应该就是 middle
|
|
8
25
|
indexedDBParams: { dbName: 'mico_i18n_db' },
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
function readMicoIntlOverride(): MicoIntlSlice | undefined {
|
|
29
|
+
if (typeof window === 'undefined') return undefined;
|
|
30
|
+
const w = (
|
|
31
|
+
window as Window & {
|
|
32
|
+
__MICO_CONFIG__?: { intl?: MicoIntlSlice; commonIntl?: MicoIntlSlice };
|
|
33
|
+
}
|
|
34
|
+
).__MICO_CONFIG__;
|
|
35
|
+
return w?.intl ?? w?.commonIntl;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getInitIntlOptions() {
|
|
39
|
+
const fromConfig = readMicoIntlOverride();
|
|
40
|
+
return {
|
|
41
|
+
...DEFAULT_INTL_OPTIONS,
|
|
42
|
+
...fromConfig,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* common-intl 侧语言读写(ILang)与 Umi 存储(zh-CN / en-US)的桥接。
|
|
48
|
+
* 主应用与子应用均应在入口最早调用 {@link configureLocale}(layout `app.tsx`、子应用 `app.tsx`)。
|
|
49
|
+
*/
|
|
50
|
+
export type LocaleBridge = {
|
|
51
|
+
getLocale: () => ILang;
|
|
52
|
+
setLocale: (lang: ILang) => void;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function createDefaultLocaleBridge(): LocaleBridge {
|
|
56
|
+
return {
|
|
57
|
+
getLocale: () => umiLocaleToILang(getCurrentLocaleForIntl()),
|
|
58
|
+
setLocale: (lang) => {
|
|
59
|
+
setLocaleToStorageForIntl(ilangToUmiLocale(lang));
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let localeBridge: LocaleBridge = createDefaultLocaleBridge();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 覆盖 initIntl 使用的 getLocale / setLocale,完成 Umi locale ↔ ILang 转换。
|
|
68
|
+
* - 主应用:与 `locale.getLocale`、`umi_locale` 一致(见 layout `app.tsx`)。
|
|
69
|
+
* - 子应用:与主应用注入的 `props.locale` 及本地 `umi_locale` 一致(见 subapp `app.tsx`、`getSubappCurrentLocale`)。
|
|
70
|
+
*/
|
|
71
|
+
export function configureLocale(patch: Partial<LocaleBridge>): void {
|
|
72
|
+
localeBridge = {
|
|
73
|
+
...localeBridge,
|
|
74
|
+
...patch,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 在模块加载时即完成 tag 绑定(读取 head 注入的 __MICO_CONFIG__)
|
|
79
|
+
const { fetchMultilingualData, i18n } = initIntl({
|
|
80
|
+
...getInitIntlOptions(),
|
|
81
|
+
getLocale: () => localeBridge.getLocale(),
|
|
82
|
+
setLocale: (lang: ILang) => localeBridge.setLocale(lang),
|
|
9
83
|
});
|
|
10
84
|
|
|
11
|
-
export { fetchMultilingualData, i18n };
|
|
85
|
+
export { fetchMultilingualData, i18n, initIntl };
|
|
12
86
|
|
|
13
87
|
/**
|
|
14
88
|
* 国际化文案汇总
|
|
@@ -17,9 +91,9 @@ export { fetchMultilingualData, i18n };
|
|
|
17
91
|
* import { intl } from '<%= packageScope %>/common-intl';
|
|
18
92
|
* intl.common_hello()
|
|
19
93
|
*
|
|
20
|
-
* 子应用默认使用本文件的 intl
|
|
21
|
-
*
|
|
22
|
-
*
|
|
94
|
+
* 子应用默认使用本文件的 intl 对象(与主应用共享同一默认 tag)。
|
|
95
|
+
* 若需与主应用不同 tag:上游支持多 tag,可在子应用内再次 initIntl(推荐从本包 import initIntl,
|
|
96
|
+
* 见 index.ts 对 @common-web/common-intl 的 re-export);亦可直接依赖 @common-web/common-intl。
|
|
23
97
|
* 详见 @common-web/common-intl README:
|
|
24
98
|
* https://gitlab-vywrajy.micoworld.net/micocenter/mico-portal/portal-center/common-web/-/blob/release/0.0.9/packages/common-intl/README.md
|
|
25
99
|
*/
|
|
@@ -36,6 +110,28 @@ const intl = addIntl({
|
|
|
36
110
|
interpolations: [name],
|
|
37
111
|
defaultMessage: `欢迎, ${name}`,
|
|
38
112
|
}),
|
|
113
|
+
|
|
114
|
+
// ============ SSO 认证失败弹框 ============
|
|
115
|
+
sso_auth_failure_modal_title: () =>
|
|
116
|
+
i18n({
|
|
117
|
+
key: 'sso_auth_failure_modal_title',
|
|
118
|
+
defaultMessage: '登录提示',
|
|
119
|
+
}),
|
|
120
|
+
sso_auth_failure_modal_content: () =>
|
|
121
|
+
i18n({
|
|
122
|
+
key: 'sso_auth_failure_modal_content',
|
|
123
|
+
defaultMessage: '自动登录失败,是否重新尝试登录?',
|
|
124
|
+
}),
|
|
125
|
+
sso_auth_failure_modal_ok: () =>
|
|
126
|
+
i18n({
|
|
127
|
+
key: 'sso_auth_failure_modal_ok',
|
|
128
|
+
defaultMessage: '重新登录',
|
|
129
|
+
}),
|
|
130
|
+
sso_auth_failure_modal_cancel: () =>
|
|
131
|
+
i18n({
|
|
132
|
+
key: 'sso_auth_failure_modal_cancel',
|
|
133
|
+
defaultMessage: '取消',
|
|
134
|
+
}),
|
|
39
135
|
}, i18n);
|
|
40
136
|
|
|
41
137
|
export default intl;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Umi locale ↔ ILang 桥接,供 initIntl 的 getLocale / setLocale 使用。
|
|
3
|
+
* 逻辑须与 apps/layout/src/common/locale.ts、common/intl/localeMapping.ts 保持一致(复制自 layout 模板)。
|
|
4
|
+
*/
|
|
5
|
+
import { LANG, type ILang } from '@common-web/common-intl';
|
|
6
|
+
|
|
7
|
+
const LOCALE_STORAGE_KEY = 'umi_locale';
|
|
8
|
+
const URL_PARAM_KEY = 'lang';
|
|
9
|
+
|
|
10
|
+
const SUPPORTED_LOCALES = ['zh-CN', 'en-US'] as const;
|
|
11
|
+
type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
12
|
+
|
|
13
|
+
const LOCALE = {
|
|
14
|
+
ZH_CN: 'zh-CN',
|
|
15
|
+
EN_US: 'en-US',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
const DEFAULT_LOCALE: SupportedLocale = 'en-US';
|
|
19
|
+
|
|
20
|
+
function getLocaleFromUrl(): string | null {
|
|
21
|
+
if (typeof window === 'undefined') {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
25
|
+
return urlParams.get(URL_PARAM_KEY);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getLocaleFromStorage(): string | null {
|
|
29
|
+
if (typeof window === 'undefined') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return localStorage.getItem(LOCALE_STORAGE_KEY);
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getBrowserLocale(): SupportedLocale {
|
|
40
|
+
if (typeof window === 'undefined') {
|
|
41
|
+
return DEFAULT_LOCALE;
|
|
42
|
+
}
|
|
43
|
+
const browserLang = navigator.language || (navigator as { userLanguage?: string }).userLanguage;
|
|
44
|
+
const locales = [...SUPPORTED_LOCALES];
|
|
45
|
+
if (locales.includes(browserLang as SupportedLocale)) {
|
|
46
|
+
return browserLang as SupportedLocale;
|
|
47
|
+
}
|
|
48
|
+
const langPrefix = browserLang.split('-')[0];
|
|
49
|
+
const matchedLocale = locales.find((locale) => locale.startsWith(langPrefix));
|
|
50
|
+
return matchedLocale || DEFAULT_LOCALE;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isValidLocale(locale: string | null): locale is SupportedLocale {
|
|
54
|
+
if (!locale) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return ([...SUPPORTED_LOCALES] as string[]).includes(locale);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getCurrentLocaleForIntl(): SupportedLocale {
|
|
61
|
+
const urlLocale = getLocaleFromUrl();
|
|
62
|
+
if (isValidLocale(urlLocale)) {
|
|
63
|
+
return urlLocale;
|
|
64
|
+
}
|
|
65
|
+
const storageLocale = getLocaleFromStorage();
|
|
66
|
+
if (isValidLocale(storageLocale)) {
|
|
67
|
+
return storageLocale;
|
|
68
|
+
}
|
|
69
|
+
return getBrowserLocale();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function setLocaleToStorageForIntl(locale: SupportedLocale): void {
|
|
73
|
+
if (typeof window === 'undefined') {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
localStorage.setItem(LOCALE_STORAGE_KEY, locale);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Failed to save locale to localStorage:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const UMI_TO_ILANG: Record<SupportedLocale, ILang> = {
|
|
84
|
+
[LOCALE.ZH_CN]: LANG.ZH_CN,
|
|
85
|
+
[LOCALE.EN_US]: LANG.EN,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export function umiLocaleToILang(locale: SupportedLocale): ILang {
|
|
89
|
+
return UMI_TO_ILANG[locale] ?? LANG.EN;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ILANG_TO_UMI: Partial<Record<ILang, SupportedLocale>> = {
|
|
93
|
+
[LANG.ZH_CN]: LOCALE.ZH_CN,
|
|
94
|
+
[LANG.EN]: LOCALE.EN_US,
|
|
95
|
+
[LANG.AR]: LOCALE.EN_US,
|
|
96
|
+
[LANG.TR]: LOCALE.EN_US,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export function ilangToUmiLocale(lang: ILang): SupportedLocale {
|
|
100
|
+
return ILANG_TO_UMI[lang] ?? LOCALE.EN_US;
|
|
101
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# shared
|
|
2
|
+
|
|
3
|
+
平台共享业务服务层骨架。当前提供两个对外模块:
|
|
4
|
+
|
|
5
|
+
- `./services`
|
|
6
|
+
- `configureRequest` / `getRequest`:统一 request 注入器
|
|
7
|
+
- `common`:通用类型(分页、响应包装)与工具函数(`isSuccess`)
|
|
8
|
+
- `./timezone`
|
|
9
|
+
- `TIMEZONE` / `TIMEZONE_HASH` / `TTimezone`:UTC 与 IANA 时区映射
|
|
10
|
+
- `utcOffsetToTimezone` / `utcOffsetToTimeZoneStr`:纯转换工具
|
|
11
|
+
- `configureTimezone({ getTimezone })`:注入「当前 IANA 时区」获取器
|
|
12
|
+
- `parseDateInTimezone` / `getShortcutDateRange` / `formatTimestampToDateTime` / `formatTimestampToDateTimeWithTimezone`:当前时区下的解析与格式化
|
|
13
|
+
|
|
14
|
+
后续共享模块(业务服务、常量、工具函数等)由各项目按需新增,本包只提供骨架与扩展约定。
|
|
15
|
+
|
|
16
|
+
## 目录约定
|
|
17
|
+
|
|
18
|
+
- 每个共享模块 = `services/` 同级的一个独立目录(例如 `services/`、`constants/`、`utils/`)
|
|
19
|
+
- 每个目录有且只有一个对外入口 `index.ts`
|
|
20
|
+
- `package.json` 的 `exports` 中**每个目录对应且仅对应一条**入口(如 `./constants → ./constants/index.ts`)
|
|
21
|
+
- 禁止暴露目录内部任何深层路径(例如 `./services/user`、`./services/common` 等都不允许)
|
|
22
|
+
|
|
23
|
+
## 初始化
|
|
24
|
+
|
|
25
|
+
在应用入口注入一次 request 实例:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { configureRequest } from '<%= packageScope %>/shared/services';
|
|
29
|
+
import { request } from '@/common/request';
|
|
30
|
+
|
|
31
|
+
// 主应用:在 app.tsx 顶层
|
|
32
|
+
configureRequest(request);
|
|
33
|
+
|
|
34
|
+
// 子应用(qiankun):在收到 mount props 后
|
|
35
|
+
configureRequest(props.request);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 使用通用类型
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { isSuccess, type IPagedResponse } from '<%= packageScope %>/shared/services';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 使用时区工具
|
|
45
|
+
|
|
46
|
+
`./timezone` 不内置时区存储,本包通过 `configureTimezone` 接收主应用的「当前 IANA 时区」获取器:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// apps/layout/src/app.tsx
|
|
50
|
+
import { configureTimezone } from '<%= packageScope %>/shared/timezone';
|
|
51
|
+
import { getTimezone } from '@/common/helpers';
|
|
52
|
+
|
|
53
|
+
configureTimezone({ getTimezone });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
随后业务代码可直接调用:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import {
|
|
60
|
+
parseDateInTimezone,
|
|
61
|
+
getShortcutDateRange,
|
|
62
|
+
formatTimestampToDateTime,
|
|
63
|
+
formatTimestampToDateTimeWithTimezone,
|
|
64
|
+
TIMEZONE,
|
|
65
|
+
} from '<%= packageScope %>/shared/timezone';
|
|
66
|
+
|
|
67
|
+
parseDateInTimezone('2025-11-04 16:20:21'); // Dayjs(按当前业务时区解析)
|
|
68
|
+
getShortcutDateRange('week'); // [周一 00:00, 周日 23:59]
|
|
69
|
+
formatTimestampToDateTime(Date.now()); // '2025.11.04 16:20:21'
|
|
70
|
+
formatTimestampToDateTimeWithTimezone(Date.now(), TIMEZONE['UTC+8']);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> 依赖前置:本模块依赖 `dayjs` + `utc / timezone / customParseFormat` 插件(声明为 `peerDependencies`,由消费方提供)。请在应用入口预先调用:
|
|
74
|
+
>
|
|
75
|
+
> ```ts
|
|
76
|
+
> import dayjs from 'dayjs';
|
|
77
|
+
> import utc from 'dayjs/plugin/utc';
|
|
78
|
+
> import timezone from 'dayjs/plugin/timezone';
|
|
79
|
+
> import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
80
|
+
> dayjs.extend(utc);
|
|
81
|
+
> dayjs.extend(timezone);
|
|
82
|
+
> dayjs.extend(customParseFormat);
|
|
83
|
+
> ```
|
|
84
|
+
>
|
|
85
|
+
> 模板的 `apps/layout/src/common/helpers.ts` 已扩展 `utc / timezone`,仅需补 `customParseFormat`。
|
|
86
|
+
|
|
87
|
+
## 在 services 内新增业务子模块
|
|
88
|
+
|
|
89
|
+
`services/` 是后端业务接口的聚合包,新增业务接口时在其内部组织子目录、统一从 `services/index.ts` 聚合出口:
|
|
90
|
+
|
|
91
|
+
1. 在 `services/` 下新建子目录,例如 `services/user/`
|
|
92
|
+
2. 在该目录下创建 `index.ts`(如有需要可再拆 `type.ts`,但仅由 `index.ts` re-export)
|
|
93
|
+
3. 在 `services/user/index.ts` 中:
|
|
94
|
+
- 用 `export type {...}` 转发对外类型
|
|
95
|
+
- 用 `getRequest()` 调用后端,导出业务函数
|
|
96
|
+
4. 在 [services/index.ts](./services/index.ts) 顶层 barrel 中追加 `export * from './user';`
|
|
97
|
+
|
|
98
|
+
> 不要在 [package.json](./package.json) 的 `exports` 中新增 `./services/user` 等深层路径——`services` 域的所有出口都收敛在 `./services` 一条入口。
|
|
99
|
+
|
|
100
|
+
## 新增同级共享模块(如 constants / utils)
|
|
101
|
+
|
|
102
|
+
当一类共享内容明显**不属于** `services` 域(例如纯常量、纯工具函数、纯类型定义),新增一个 `services/` 同级的目录,作为一个全新的独立模块对外暴露:
|
|
103
|
+
|
|
104
|
+
1. 在包根目录下新建模块目录,例如 `constants/`
|
|
105
|
+
2. 创建唯一对外入口 `constants/index.ts`,内部需要细分时只允许 `index.ts` re-export
|
|
106
|
+
3. 在 [package.json](./package.json) 的 `exports` 中追加且仅追加一条:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
"./constants": "./constants/index.ts"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
4. 在 [package.json](./package.json) 的 `files` 字段追加目录名 `"constants"`,确保发包时一并打包
|
|
113
|
+
|
|
114
|
+
> 同样禁止新增任何 `./constants/<sub>` 形式的深层路径。
|
|
115
|
+
|
|
116
|
+
## 为什么限制导出粒度
|
|
117
|
+
|
|
118
|
+
- **封装边界**:消费方只能通过模块 index 拿到类型与函数,内部实现拆分(拆分 type.ts、新增 helpers)不会破坏外部契约
|
|
119
|
+
- **重构自由度**:未来调整目录结构时,只要保持 index.ts 出口稳定,即可零成本重构
|
|
120
|
+
- **依赖图清晰**:避免出现"某处只 import 了一个深层 type"导致的隐式耦合
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= packageScope %>/shared",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "平台共享业务服务层骨架(request 注入器 + 通用类型 + 时区工具)",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./services": "./services/index.ts",
|
|
9
|
+
"./timezone": "./timezone/index.ts"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"services",
|
|
13
|
+
"timezone"
|
|
14
|
+
],
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"dayjs": "^1.11.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependenciesMeta": {
|
|
19
|
+
"dayjs": {
|
|
20
|
+
"optional": true
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.0.3"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用类型与工具函数
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** 通用接口响应结果码 */
|
|
6
|
+
export enum EResultCode {
|
|
7
|
+
OK = 0,
|
|
8
|
+
UnknownErr = -1,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ResultCode = EResultCode | number;
|
|
12
|
+
|
|
13
|
+
/** 通用接口响应结果 */
|
|
14
|
+
export interface IResult {
|
|
15
|
+
code?: ResultCode;
|
|
16
|
+
msg?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 判断接口响应是否成功(code === 0)
|
|
21
|
+
*/
|
|
22
|
+
export function isSuccess(result: IResult = {}): boolean {
|
|
23
|
+
return (result.code ?? 0) === 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 通用分页请求参数 */
|
|
27
|
+
export interface IPagedRequest {
|
|
28
|
+
page?: number;
|
|
29
|
+
page_size?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 通用分页响应结构(后端返回 count + results,与 DRF 默认格式一致) */
|
|
33
|
+
export interface IPagedResponse<T> {
|
|
34
|
+
results: T[];
|
|
35
|
+
count: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 通用接口响应包装 */
|
|
39
|
+
export interface IApiResponse<T = unknown> {
|
|
40
|
+
code: number;
|
|
41
|
+
msg: string;
|
|
42
|
+
data: T;
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// request 注入器
|
|
2
|
+
export { configureRequest, getRequest } from './request';
|
|
3
|
+
export type { RequestFn } from './request';
|
|
4
|
+
|
|
5
|
+
// 通用类型与工具
|
|
6
|
+
export * from './common';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 模块扩展规范:
|
|
10
|
+
*
|
|
11
|
+
* 1) 新增「业务接口」:在 services/ 下新建子目录(如 services/user/),
|
|
12
|
+
* 在本文件追加 `export * from './<module>';`,
|
|
13
|
+
* 不要在 package.json 的 exports 中新增子路径。
|
|
14
|
+
*
|
|
15
|
+
* 2) 新增「services 之外的共享模块」(如 constants / utils):
|
|
16
|
+
* 在包根目录新建与 services/ 同级的目录(如 constants/),
|
|
17
|
+
* 在 package.json 的 exports 中新增「且仅新增」一条:
|
|
18
|
+
* "./<module>": "./<module>/index.ts"
|
|
19
|
+
* 并将目录名加入 package.json 的 files 数组。
|
|
20
|
+
* 禁止暴露任何 `./<module>/<sub>` 形式的深层路径。
|
|
21
|
+
*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* request 注入器
|
|
3
|
+
*
|
|
4
|
+
* 各消费方在初始化时调用 configureRequest 注入 request 实例:
|
|
5
|
+
* - 主应用(layout 独立运行):在 app.tsx 顶层调用
|
|
6
|
+
* - 子应用(qiankun 模式):在收到 mount props 后的 setMicroAppProps 中调用
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // 主应用
|
|
10
|
+
* import { configureRequest } from '<packageScope>/shared/services';
|
|
11
|
+
* configureRequest(request);
|
|
12
|
+
*
|
|
13
|
+
* // 子应用
|
|
14
|
+
* import { configureRequest } from '<packageScope>/shared/services';
|
|
15
|
+
* configureRequest(props.request);
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type RequestFn = <T = unknown>(
|
|
19
|
+
url: string,
|
|
20
|
+
options?: Record<string, unknown>,
|
|
21
|
+
) => Promise<T>;
|
|
22
|
+
|
|
23
|
+
let _request: RequestFn | null = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 注入 request 实例,应在应用初始化阶段调用一次
|
|
27
|
+
*/
|
|
28
|
+
export function configureRequest(req: RequestFn): void {
|
|
29
|
+
_request = req;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取已注入的 request 实例
|
|
34
|
+
* 若未初始化则抛出错误,提示调用方检查初始化时序
|
|
35
|
+
*/
|
|
36
|
+
export function getRequest(): RequestFn {
|
|
37
|
+
if (!_request) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'[shared/services] request 未初始化,请先调用 configureRequest()',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return _request;
|
|
43
|
+
}
|