@liujitcn/kratos-taro-app-core 0.0.21 → 0.0.23
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/runner.mjs +11 -1
- package/package.json +1 -1
- package/src/api/base/file.ts +3 -3
- package/src/api/base/language.ts +4 -4
- package/src/bootstrap.ts +1 -1
- package/src/index.ts +3 -17
- package/src/locales/README.md +2 -2
- package/src/locales/en-US.json +66 -71
- package/src/locales/es-ES.json +89 -94
- package/src/locales/fr-FR.json +92 -97
- package/src/locales/generated.ts +22 -0
- package/src/locales/index.ts +47 -44
- package/src/locales/ja-JP.json +78 -83
- package/src/locales/ko-KR.json +88 -93
- package/src/locales/zh-CN.json +66 -71
- package/src/locales/zh-TW.json +66 -71
- package/src/module.ts +1 -1
- package/src/navigation.ts +11 -11
- package/src/rpc/base/v1/config.ts +12 -1
- package/src/rpc/base/v1/language.ts +7 -13
- package/src/rpc/base/v1/login.ts +14 -1
- package/src/runner.ts +11 -1
- package/src/stores/setting.ts +3 -3
- package/src/utils/file.ts +1 -1
- package/src/utils/http.ts +7 -7
- package/src/utils/index.ts +1 -1
- package/src/utils/passwordCrypto.ts +13 -13
- package/src/views/pages/index/index.tsx +9 -9
- package/src/views/pages/login/login.scss +24 -3
- package/src/views/pages/login/login.tsx +29 -28
- package/src/views/pages/login/protocal.tsx +2 -2
- package/src/views/pages/webview/webview.tsx +4 -4
- package/src/rpc/base/v1/enum.ts +0 -54
- package/src/rpc/system/common/v1/enum.ts +0 -80
package/src/locales/zh-TW.json
CHANGED
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
"common.action.login": "登入",
|
|
7
7
|
"common.action.logout": "退出登入",
|
|
8
8
|
"common.action.new": "新建",
|
|
9
|
-
"common.action.
|
|
10
|
-
"common.action.retry": "重試",
|
|
9
|
+
"common.action.open_in_new_window": "新視窗開啟",
|
|
11
10
|
"common.action.save": "儲存",
|
|
12
|
-
"common.action.search": "搜尋",
|
|
13
11
|
"common.field.language": "語言",
|
|
14
12
|
"common.language.en-US": "English",
|
|
15
13
|
"common.language.ja-JP": "日本語",
|
|
@@ -19,95 +17,92 @@
|
|
|
19
17
|
"common.language.fr-FR": "法語",
|
|
20
18
|
"common.language.es-ES": "西班牙語",
|
|
21
19
|
"common.message.loading": "正在載入",
|
|
22
|
-
"common.message.
|
|
23
|
-
"common.message.
|
|
20
|
+
"common.message.network_error": "網路錯誤,換個網路試試",
|
|
21
|
+
"common.message.request_error": "請求錯誤",
|
|
24
22
|
"common.message.unknown": "暫時無法處理,請稍後重試",
|
|
25
23
|
"common.title.notice": "提示",
|
|
26
|
-
"core.auth.
|
|
27
|
-
"core.auth.
|
|
24
|
+
"core.auth.login_again": "重新登入",
|
|
25
|
+
"core.auth.session_expired": "當前頁面已失效,請重新登入",
|
|
28
26
|
"core.config.missing": "移動端配置缺失:{names}",
|
|
29
|
-
"core.crypto.
|
|
30
|
-
"core.crypto.
|
|
31
|
-
"core.crypto.
|
|
32
|
-
"core.crypto.
|
|
33
|
-
"core.crypto.
|
|
34
|
-
"core.crypto.
|
|
35
|
-
"core.
|
|
36
|
-
"core.
|
|
37
|
-
"core.file.
|
|
38
|
-
"core.
|
|
39
|
-
"core.
|
|
40
|
-
"core.home.
|
|
41
|
-
"core.home.
|
|
42
|
-
"core.home.
|
|
43
|
-
"core.home.crossPlatformDescription": "同一套頁面程式碼適配 H5 和微信小程式",
|
|
44
|
-
"core.home.demoScope": "演示範圍",
|
|
27
|
+
"core.crypto.base64_decode_unsupported": "當前環境不支援 Base64 解碼",
|
|
28
|
+
"core.crypto.base64_encode_unsupported": "當前環境不支援 Base64 編碼",
|
|
29
|
+
"core.crypto.encrypt_failed": "密碼加密失敗",
|
|
30
|
+
"core.crypto.password_required": "密碼不能為空",
|
|
31
|
+
"core.crypto.password_too_long": "密碼長度超出限制",
|
|
32
|
+
"core.crypto.public_key_invalid": "公鑰格式無效",
|
|
33
|
+
"core.file.download_failed": "下載失敗",
|
|
34
|
+
"core.file.temporary_path_required": "小程式端請使用檔案臨時路徑上傳",
|
|
35
|
+
"core.file.upload_failed": "上傳失敗",
|
|
36
|
+
"core.home.account_capability": "賬戶能力",
|
|
37
|
+
"core.home.account_capability_description": "個人資料、應用設定與智慧助手入口已保留",
|
|
38
|
+
"core.home.cross_platform": "跨端頁面",
|
|
39
|
+
"core.home.cross_platform_description": "同一套頁面程式碼適配 H5 和微信小程式",
|
|
40
|
+
"core.home.demo_scope": "演示範圍",
|
|
45
41
|
"core.home.framework": "應用框架",
|
|
46
42
|
"core.home.language": "開發語言",
|
|
47
|
-
"core.home.
|
|
48
|
-
"core.home.
|
|
49
|
-
"core.home.
|
|
50
|
-
"core.home.
|
|
51
|
-
"core.home.
|
|
52
|
-
"core.home.
|
|
53
|
-
"core.home.
|
|
54
|
-
"core.login.
|
|
55
|
-
"core.login.
|
|
56
|
-
"core.login.
|
|
57
|
-
"core.login.
|
|
58
|
-
"core.login.
|
|
59
|
-
"core.login.
|
|
60
|
-
"core.login.
|
|
43
|
+
"core.home.main_title": "應用框架示例",
|
|
44
|
+
"core.home.state_management": "狀態管理",
|
|
45
|
+
"core.home.static_home": "靜態首頁",
|
|
46
|
+
"core.home.static_home_description": "當前首頁用於展示框架資訊,不依賴業務介面",
|
|
47
|
+
"core.home.style_solution": "樣式方案",
|
|
48
|
+
"core.home.sub_title": "保留通用導航與個人中心體驗",
|
|
49
|
+
"core.home.tech_stack": "當前技術棧",
|
|
50
|
+
"core.login.agree_prefix": "我已閱讀並同意",
|
|
51
|
+
"core.login.agree_separator": "和",
|
|
52
|
+
"core.login.behavior_click": "請按順序點選文字",
|
|
53
|
+
"core.login.behavior_loading": "載入中...",
|
|
54
|
+
"core.login.behavior_puzzle": "請拖動滑塊完成拼圖",
|
|
55
|
+
"core.login.behavior_rotate": "拖動滑塊,將內圈圖片轉正",
|
|
56
|
+
"core.login.bind_wechat": "登入並繫結微信",
|
|
61
57
|
"core.login.captcha": "請輸入驗證碼",
|
|
62
|
-
"core.login.
|
|
63
|
-
"core.login.
|
|
64
|
-
"core.login.
|
|
65
|
-
"core.login.
|
|
66
|
-
"core.login.
|
|
67
|
-
"core.login.
|
|
68
|
-
"core.login.
|
|
69
|
-
"core.login.
|
|
58
|
+
"core.login.captcha_load_failed": "驗證碼載入失敗",
|
|
59
|
+
"core.login.captcha_type_missing": "登入驗證碼型別未配置",
|
|
60
|
+
"core.login.captcha_type_name": "登入驗證碼型別",
|
|
61
|
+
"core.login.captcha_type_response_missing": "驗證碼介面未返回型別",
|
|
62
|
+
"core.login.captcha_write_failed": "驗證碼圖片寫入失敗",
|
|
63
|
+
"core.login.default_sub_title": "歡迎使用本應用",
|
|
64
|
+
"core.login.login_failed": "登入失敗,請重試",
|
|
65
|
+
"core.login.login_success": "登入成功",
|
|
70
66
|
"core.login.password": "請輸入密碼",
|
|
71
67
|
"core.login.privacy": "《隱私協議》",
|
|
72
|
-
"core.login.
|
|
73
|
-
"core.login.
|
|
68
|
+
"core.login.protocol_missing": "服務條款和隱私協議未配置",
|
|
69
|
+
"core.login.protocol_prompt": "請先閱讀並勾選協議內容,點選確定後將自動勾選並繼續登入",
|
|
74
70
|
"core.login.service": "《服務條款》",
|
|
75
71
|
"core.login.tenant": "請輸入租戶編號",
|
|
76
|
-
"core.login.
|
|
77
|
-
"core.login.
|
|
78
|
-
"core.login.
|
|
72
|
+
"core.login.user_name": "請輸入使用者名稱或手機號",
|
|
73
|
+
"core.login.user_name_mobile": "請輸入使用者名稱/手機號碼",
|
|
74
|
+
"core.login.user_name_password": "請輸入使用者名稱和密碼",
|
|
79
75
|
"core.login.wechat": "微信一鍵登入",
|
|
80
|
-
"core.login.
|
|
81
|
-
"core.login.
|
|
76
|
+
"core.login.wechat_bind_tip": "該微信尚未繫結,請登入已有賬號完成繫結",
|
|
77
|
+
"core.login.wechat_failed": "微信登入失敗,請稍後重試",
|
|
82
78
|
"core.navigation.home": "首頁",
|
|
83
79
|
"core.navigation.error.access": "選單訪問模式無效:{access}",
|
|
84
|
-
"core.navigation.error.
|
|
80
|
+
"core.navigation.error.child_tab": "只有移動端二級選單可以作為 tab:{name}",
|
|
85
81
|
"core.navigation.error.cycle": "移動端選單存在迴圈父級:{name}",
|
|
86
82
|
"core.navigation.error.identity": "選單 id、name 或 app/ 路徑無效",
|
|
87
|
-
"core.navigation.error.
|
|
88
|
-
"core.navigation.error.
|
|
89
|
-
"core.navigation.error.
|
|
90
|
-
"core.navigation.error.
|
|
91
|
-
"core.navigation.error.
|
|
92
|
-
"core.navigation.error.
|
|
93
|
-
"core.navigation.error.
|
|
94
|
-
"core.navigation.error.
|
|
95
|
-
"core.navigation.error.tabCount": "tabBar 只能配置 0 或 2-5 項",
|
|
83
|
+
"core.navigation.error.item_object": "選單項不是物件",
|
|
84
|
+
"core.navigation.error.list_missing": "選單響應缺少扁平列表",
|
|
85
|
+
"core.navigation.error.name_prefix": "選單名稱必須使用 App 字首:{name}",
|
|
86
|
+
"core.navigation.error.parent_missing": "選單缺少父級編號:{name}",
|
|
87
|
+
"core.navigation.error.parent_not_found": "移動端選單父級不存在:{name}",
|
|
88
|
+
"core.navigation.error.response_object": "選單響應不是物件",
|
|
89
|
+
"core.navigation.error.root_tab": "移動端二級選單必須作為 tab:{name}",
|
|
90
|
+
"core.navigation.error.tab_count": "tabBar 只能配置 0 或 2-5 項",
|
|
96
91
|
"core.navigation.error.unique": "選單 id、name、path 必須唯一",
|
|
97
|
-
"core.navigation.error.
|
|
92
|
+
"core.navigation.error.view_key": "未註冊 viewKey:{viewKey}",
|
|
98
93
|
"core.navigation.my": "我的",
|
|
99
94
|
"core.navigation.protocol": "協議詳情",
|
|
100
|
-
"core.protocol.
|
|
101
|
-
"core.protocol.
|
|
95
|
+
"core.protocol.load_failed": "移動端配置載入失敗",
|
|
96
|
+
"core.protocol.not_configured": "{title}未配置,暫時無法檢視",
|
|
102
97
|
"core.protocol.privacy": "隱私協議",
|
|
103
98
|
"core.protocol.service": "服務條款",
|
|
104
99
|
"core.status.CONFIG_ERROR": "導航配置無效",
|
|
105
|
-
"core.status.
|
|
100
|
+
"core.status.forbidden": "暫無訪問許可權",
|
|
106
101
|
"core.status.NOT_FOUND": "頁面不存在",
|
|
107
|
-
"core.status.
|
|
108
|
-
"core.status.
|
|
102
|
+
"core.status.offline": "網路不可用",
|
|
103
|
+
"core.status.page_unavailable": "頁面暫不可用",
|
|
109
104
|
"core.status.loading": "正在載入",
|
|
110
|
-
"core.webview.
|
|
111
|
-
"core.webview.
|
|
112
|
-
"core.webview.
|
|
105
|
+
"core.webview.embed_blocked": "當前 H5 頁面可能被目標站點限制嵌入",
|
|
106
|
+
"core.webview.invalid_url": "缺少有效連結地址",
|
|
107
|
+
"core.webview.open_failed": "連結無法開啟"
|
|
113
108
|
}
|
package/src/module.ts
CHANGED
|
@@ -62,6 +62,6 @@ export function resolveModuleIcon(icon: string | undefined): string | undefined
|
|
|
62
62
|
/** 解析随应用打包的静态资源路径。 */
|
|
63
63
|
export function resolveBundledAsset(source: string): string {
|
|
64
64
|
if (/^(?:https?:|data:)/.test(source)) return source
|
|
65
|
-
const base = process.env.
|
|
65
|
+
const base = process.env.VITE_APP_BASE_PATH || '/'
|
|
66
66
|
return `${base.replace(/\/?$/, '/')}${source.replace(/^\/+/, '')}`
|
|
67
67
|
}
|
package/src/navigation.ts
CHANGED
|
@@ -150,7 +150,7 @@ const defaultMenuTitleKeys: Record<string, string> = {
|
|
|
150
150
|
AppProtocol: 'core.navigation.protocol',
|
|
151
151
|
AppProfile: 'system.profile.title',
|
|
152
152
|
AppSettings: 'system.settings.title',
|
|
153
|
-
AppAi: 'system.ai.
|
|
153
|
+
AppAi: 'system.ai.chat_title',
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function localizedDefaultAppMenus(): AppMenu[] {
|
|
@@ -278,15 +278,15 @@ function readCachedMenus(cacheKey: string): AppMenu[] | undefined {
|
|
|
278
278
|
|
|
279
279
|
function normalizeMenuResponse(response: unknown): AppMenu[] {
|
|
280
280
|
if (Array.isArray(response)) return response.map(normalizeMenu)
|
|
281
|
-
if (!response || typeof response !== 'object') throw new Error(t('core.navigation.error.
|
|
281
|
+
if (!response || typeof response !== 'object') throw new Error(t('core.navigation.error.response_object'))
|
|
282
282
|
const record = response as Record<string, unknown>
|
|
283
283
|
const list = record.items ?? record.list ?? record.data
|
|
284
|
-
if (!Array.isArray(list)) throw new Error(t('core.navigation.error.
|
|
284
|
+
if (!Array.isArray(list)) throw new Error(t('core.navigation.error.list_missing'))
|
|
285
285
|
return list.map(normalizeMenu)
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
function normalizeMenu(value: unknown): AppMenu {
|
|
289
|
-
if (!value || typeof value !== 'object') throw new Error(t('core.navigation.error.
|
|
289
|
+
if (!value || typeof value !== 'object') throw new Error(t('core.navigation.error.item_object'))
|
|
290
290
|
const item = value as Record<string, unknown>
|
|
291
291
|
const meta = (item.meta ?? {}) as Record<string, unknown>
|
|
292
292
|
const app = (meta.app ?? item.app ?? {}) as Record<string, unknown>
|
|
@@ -321,13 +321,13 @@ function validateMenus(nextMenus: AppMenu[]): void {
|
|
|
321
321
|
throw new Error(t('core.navigation.error.identity'))
|
|
322
322
|
}
|
|
323
323
|
if (!menu.name.startsWith('App')) {
|
|
324
|
-
throw new Error(t('core.navigation.error.
|
|
324
|
+
throw new Error(t('core.navigation.error.name_prefix', { name: menu.name }))
|
|
325
325
|
}
|
|
326
326
|
if (!['PUBLIC', 'GUEST_ONLY', 'AUTHENTICATED'].includes(menu.access)) {
|
|
327
327
|
throw new Error(t('core.navigation.error.access', { access: menu.access }))
|
|
328
328
|
}
|
|
329
329
|
if (!resolveStaticView(menu.viewKey)) {
|
|
330
|
-
throw new Error(t('core.navigation.error.
|
|
330
|
+
throw new Error(t('core.navigation.error.view_key', { viewKey: menu.viewKey }))
|
|
331
331
|
}
|
|
332
332
|
if (ids.has(menu.id) || names.has(menu.name) || paths.has(menu.path)) {
|
|
333
333
|
throw new Error(t('core.navigation.error.unique'))
|
|
@@ -339,23 +339,23 @@ function validateMenus(nextMenus: AppMenu[]): void {
|
|
|
339
339
|
const menuMap = new Map(nextMenus.map((menu) => [menu.id, menu]))
|
|
340
340
|
for (const menu of nextMenus) {
|
|
341
341
|
if (menu.parentId === undefined) {
|
|
342
|
-
throw new Error(t('core.navigation.error.
|
|
342
|
+
throw new Error(t('core.navigation.error.parent_missing', { name: menu.name }))
|
|
343
343
|
}
|
|
344
344
|
if (menu.parentId === APP_MENU_ROOT_ID) {
|
|
345
|
-
if (!menu.inTabBar) throw new Error(t('core.navigation.error.
|
|
345
|
+
if (!menu.inTabBar) throw new Error(t('core.navigation.error.root_tab', { name: menu.name }))
|
|
346
346
|
continue
|
|
347
347
|
}
|
|
348
|
-
if (menu.inTabBar) throw new Error(t('core.navigation.error.
|
|
348
|
+
if (menu.inTabBar) throw new Error(t('core.navigation.error.child_tab', { name: menu.name }))
|
|
349
349
|
const visited = new Set<number>([menu.id])
|
|
350
350
|
let parentId = menu.parentId
|
|
351
351
|
while (parentId !== APP_MENU_ROOT_ID) {
|
|
352
352
|
if (visited.has(parentId)) throw new Error(t('core.navigation.error.cycle', { name: menu.name }))
|
|
353
353
|
visited.add(parentId)
|
|
354
354
|
const parent = menuMap.get(parentId)
|
|
355
|
-
if (!parent?.parentId) throw new Error(t('core.navigation.error.
|
|
355
|
+
if (!parent?.parentId) throw new Error(t('core.navigation.error.parent_not_found', { name: menu.name }))
|
|
356
356
|
parentId = parent.parentId
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
const tabCount = nextMenus.filter((menu) => menu.parentId === APP_MENU_ROOT_ID).length
|
|
360
|
-
if (tabCount === 1 || tabCount > 5) throw new Error(t('core.navigation.error.
|
|
360
|
+
if (tabCount === 1 || tabCount > 5) throw new Error(t('core.navigation.error.tab_count'))
|
|
361
361
|
}
|
|
@@ -5,7 +5,18 @@
|
|
|
5
5
|
// source: base/v1/config.proto
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
/** 系统配置位置。 */
|
|
10
|
+
export enum BaseConfigSite {
|
|
11
|
+
/** BASE_CONFIG_SITE_UNSPECIFIED - 未指定系统配置位置。 */
|
|
12
|
+
BASE_CONFIG_SITE_UNSPECIFIED = 0,
|
|
13
|
+
/** BASE_CONFIG_SITE_SYSTEM - 系统内使用。 */
|
|
14
|
+
BASE_CONFIG_SITE_SYSTEM = 1,
|
|
15
|
+
/** BASE_CONFIG_SITE_ADMIN - 管理端。 */
|
|
16
|
+
BASE_CONFIG_SITE_ADMIN = 2,
|
|
17
|
+
/** BASE_CONFIG_SITE_APP - 移动端。 */
|
|
18
|
+
BASE_CONFIG_SITE_APP = 3,
|
|
19
|
+
}
|
|
9
20
|
|
|
10
21
|
/** 获取系统配置条件 */
|
|
11
22
|
export interface GetConfigRequest {
|
|
@@ -6,32 +6,26 @@
|
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
export interface
|
|
9
|
+
/** 查询语言选项请求参数。 */
|
|
10
|
+
export interface OptionLanguageRequest {
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** 语言选择项。 */
|
|
14
14
|
export interface LanguageItem {
|
|
15
15
|
/** 标准语言代码 */
|
|
16
16
|
language_code: string;
|
|
17
|
-
/** 语言名称 */
|
|
18
|
-
language_name: string;
|
|
19
17
|
/** 本地语言名称 */
|
|
20
18
|
native_name: string;
|
|
21
|
-
/** 排序,值越小越靠前 */
|
|
22
|
-
sort: number;
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
/**
|
|
26
|
-
export interface
|
|
21
|
+
/** 当前支持的语言选项查询结果。 */
|
|
22
|
+
export interface OptionLanguageResponse {
|
|
27
23
|
/** 当前支持的语言列表 */
|
|
28
24
|
languages: LanguageItem[];
|
|
29
|
-
/** 主语言代码 */
|
|
30
|
-
primary_language_code: string;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
/** 语言公共服务。 */
|
|
34
28
|
export interface LanguageService {
|
|
35
|
-
/**
|
|
36
|
-
|
|
29
|
+
/** 查询当前支持的语言选项。 */
|
|
30
|
+
OptionLanguage(request: OptionLanguageRequest): Promise<OptionLanguageResponse>;
|
|
37
31
|
}
|
package/src/rpc/base/v1/login.ts
CHANGED
|
@@ -7,7 +7,20 @@
|
|
|
7
7
|
/* eslint-disable */
|
|
8
8
|
import type { PasswordCrypto } from "../../common/v1/types";
|
|
9
9
|
import type { Empty } from "../../google/protobuf/empty";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
/** 密码加密场景枚举。 */
|
|
12
|
+
export enum PasswordCryptoScene {
|
|
13
|
+
/** PASSWORD_CRYPTO_SCENE_UNSPECIFIED - 未指定密码加密场景。 */
|
|
14
|
+
PASSWORD_CRYPTO_SCENE_UNSPECIFIED = 0,
|
|
15
|
+
/** PASSWORD_CRYPTO_SCENE_LOGIN - 登录密码加密场景。 */
|
|
16
|
+
PASSWORD_CRYPTO_SCENE_LOGIN = 1,
|
|
17
|
+
/** PASSWORD_CRYPTO_SCENE_CREATE_BASE_USER - 新增后台用户密码加密场景。 */
|
|
18
|
+
PASSWORD_CRYPTO_SCENE_CREATE_BASE_USER = 2,
|
|
19
|
+
/** PASSWORD_CRYPTO_SCENE_RESET_BASE_USER_PASSWORD - 重置后台用户密码加密场景。 */
|
|
20
|
+
PASSWORD_CRYPTO_SCENE_RESET_BASE_USER_PASSWORD = 3,
|
|
21
|
+
/** PASSWORD_CRYPTO_SCENE_UPDATE_USER_PASSWORD - 个人修改密码加密场景。 */
|
|
22
|
+
PASSWORD_CRYPTO_SCENE_UPDATE_USER_PASSWORD = 4,
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
/** 验证码获取条件 */
|
|
13
26
|
export interface CaptchaRequest {
|
package/src/runner.ts
CHANGED
|
@@ -109,7 +109,17 @@ async function main(): Promise<void> {
|
|
|
109
109
|
process.once('SIGTERM', () => handleSignal('SIGTERM'))
|
|
110
110
|
|
|
111
111
|
try {
|
|
112
|
-
const args = [
|
|
112
|
+
const args = [
|
|
113
|
+
'exec',
|
|
114
|
+
'taro',
|
|
115
|
+
'build',
|
|
116
|
+
'--type',
|
|
117
|
+
options.type,
|
|
118
|
+
'--mode',
|
|
119
|
+
options.mode,
|
|
120
|
+
'--env-prefix',
|
|
121
|
+
'VITE_APP_,TARO_APP_',
|
|
122
|
+
]
|
|
113
123
|
if (options.watch) args.push('--watch')
|
|
114
124
|
child = spawn(process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', args, {
|
|
115
125
|
cwd: hostRoot,
|
package/src/stores/setting.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { create } from 'zustand'
|
|
2
2
|
import { defConfigService } from '../api/base/config'
|
|
3
3
|
import { t } from '../locales'
|
|
4
|
-
import { BaseConfigSite } from '../rpc/base/v1/
|
|
4
|
+
import { BaseConfigSite } from '../rpc/base/v1/config'
|
|
5
5
|
|
|
6
6
|
const REQUIRED_APP_CONFIGS = [
|
|
7
7
|
{ key: 'serviceProtocol', nameKey: 'core.login.service' },
|
|
8
8
|
{ key: 'privacyProtocol', nameKey: 'core.login.privacy' },
|
|
9
|
-
{ key: 'captchaType', nameKey: 'core.login.
|
|
9
|
+
{ key: 'captchaType', nameKey: 'core.login.captcha_type_name' },
|
|
10
10
|
] as const
|
|
11
11
|
|
|
12
12
|
/** 应用配置状态及操作。 */
|
|
@@ -27,7 +27,7 @@ export const useSettingStore = create<SettingStoreState>((set, get) => ({
|
|
|
27
27
|
async loadData() {
|
|
28
28
|
if (loading) return loading
|
|
29
29
|
loading = (async () => {
|
|
30
|
-
const response = await defConfigService.GetConfig({ site: BaseConfigSite.
|
|
30
|
+
const response = await defConfigService.GetConfig({ site: BaseConfigSite.BASE_CONFIG_SITE_APP })
|
|
31
31
|
const nextData = new Map(response.configs.map((item) => [item.key, item.value]))
|
|
32
32
|
const missing = REQUIRED_APP_CONFIGS.filter(({ key }) => !nextData.get(key))
|
|
33
33
|
if (missing.length) {
|
package/src/utils/file.ts
CHANGED
|
@@ -18,7 +18,7 @@ export async function uploadFile(fileType: string, filePath: string): Promise<Fi
|
|
|
18
18
|
...(token ? { Authorization: token } : {}),
|
|
19
19
|
},
|
|
20
20
|
})
|
|
21
|
-
if (response.statusCode !== 200) throw new Error(t('core.file.
|
|
21
|
+
if (response.statusCode !== 200) throw new Error(t('core.file.upload_failed'))
|
|
22
22
|
return JSON.parse(response.data) as FileInfo
|
|
23
23
|
}
|
|
24
24
|
|
package/src/utils/http.ts
CHANGED
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
import { saveCurrentRoute } from './navigation'
|
|
13
13
|
import { getLocaleRequestHeaders, t } from '../locales'
|
|
14
14
|
|
|
15
|
-
const apiBasePath = process.env.
|
|
16
|
-
const apiTargetUrl = process.env.
|
|
15
|
+
const apiBasePath = process.env.VITE_APP_BASE_API || '/api'
|
|
16
|
+
const apiTargetUrl = process.env.VITE_APP_API_URL || ''
|
|
17
17
|
const normalizedApiBasePath = apiBasePath.startsWith('/') ? apiBasePath : `/${apiBasePath}`
|
|
18
18
|
const requestOrigin = process.env.TARO_ENV === 'h5' ? '' : apiTargetUrl.replace(/\/$/, '')
|
|
19
19
|
/** 请求基础地址。 */
|
|
@@ -144,7 +144,7 @@ async function sendRequest<T>(
|
|
|
144
144
|
}
|
|
145
145
|
await Taro.showToast({
|
|
146
146
|
icon: 'none',
|
|
147
|
-
title: responseData?.message || t('common.message.
|
|
147
|
+
title: responseData?.message || t('common.message.request_error'),
|
|
148
148
|
})
|
|
149
149
|
throw response
|
|
150
150
|
} catch (error) {
|
|
@@ -155,7 +155,7 @@ async function sendRequest<T>(
|
|
|
155
155
|
) {
|
|
156
156
|
throw error
|
|
157
157
|
}
|
|
158
|
-
await Taro.showToast({ icon: 'none', title: t('common.message.
|
|
158
|
+
await Taro.showToast({ icon: 'none', title: t('common.message.network_error') })
|
|
159
159
|
throw error
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -241,9 +241,9 @@ async function promptRelogin(): Promise<void> {
|
|
|
241
241
|
try {
|
|
242
242
|
const modal = await Taro.showModal({
|
|
243
243
|
title: t('common.title.notice'),
|
|
244
|
-
content: t('core.auth.
|
|
244
|
+
content: t('core.auth.session_expired'),
|
|
245
245
|
showCancel: false,
|
|
246
|
-
confirmText: t('core.auth.
|
|
246
|
+
confirmText: t('core.auth.login_again'),
|
|
247
247
|
})
|
|
248
248
|
if (!modal.confirm) return
|
|
249
249
|
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
@@ -269,7 +269,7 @@ function handleAuthExpiredByMode(
|
|
|
269
269
|
if (authMode !== 'optional') {
|
|
270
270
|
void Taro.showToast({
|
|
271
271
|
icon: 'none',
|
|
272
|
-
title: responseData?.message || t('common.message.
|
|
272
|
+
title: responseData?.message || t('common.message.request_error'),
|
|
273
273
|
})
|
|
274
274
|
}
|
|
275
275
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import Taro from '@tarojs/taro'
|
|
|
2
2
|
import * as miniCrypto from 'asmcrypto.js'
|
|
3
3
|
import { defLoginService } from '../api/base/login'
|
|
4
4
|
import { t } from '../locales'
|
|
5
|
-
import { PasswordCryptoScene } from '../rpc/base/v1/
|
|
5
|
+
import { PasswordCryptoScene } from '../rpc/base/v1/login'
|
|
6
6
|
import type { PasswordCrypto } from '../rpc/common/v1/types'
|
|
7
7
|
|
|
8
8
|
type MiniCrypto = typeof import('asmcrypto.js')
|
|
@@ -18,7 +18,7 @@ function pemToArrayBuffer(pem: string) {
|
|
|
18
18
|
.replace(/\s/g, '')
|
|
19
19
|
const decode = globalThis.atob
|
|
20
20
|
if (!decode) {
|
|
21
|
-
throw new Error(t('core.crypto.
|
|
21
|
+
throw new Error(t('core.crypto.base64_decode_unsupported'))
|
|
22
22
|
}
|
|
23
23
|
const binary = decode(base64)
|
|
24
24
|
const buffer = new Uint8Array(binary.length)
|
|
@@ -37,7 +37,7 @@ function arrayBufferToBase64(buffer: ArrayBuffer) {
|
|
|
37
37
|
}
|
|
38
38
|
const encode = globalThis.btoa
|
|
39
39
|
if (!encode) {
|
|
40
|
-
throw new Error(t('core.crypto.
|
|
40
|
+
throw new Error(t('core.crypto.base64_encode_unsupported'))
|
|
41
41
|
}
|
|
42
42
|
return encode(binary)
|
|
43
43
|
}
|
|
@@ -60,7 +60,7 @@ type DerNode = {
|
|
|
60
60
|
/** 读取 DER 编码节点,用于解析 RSA SubjectPublicKeyInfo。 */
|
|
61
61
|
function readDerNode(data: Uint8Array, offset: number): DerNode {
|
|
62
62
|
if (offset + 2 > data.length) {
|
|
63
|
-
throw new Error(t('core.crypto.
|
|
63
|
+
throw new Error(t('core.crypto.public_key_invalid'))
|
|
64
64
|
}
|
|
65
65
|
const tag = data[offset]
|
|
66
66
|
const firstLengthByte = data[offset + 1]
|
|
@@ -69,7 +69,7 @@ function readDerNode(data: Uint8Array, offset: number): DerNode {
|
|
|
69
69
|
if (firstLengthByte & 0x80) {
|
|
70
70
|
const lengthByteCount = firstLengthByte & 0x7f
|
|
71
71
|
if (lengthByteCount === 0 || valueOffset + lengthByteCount > data.length) {
|
|
72
|
-
throw new Error(t('core.crypto.
|
|
72
|
+
throw new Error(t('core.crypto.public_key_invalid'))
|
|
73
73
|
}
|
|
74
74
|
length = 0
|
|
75
75
|
for (let index = 0; index < lengthByteCount; index += 1) {
|
|
@@ -79,7 +79,7 @@ function readDerNode(data: Uint8Array, offset: number): DerNode {
|
|
|
79
79
|
}
|
|
80
80
|
const end = valueOffset + length
|
|
81
81
|
if (end > data.length) {
|
|
82
|
-
throw new Error(t('core.crypto.
|
|
82
|
+
throw new Error(t('core.crypto.public_key_invalid'))
|
|
83
83
|
}
|
|
84
84
|
return {
|
|
85
85
|
tag,
|
|
@@ -108,13 +108,13 @@ function parseMiniRsaPublicKey(publicKey: string): [Uint8Array, Uint8Array] {
|
|
|
108
108
|
const algorithm = readDerNode(subjectPublicKeyInfo.value, 0)
|
|
109
109
|
const bitString = readDerNode(subjectPublicKeyInfo.value, algorithm.next)
|
|
110
110
|
if (bitString.tag !== 0x03 || bitString.value[0] !== 0) {
|
|
111
|
-
throw new Error(t('core.crypto.
|
|
111
|
+
throw new Error(t('core.crypto.public_key_invalid'))
|
|
112
112
|
}
|
|
113
113
|
const rsaPublicKey = readDerNode(bitString.value, 1)
|
|
114
114
|
const modulus = readDerNode(rsaPublicKey.value, 0)
|
|
115
115
|
const exponent = readDerNode(rsaPublicKey.value, modulus.next)
|
|
116
116
|
if (modulus.tag !== 0x02 || exponent.tag !== 0x02) {
|
|
117
|
-
throw new Error(t('core.crypto.
|
|
117
|
+
throw new Error(t('core.crypto.public_key_invalid'))
|
|
118
118
|
}
|
|
119
119
|
return [trimDerInteger(modulus.value), trimDerInteger(exponent.value)]
|
|
120
120
|
}
|
|
@@ -132,7 +132,7 @@ function generateMgf1Mask(crypto: MiniCrypto, seed: Uint8Array, length: number)
|
|
|
132
132
|
counter[3] = index
|
|
133
133
|
let block = hash.reset().process(seed).process(counter).finish().result
|
|
134
134
|
if (!block) {
|
|
135
|
-
throw new Error(t('core.crypto.
|
|
135
|
+
throw new Error(t('core.crypto.encrypt_failed'))
|
|
136
136
|
}
|
|
137
137
|
const target = mask.subarray(index * hash.HASH_SIZE)
|
|
138
138
|
if (block.length > target.length) {
|
|
@@ -172,14 +172,14 @@ async function encryptPortablePassword(
|
|
|
172
172
|
const dataBlockLength = keySize - hash.HASH_SIZE - 1
|
|
173
173
|
const paddingLength = dataBlockLength - aesKey.length - hash.HASH_SIZE - 1
|
|
174
174
|
if (paddingLength < 0) {
|
|
175
|
-
throw new Error(t('core.crypto.
|
|
175
|
+
throw new Error(t('core.crypto.password_too_long'))
|
|
176
176
|
}
|
|
177
177
|
const encodedMessage = new Uint8Array(keySize)
|
|
178
178
|
const maskedSeed = encodedMessage.subarray(1, hash.HASH_SIZE + 1)
|
|
179
179
|
const dataBlock = encodedMessage.subarray(hash.HASH_SIZE + 1)
|
|
180
180
|
const labelHash = hash.reset().process(new Uint8Array()).finish().result
|
|
181
181
|
if (!labelHash) {
|
|
182
|
-
throw new Error(t('core.crypto.
|
|
182
|
+
throw new Error(t('core.crypto.encrypt_failed'))
|
|
183
183
|
}
|
|
184
184
|
dataBlock.set(labelHash, 0)
|
|
185
185
|
dataBlock.set(aesKey, hash.HASH_SIZE + paddingLength + 1)
|
|
@@ -197,7 +197,7 @@ async function encryptPortablePassword(
|
|
|
197
197
|
new crypto.BigNumber(encodedMessage),
|
|
198
198
|
).result
|
|
199
199
|
if (!encryptedKey) {
|
|
200
|
-
throw new Error(t('core.crypto.
|
|
200
|
+
throw new Error(t('core.crypto.encrypt_failed'))
|
|
201
201
|
}
|
|
202
202
|
const ciphertext = crypto.AES_GCM.encrypt(plaintext, aesKey, iv)
|
|
203
203
|
return {
|
|
@@ -217,7 +217,7 @@ export async function encryptPassword(
|
|
|
217
217
|
): Promise<PasswordCrypto> {
|
|
218
218
|
const plainPassword = password.trim()
|
|
219
219
|
if (!plainPassword) {
|
|
220
|
-
throw new Error(t('core.crypto.
|
|
220
|
+
throw new Error(t('core.crypto.password_required'))
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
const publicKeyResponse = await defLoginService.PasswordPublicKey({ scene })
|
|
@@ -10,8 +10,8 @@ export default function HomePage() {
|
|
|
10
10
|
const { t } = useI18n()
|
|
11
11
|
const settings = useSettingStore((state) => state.data)
|
|
12
12
|
const loadData = useSettingStore((state) => state.loadData)
|
|
13
|
-
const mainTitle = settings?.get('mainTitle') || t('core.home.
|
|
14
|
-
const subTitle = settings?.get('subTitle') || t('core.home.
|
|
13
|
+
const mainTitle = settings?.get('mainTitle') || t('core.home.main_title')
|
|
14
|
+
const subTitle = settings?.get('subTitle') || t('core.home.sub_title')
|
|
15
15
|
const appLogo = settings?.get('appLogo') || defaultLogo
|
|
16
16
|
useLoad(() => {
|
|
17
17
|
void loadData().catch(() => undefined)
|
|
@@ -20,13 +20,13 @@ export default function HomePage() {
|
|
|
20
20
|
const stack = [
|
|
21
21
|
[t('core.home.framework'), 'Taro + React 18'],
|
|
22
22
|
[t('core.home.language'), 'TypeScript'],
|
|
23
|
-
[t('core.home.
|
|
24
|
-
[t('core.home.
|
|
23
|
+
[t('core.home.state_management'), 'Zustand'],
|
|
24
|
+
[t('core.home.style_solution'), 'Sass + px'],
|
|
25
25
|
]
|
|
26
26
|
const demos = [
|
|
27
|
-
[t('core.home.
|
|
28
|
-
[t('core.home.
|
|
29
|
-
[t('core.home.
|
|
27
|
+
[t('core.home.cross_platform'), t('core.home.cross_platform_description')],
|
|
28
|
+
[t('core.home.account_capability'), t('core.home.account_capability_description')],
|
|
29
|
+
[t('core.home.static_home'), t('core.home.static_home_description')],
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
return (
|
|
@@ -44,7 +44,7 @@ export default function HomePage() {
|
|
|
44
44
|
</View>
|
|
45
45
|
|
|
46
46
|
<View className='home-section'>
|
|
47
|
-
<Text className='home-section__title'>{t('core.home.
|
|
47
|
+
<Text className='home-section__title'>{t('core.home.tech_stack')}</Text>
|
|
48
48
|
<View className='home-info-list'>
|
|
49
49
|
{stack.map(([label, value]) => (
|
|
50
50
|
<View className='home-info-row' key={label}>
|
|
@@ -56,7 +56,7 @@ export default function HomePage() {
|
|
|
56
56
|
</View>
|
|
57
57
|
|
|
58
58
|
<View className='home-section'>
|
|
59
|
-
<Text className='home-section__title'>{t('core.home.
|
|
59
|
+
<Text className='home-section__title'>{t('core.home.demo_scope')}</Text>
|
|
60
60
|
<View className='home-demo-list'>
|
|
61
61
|
{demos.map(([title, description], index) => (
|
|
62
62
|
<View className='home-demo-item' key={title}>
|