@liujitcn/kratos-taro-app-core 0.0.82 → 0.0.84
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/package.json +1 -1
- package/src/locales/README.md +1 -1
- package/src/locales/en-US.json +1 -13
- package/src/locales/ja-JP.json +1 -13
- package/src/locales/zh-CN.json +1 -13
- package/src/locales/zh-TW.json +1 -13
- package/src/utils/http.ts +2 -1
- package/src/utils/navigation.ts +26 -6
- package/src/views/pages/login/login.tsx +10 -7
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
## 公开入口
|
|
20
20
|
|
|
21
|
+
登录页返回会清理本次回跳并替换到首页;直接打开登录页清理旧 `lastRoute`,登录成功默认进入首页。主动登录、路由守卫和会话过期重登通过 `redirect=1` 标记保留本次业务目标及查询参数,成功跳转后才清理目标。登录页每次显示都会检查有效登录态,已登录则替换回首页,避免浏览器回退重新停留在登录页。H5 与微信小程序统一使用 `reLaunch` 替换登录入口并重置应用页面栈。
|
|
22
|
+
|
|
21
23
|
- `@liujitcn/kratos-taro-app-core`:启动、模块、导航、状态和常用工具,包含动态路径使用的 `resolveBundledAsset()`。
|
|
22
24
|
- `@liujitcn/kratos-taro-app-core/static/*`:页面图片等可由 Webpack 静态分析的打包资源。
|
|
23
25
|
- `@liujitcn/kratos-taro-app-core/build`:构建期模块描述和双语构建错误文案格式化器。
|
package/package.json
CHANGED
package/src/locales/README.md
CHANGED
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
| `en-US.json` | 英文 Core 语言包。 | Taro 切换为英文时使用。 |
|
|
10
10
|
| `ja-JP.json` | 日文 Core 语言包。 | Taro 切换为日文时使用。 |
|
|
11
11
|
|
|
12
|
-
语言包是标准 JSON,不能在文件内直接写注释;公共键使用 `common.` 或 `core.` 命名空间。新增语言后执行仓库根目录的 `make i18n`。语言切换选项固定显示后端 `base_language.native_name
|
|
12
|
+
语言包是标准 JSON,不能在文件内直接写注释;公共键使用 `common.` 或 `core.` 命名空间。新增语言后执行仓库根目录的 `make i18n`。语言切换选项固定显示后端 `base_language.native_name`,接口不可用时使用代码内置的原生名称;语言迁移初始名称来自管理端 Core 的 `common.language.*`。新增语言的完整文件清单见[国际化设计与语言扩展](../../../../../../docs/国际化设计与语言扩展.md)。
|
package/src/locales/en-US.json
CHANGED
|
@@ -10,16 +10,11 @@
|
|
|
10
10
|
"common.action.save": "Save",
|
|
11
11
|
"common.action.view": "View",
|
|
12
12
|
"common.field.language": "Language",
|
|
13
|
-
"common.language.en-US": "English",
|
|
14
|
-
"common.language.zh-CN": "简体中文",
|
|
15
|
-
"common.language.zh-TW": "Traditional Chinese",
|
|
16
|
-
"common.language.ja-JP": "Japanese",
|
|
17
13
|
"common.message.loading": "Loading",
|
|
18
14
|
"common.message.network_error": "Network error. Check your connection and try again.",
|
|
19
15
|
"common.message.request_error": "Request failed",
|
|
20
16
|
"common.message.unknown": "Unable to process this request. Try again later.",
|
|
21
17
|
"common.title.notice": "Notice",
|
|
22
|
-
"core.auth.login_again": "Log in again",
|
|
23
18
|
"core.auth.session_expired": "Your session has expired. Please log in again.",
|
|
24
19
|
"core.config.missing": "Missing app configuration: {names}",
|
|
25
20
|
"core.crypto.base64_decode_unsupported": "Base64 decoding is not supported in this environment",
|
|
@@ -123,12 +118,5 @@
|
|
|
123
118
|
"core.webview.embed_blocked": "The destination may block embedding on H5",
|
|
124
119
|
"core.webview.invalid_url": "No valid link was provided",
|
|
125
120
|
"core.webview.open_failed": "Unable to open link",
|
|
126
|
-
"common.
|
|
127
|
-
"common.resource.default": "{resource}",
|
|
128
|
-
"common.action.delete_resource": "Delete {resource}",
|
|
129
|
-
"common.action.set_field_status": "Set {field}",
|
|
130
|
-
"common.action.load_more": "Load more",
|
|
131
|
-
"common.status.loading": "Loading…",
|
|
132
|
-
"core.navigation.analytics": "Analytics",
|
|
133
|
-
"core.navigation.report": "Reports"
|
|
121
|
+
"common.action.load_more": "Load more"
|
|
134
122
|
}
|
package/src/locales/ja-JP.json
CHANGED
|
@@ -10,16 +10,11 @@
|
|
|
10
10
|
"common.action.save": "保存",
|
|
11
11
|
"common.action.view": "表示",
|
|
12
12
|
"common.field.language": "言語",
|
|
13
|
-
"common.language.en-US": "英語",
|
|
14
|
-
"common.language.zh-CN": "簡体字中国語",
|
|
15
|
-
"common.language.zh-TW": "繁体字中国語",
|
|
16
|
-
"common.language.ja-JP": "日本語",
|
|
17
13
|
"common.message.loading": "をロード中です",
|
|
18
14
|
"common.message.network_error": "ネットワーク エラー。別のネットワークを試してください",
|
|
19
15
|
"common.message.request_error": "リクエストエラー",
|
|
20
16
|
"common.message.unknown": "は一時的に処理できません。後でもう一度試してください。",
|
|
21
17
|
"common.title.notice": "ヒント",
|
|
22
|
-
"core.auth.login_again": "再度ログインしてください",
|
|
23
18
|
"core.auth.session_expired": "現在のページは無効です。再度ログインしてください。",
|
|
24
19
|
"core.config.missing": "モバイル端末の設定がありません: {names}",
|
|
25
20
|
"core.crypto.base64_decode_unsupported": "現在の環境は Base64 デコードをサポートしていません",
|
|
@@ -123,12 +118,5 @@
|
|
|
123
118
|
"core.webview.embed_blocked": "現在の H5 ページは、ターゲット サイトによって埋め込みが制限されている可能性があります",
|
|
124
119
|
"core.webview.invalid_url": "有効なリンク アドレスがありません",
|
|
125
120
|
"core.webview.open_failed": "リンクを開けません",
|
|
126
|
-
"common.
|
|
127
|
-
"common.resource.default": "{resource}",
|
|
128
|
-
"common.action.delete_resource": "{resource}を削除",
|
|
129
|
-
"common.action.set_field_status": "{field}を設定",
|
|
130
|
-
"common.action.load_more": "さらに読み込む",
|
|
131
|
-
"common.status.loading": "読み込み中…",
|
|
132
|
-
"core.navigation.analytics": "分析",
|
|
133
|
-
"core.navigation.report": "レポート"
|
|
121
|
+
"common.action.load_more": "さらに読み込む"
|
|
134
122
|
}
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -10,16 +10,11 @@
|
|
|
10
10
|
"common.action.save": "保存",
|
|
11
11
|
"common.action.view": "查看",
|
|
12
12
|
"common.field.language": "语言",
|
|
13
|
-
"common.language.en-US": "English",
|
|
14
|
-
"common.language.zh-CN": "简体中文",
|
|
15
|
-
"common.language.zh-TW": "繁体中文",
|
|
16
|
-
"common.language.ja-JP": "日语",
|
|
17
13
|
"common.message.loading": "正在加载",
|
|
18
14
|
"common.message.network_error": "网络错误,换个网络试试",
|
|
19
15
|
"common.message.request_error": "请求错误",
|
|
20
16
|
"common.message.unknown": "暂时无法处理,请稍后重试",
|
|
21
17
|
"common.title.notice": "提示",
|
|
22
|
-
"core.auth.login_again": "重新登录",
|
|
23
18
|
"core.auth.session_expired": "当前页面已失效,请重新登录",
|
|
24
19
|
"core.config.missing": "移动端配置缺失:{names}",
|
|
25
20
|
"core.crypto.base64_decode_unsupported": "当前环境不支持 Base64 解码",
|
|
@@ -123,12 +118,5 @@
|
|
|
123
118
|
"core.webview.embed_blocked": "当前 H5 页面可能被目标站点限制嵌入",
|
|
124
119
|
"core.webview.invalid_url": "缺少有效链接地址",
|
|
125
120
|
"core.webview.open_failed": "链接无法打开",
|
|
126
|
-
"common.
|
|
127
|
-
"common.resource.default": "{resource}",
|
|
128
|
-
"common.action.delete_resource": "删除{resource}",
|
|
129
|
-
"common.action.set_field_status": "设置{field}",
|
|
130
|
-
"common.action.load_more": "加载更多",
|
|
131
|
-
"common.status.loading": "加载中…",
|
|
132
|
-
"core.navigation.analytics": "分析",
|
|
133
|
-
"core.navigation.report": "报表"
|
|
121
|
+
"common.action.load_more": "加载更多"
|
|
134
122
|
}
|
package/src/locales/zh-TW.json
CHANGED
|
@@ -10,16 +10,11 @@
|
|
|
10
10
|
"common.action.save": "儲存",
|
|
11
11
|
"common.action.view": "檢視",
|
|
12
12
|
"common.field.language": "語言",
|
|
13
|
-
"common.language.en-US": "English",
|
|
14
|
-
"common.language.zh-CN": "簡體中文",
|
|
15
|
-
"common.language.zh-TW": "繁體中文",
|
|
16
|
-
"common.language.ja-JP": "日語",
|
|
17
13
|
"common.message.loading": "正在載入",
|
|
18
14
|
"common.message.network_error": "網路錯誤,換個網路試試",
|
|
19
15
|
"common.message.request_error": "請求錯誤",
|
|
20
16
|
"common.message.unknown": "暫時無法處理,請稍後重試",
|
|
21
17
|
"common.title.notice": "提示",
|
|
22
|
-
"core.auth.login_again": "重新登入",
|
|
23
18
|
"core.auth.session_expired": "當前頁面已失效,請重新登入",
|
|
24
19
|
"core.config.missing": "移動端配置缺失:{names}",
|
|
25
20
|
"core.crypto.base64_decode_unsupported": "當前環境不支援 Base64 解碼",
|
|
@@ -123,12 +118,5 @@
|
|
|
123
118
|
"core.webview.embed_blocked": "當前 H5 頁面可能被目標站點限制嵌入",
|
|
124
119
|
"core.webview.invalid_url": "缺少有效連結地址",
|
|
125
120
|
"core.webview.open_failed": "連結無法開啟",
|
|
126
|
-
"common.
|
|
127
|
-
"common.resource.default": "{resource}",
|
|
128
|
-
"common.action.delete_resource": "刪除{resource}",
|
|
129
|
-
"common.action.set_field_status": "設定{field}",
|
|
130
|
-
"common.action.load_more": "載入更多",
|
|
131
|
-
"common.status.loading": "載入中…",
|
|
132
|
-
"core.navigation.analytics": "分析",
|
|
133
|
-
"core.navigation.report": "報表"
|
|
121
|
+
"common.action.load_more": "載入更多"
|
|
134
122
|
}
|
package/src/utils/http.ts
CHANGED
|
@@ -265,6 +265,7 @@ async function refreshAccessToken(): Promise<void> {
|
|
|
265
265
|
/** 会话过期弹窗等待时长:H5 端路由切换会自动隐藏模态且 Promise 永不结算,超时释放避免提示永久卡死。 */
|
|
266
266
|
const RELOGIN_PROMPT_TIMEOUT = 60_000
|
|
267
267
|
|
|
268
|
+
/** 提示会话过期并进入携带本次业务回跳标记的登录页。 */
|
|
268
269
|
async function promptRelogin(): Promise<void> {
|
|
269
270
|
// 401 来自旧会话的在途请求而当前已是有效登录态时直接忽略,避免刚登录成功被弹窗打断。
|
|
270
271
|
if (hasValidToken()) return
|
|
@@ -295,7 +296,7 @@ async function promptRelogin(): Promise<void> {
|
|
|
295
296
|
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
296
297
|
silentClearAuthData()
|
|
297
298
|
saveCurrentRoute()
|
|
298
|
-
await Taro.reLaunch({ url: '/pages/login/login' })
|
|
299
|
+
await Taro.reLaunch({ url: '/pages/login/login?redirect=1' })
|
|
299
300
|
} finally {
|
|
300
301
|
isPromptingRelogin = false
|
|
301
302
|
}
|
package/src/utils/navigation.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import Taro, { getCurrentPages } from '@tarojs/taro'
|
|
2
|
+
import { hasValidToken } from './auth'
|
|
2
3
|
|
|
3
4
|
const LAST_ROUTE_KEY = 'lastRoute'
|
|
4
5
|
const LOGIN_PAGE = '/pages/login/login'
|
|
6
|
+
/** 携带本次回跳标记的登录入口。 */
|
|
7
|
+
const LOGIN_REDIRECT_PAGE = `${LOGIN_PAGE}?redirect=1`
|
|
5
8
|
/** 首页 tab 页面路径。 */
|
|
6
9
|
export const homeTabPage = '/pages/index/index'
|
|
7
10
|
|
|
@@ -57,10 +60,27 @@ export function consumeLoginRedirectUrl(): string {
|
|
|
57
60
|
return url
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
/**
|
|
63
|
+
/** prepareLoginPage 清理直接访问的旧目标,已登录时替换回首页。 */
|
|
64
|
+
export function prepareLoginPage(redirect?: string): boolean {
|
|
65
|
+
if (redirect !== '1') Taro.removeStorageSync(LAST_ROUTE_KEY)
|
|
66
|
+
if (!hasValidToken()) return true
|
|
67
|
+
void returnFromLogin()
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** returnFromLogin 结束本次登录入口并替换到首页,避免返回受保护页面。 */
|
|
72
|
+
export async function returnFromLogin(): Promise<void> {
|
|
73
|
+
Taro.removeStorageSync(LAST_ROUTE_KEY)
|
|
74
|
+
Taro.setStorageSync('SwitchTabIndex', true)
|
|
75
|
+
await Taro.reLaunch({ url: homeTabPage })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** restoreLoginRedirect 替换到本次访问目标,跳转成功后才清理保存的地址。 */
|
|
61
79
|
export async function restoreLoginRedirect(): Promise<void> {
|
|
62
80
|
const savedRoute = Taro.getStorageSync<string>(LAST_ROUTE_KEY) || ''
|
|
63
|
-
const
|
|
81
|
+
const normalizedRoute = normalizeRouteUrl(savedRoute)
|
|
82
|
+
const targetRoute =
|
|
83
|
+
savedRoute && !normalizedRoute.startsWith(LOGIN_PAGE) ? normalizedRoute : homeTabPage
|
|
64
84
|
if (targetRoute.startsWith(homeTabPage)) Taro.setStorageSync('SwitchTabIndex', true)
|
|
65
85
|
await Taro.reLaunch({ url: targetRoute })
|
|
66
86
|
if (savedRoute && Taro.getStorageSync<string>(LAST_ROUTE_KEY) === savedRoute) {
|
|
@@ -73,9 +93,9 @@ export function navigateToLogin(redirectUrl?: string): void {
|
|
|
73
93
|
if (typeof redirectUrl === 'string' && redirectUrl) saveLoginRedirectUrl(redirectUrl)
|
|
74
94
|
else saveCurrentRoute()
|
|
75
95
|
void Taro.navigateTo({
|
|
76
|
-
url:
|
|
96
|
+
url: LOGIN_REDIRECT_PAGE,
|
|
77
97
|
fail: () => {
|
|
78
|
-
void Taro.reLaunch({ url:
|
|
98
|
+
void Taro.reLaunch({ url: LOGIN_REDIRECT_PAGE })
|
|
79
99
|
},
|
|
80
100
|
})
|
|
81
101
|
}
|
|
@@ -85,9 +105,9 @@ export function redirectToLogin(redirectUrl?: string): void {
|
|
|
85
105
|
if (typeof redirectUrl === 'string' && redirectUrl) saveLoginRedirectUrl(redirectUrl)
|
|
86
106
|
else saveCurrentRoute()
|
|
87
107
|
void Taro.redirectTo({
|
|
88
|
-
url:
|
|
108
|
+
url: LOGIN_REDIRECT_PAGE,
|
|
89
109
|
fail: () => {
|
|
90
|
-
void Taro.reLaunch({ url:
|
|
110
|
+
void Taro.reLaunch({ url: LOGIN_REDIRECT_PAGE })
|
|
91
111
|
},
|
|
92
112
|
})
|
|
93
113
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button, Checkbox, Image, Input, Picker, Text, View } from '@tarojs/components'
|
|
2
|
-
import Taro, { useLoad } from '@tarojs/taro'
|
|
2
|
+
import Taro, { useDidShow, useLoad } from '@tarojs/taro'
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import defaultLogo from '@liujitcn/kratos-taro-app-core/static/images/logo_icon.png'
|
|
5
5
|
import { defLoginService } from '../../../api/base/v1/login'
|
|
@@ -8,7 +8,7 @@ import { navigateAppRoute } from '../../../navigation'
|
|
|
8
8
|
import { LoginStatus, type LoginRequest, type LoginResponse } from '../../../rpc/base/v1/login'
|
|
9
9
|
import { useSettingStore, useThemeStore, useUserStore } from '../../../stores'
|
|
10
10
|
import { formatSrc } from '../../../utils'
|
|
11
|
-
import {
|
|
11
|
+
import { prepareLoginPage, restoreLoginRedirect, returnFromLogin } from '../../../utils/navigation'
|
|
12
12
|
import { encryptPassword } from '../../../utils/secretCrypto'
|
|
13
13
|
import { createWebAuthnCredential, getWebAuthnAssertion } from '../../../utils/webauthn'
|
|
14
14
|
import MfaRecoveryCodesDialog from '../../../components/MfaRecoveryCodesDialog'
|
|
@@ -84,11 +84,9 @@ export default function LoginPage() {
|
|
|
84
84
|
const settings = useSettingStore((state) => state.data)
|
|
85
85
|
const themeStore = useThemeStore()
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/** 返回首页并结束本次登录回跳,避免上一页再次要求登录。 */
|
|
88
88
|
const goBack = () => {
|
|
89
|
-
|
|
90
|
-
fail: () => void Taro.reLaunch({ url: homeTabPage }),
|
|
91
|
-
})
|
|
89
|
+
void returnFromLogin()
|
|
92
90
|
}
|
|
93
91
|
const loadSettings = useSettingStore((state) => state.loadData)
|
|
94
92
|
const userStore = useUserStore.getState()
|
|
@@ -281,7 +279,12 @@ export default function LoginPage() {
|
|
|
281
279
|
return settingsPromise.current
|
|
282
280
|
}
|
|
283
281
|
|
|
284
|
-
|
|
282
|
+
const loginRedirect = useRef<string>()
|
|
283
|
+
useLoad((options) => {
|
|
284
|
+
loginRedirect.current = options.redirect
|
|
285
|
+
})
|
|
286
|
+
useDidShow(() => {
|
|
287
|
+
if (!prepareLoginPage(loginRedirect.current)) return
|
|
285
288
|
void loadLoginSettings()
|
|
286
289
|
})
|
|
287
290
|
|