@liujitcn/kratos-taro-app-core 0.0.81 → 0.0.83
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/dist/build.mjs +0 -1
- package/package.json +1 -1
- package/src/components/KratosEmpty.tsx +26 -0
- package/src/components/KratosTabBar.scss +1 -14
- package/src/index.ts +2 -0
- package/src/locales/README.md +1 -1
- package/src/locales/en-US.json +7 -17
- package/src/locales/ja-JP.json +7 -17
- package/src/locales/zh-CN.json +7 -17
- package/src/locales/zh-TW.json +7 -17
- package/src/navigation.ts +42 -16
- package/src/pages.ts +0 -1
- package/src/styles/base.scss +13 -0
- package/src/styles/components.scss +96 -47
- package/src/utils/http.ts +28 -10
- package/src/utils/navigation.ts +26 -6
- package/src/views/pages/index/index.scss +19 -6
- package/src/views/pages/index/index.tsx +3 -6
- package/src/views/pages/login/login.tsx +10 -11
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/dist/build.mjs
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { Text, View } from '@tarojs/components'
|
|
3
|
+
|
|
4
|
+
/** 空态组件属性。 */
|
|
5
|
+
export interface KratosEmptyProps {
|
|
6
|
+
/** 图标节点(如 <UniIcon />),不传则不渲染图标位。 */
|
|
7
|
+
icon?: ReactNode
|
|
8
|
+
/** 主文案。 */
|
|
9
|
+
title?: string
|
|
10
|
+
/** 次级提示文案。 */
|
|
11
|
+
description?: string
|
|
12
|
+
/** 操作区内容(如引导按钮)。 */
|
|
13
|
+
children?: ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 标准空态组件:图标 + 主文案 + 提示文案 + 可选操作区。 */
|
|
17
|
+
export default function KratosEmpty({ icon, title, description, children }: KratosEmptyProps) {
|
|
18
|
+
return (
|
|
19
|
+
<View className='k-empty'>
|
|
20
|
+
{icon && <View className='k-empty__icon'>{icon}</View>}
|
|
21
|
+
{title && <Text className='k-empty__title'>{title}</Text>}
|
|
22
|
+
{description && <Text className='k-empty__desc'>{description}</Text>}
|
|
23
|
+
{children}
|
|
24
|
+
</View>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -30,21 +30,8 @@
|
|
|
30
30
|
line-height: 12PX;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/* 设计稿:激活项顶部霓虹发光条 */
|
|
34
|
-
.kratos-tab-bar__item--active::before {
|
|
35
|
-
position: absolute;
|
|
36
|
-
top: 0;
|
|
37
|
-
left: 50%;
|
|
38
|
-
width: 26px;
|
|
39
|
-
height: 3px;
|
|
40
|
-
border-radius: 3px;
|
|
41
|
-
background: var(--kratos-tab-bar-active-color);
|
|
42
|
-
box-shadow: 0 0 12px var(--kratos-tab-bar-active-color);
|
|
43
|
-
content: '';
|
|
44
|
-
transform: translateX(-50%);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
33
|
.kratos-tab-bar__icon {
|
|
34
|
+
flex-shrink: 0;
|
|
48
35
|
width: 28PX;
|
|
49
36
|
height: 28PX;
|
|
50
37
|
margin-bottom: 4PX;
|
package/src/index.ts
CHANGED
|
@@ -42,3 +42,5 @@ export * from './utils/navigation'
|
|
|
42
42
|
export * from './utils/secretCrypto'
|
|
43
43
|
export { default as PasswordVerifyDialog } from './components/PasswordVerifyDialog'
|
|
44
44
|
export type { PasswordVerifyDialogProps } from './components/PasswordVerifyDialog'
|
|
45
|
+
export { default as KratosEmpty } from './components/KratosEmpty'
|
|
46
|
+
export type { KratosEmptyProps } from './components/KratosEmpty'
|
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",
|
|
@@ -34,18 +29,20 @@
|
|
|
34
29
|
"core.file.temporary_path_required": "Use a temporary file path when uploading from a Mini Program",
|
|
35
30
|
"core.file.upload_failed": "Upload failed",
|
|
36
31
|
"core.home.account_capability": "Account features",
|
|
37
|
-
"core.home.account_capability_description": "
|
|
32
|
+
"core.home.account_capability_description": "Sign-in, profile, multi-factor authentication, and app settings out of the box",
|
|
38
33
|
"core.home.cross_platform": "Cross-platform pages",
|
|
39
34
|
"core.home.cross_platform_description": "One codebase for H5 and WeChat Mini Program",
|
|
40
35
|
"core.home.demo_scope": "Overview",
|
|
36
|
+
"core.home.dynamic_config": "Dynamic config",
|
|
37
|
+
"core.home.dynamic_config_description": "Home logo and tagline configurable from the admin console",
|
|
41
38
|
"core.home.framework": "App framework",
|
|
42
39
|
"core.home.language": "Language",
|
|
43
40
|
"core.home.main_title": "Application Home",
|
|
41
|
+
"core.home.message_center": "Message center",
|
|
42
|
+
"core.home.message_center_description": "Unread messages in one inbox with badge reminders",
|
|
44
43
|
"core.home.state_management": "State management",
|
|
45
|
-
"core.home.static_home": "Static home",
|
|
46
|
-
"core.home.static_home_description": "This page presents framework details without business APIs",
|
|
47
44
|
"core.home.style_solution": "Styling",
|
|
48
|
-
"core.home.sub_title": "
|
|
45
|
+
"core.home.sub_title": "Accounts, messages, and AI assistant in one place",
|
|
49
46
|
"core.home.tech_stack": "Technology stack",
|
|
50
47
|
"core.login.agree_prefix": "I have read and agree to the",
|
|
51
48
|
"core.login.agree_separator": "and",
|
|
@@ -121,12 +118,5 @@
|
|
|
121
118
|
"core.webview.embed_blocked": "The destination may block embedding on H5",
|
|
122
119
|
"core.webview.invalid_url": "No valid link was provided",
|
|
123
120
|
"core.webview.open_failed": "Unable to open link",
|
|
124
|
-
"common.
|
|
125
|
-
"common.resource.default": "{resource}",
|
|
126
|
-
"common.action.delete_resource": "Delete {resource}",
|
|
127
|
-
"common.action.set_field_status": "Set {field}",
|
|
128
|
-
"common.action.load_more": "Load more",
|
|
129
|
-
"common.status.loading": "Loading…",
|
|
130
|
-
"core.navigation.analytics": "Analytics",
|
|
131
|
-
"core.navigation.report": "Reports"
|
|
121
|
+
"common.action.load_more": "Load more"
|
|
132
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 デコードをサポートしていません",
|
|
@@ -34,18 +29,20 @@
|
|
|
34
29
|
"core.file.temporary_path_required": "ミニ プログラムをアップロードするには、一時ファイル パスを使用してください。",
|
|
35
30
|
"core.file.upload_failed": "アップロードに失敗しました",
|
|
36
31
|
"core.home.account_capability": "アカウントの機能",
|
|
37
|
-
"core.home.account_capability_description": "
|
|
32
|
+
"core.home.account_capability_description": "ログイン、プロフィール、多要素認証、アプリ設定を同梱",
|
|
38
33
|
"core.home.cross_platform": "クロスエンド ページ",
|
|
39
34
|
"core.home.cross_platform_description": "同じページ コード セットが H5 および WeChat ミニ プログラムに適応します",
|
|
40
35
|
"core.home.demo_scope": "機能概要",
|
|
36
|
+
"core.home.dynamic_config": "動的設定",
|
|
37
|
+
"core.home.dynamic_config_description": "ホームのロゴとキャッチコピーは管理画面から変更可能",
|
|
41
38
|
"core.home.framework": "アプリケーション フレームワーク",
|
|
42
39
|
"core.home.language": "開発言語",
|
|
43
40
|
"core.home.main_title": "アプリケーションホーム",
|
|
41
|
+
"core.home.message_center": "メッセージセンター",
|
|
42
|
+
"core.home.message_center_description": "未読メッセージを一括管理し、バッジでお知らせ",
|
|
44
43
|
"core.home.state_management": "ステータス管理",
|
|
45
|
-
"core.home.static_home": "静的ホームページ",
|
|
46
|
-
"core.home.static_home_description": "現在のホームページはフレーム情報を表示するために使用されており、ビジネス インターフェイスには依存していません",
|
|
47
44
|
"core.home.style_solution": "スタイル スキーム",
|
|
48
|
-
"core.home.sub_title": "
|
|
45
|
+
"core.home.sub_title": "アカウント・メッセージ・スマートアシスタントをひとつに",
|
|
49
46
|
"core.home.tech_stack": "現在のテクノロジースタック",
|
|
50
47
|
"core.login.agree_prefix": "読んで同意します",
|
|
51
48
|
"core.login.agree_separator": "および",
|
|
@@ -121,12 +118,5 @@
|
|
|
121
118
|
"core.webview.embed_blocked": "現在の H5 ページは、ターゲット サイトによって埋め込みが制限されている可能性があります",
|
|
122
119
|
"core.webview.invalid_url": "有効なリンク アドレスがありません",
|
|
123
120
|
"core.webview.open_failed": "リンクを開けません",
|
|
124
|
-
"common.
|
|
125
|
-
"common.resource.default": "{resource}",
|
|
126
|
-
"common.action.delete_resource": "{resource}を削除",
|
|
127
|
-
"common.action.set_field_status": "{field}を設定",
|
|
128
|
-
"common.action.load_more": "さらに読み込む",
|
|
129
|
-
"common.status.loading": "読み込み中…",
|
|
130
|
-
"core.navigation.analytics": "分析",
|
|
131
|
-
"core.navigation.report": "レポート"
|
|
121
|
+
"common.action.load_more": "さらに読み込む"
|
|
132
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 解码",
|
|
@@ -34,18 +29,20 @@
|
|
|
34
29
|
"core.file.temporary_path_required": "小程序端请使用文件临时路径上传",
|
|
35
30
|
"core.file.upload_failed": "上传失败",
|
|
36
31
|
"core.home.account_capability": "账户能力",
|
|
37
|
-
"core.home.account_capability_description": "
|
|
32
|
+
"core.home.account_capability_description": "登录、个人资料、多因素认证与应用设置开箱即用",
|
|
38
33
|
"core.home.cross_platform": "跨端页面",
|
|
39
34
|
"core.home.cross_platform_description": "同一套页面代码适配 H5 和微信小程序",
|
|
40
35
|
"core.home.demo_scope": "功能概览",
|
|
36
|
+
"core.home.dynamic_config": "动态配置",
|
|
37
|
+
"core.home.dynamic_config_description": "首页 Logo 与标语支持后台远程配置",
|
|
41
38
|
"core.home.framework": "应用框架",
|
|
42
39
|
"core.home.language": "开发语言",
|
|
43
40
|
"core.home.main_title": "应用首页",
|
|
41
|
+
"core.home.message_center": "消息中心",
|
|
42
|
+
"core.home.message_center_description": "未读消息统一收纳,红点提醒不错过",
|
|
44
43
|
"core.home.state_management": "状态管理",
|
|
45
|
-
"core.home.static_home": "静态首页",
|
|
46
|
-
"core.home.static_home_description": "当前首页用于展示框架信息,不依赖业务接口",
|
|
47
44
|
"core.home.style_solution": "样式方案",
|
|
48
|
-
"core.home.sub_title": "
|
|
45
|
+
"core.home.sub_title": "账号、消息与智能助手,一站触达",
|
|
49
46
|
"core.home.tech_stack": "当前技术栈",
|
|
50
47
|
"core.login.agree_prefix": "我已阅读并同意",
|
|
51
48
|
"core.login.agree_separator": "和",
|
|
@@ -121,12 +118,5 @@
|
|
|
121
118
|
"core.webview.embed_blocked": "当前 H5 页面可能被目标站点限制嵌入",
|
|
122
119
|
"core.webview.invalid_url": "缺少有效链接地址",
|
|
123
120
|
"core.webview.open_failed": "链接无法打开",
|
|
124
|
-
"common.
|
|
125
|
-
"common.resource.default": "{resource}",
|
|
126
|
-
"common.action.delete_resource": "删除{resource}",
|
|
127
|
-
"common.action.set_field_status": "设置{field}",
|
|
128
|
-
"common.action.load_more": "加载更多",
|
|
129
|
-
"common.status.loading": "加载中…",
|
|
130
|
-
"core.navigation.analytics": "分析",
|
|
131
|
-
"core.navigation.report": "报表"
|
|
121
|
+
"common.action.load_more": "加载更多"
|
|
132
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 解碼",
|
|
@@ -34,18 +29,20 @@
|
|
|
34
29
|
"core.file.temporary_path_required": "小程式端請使用檔案臨時路徑上傳",
|
|
35
30
|
"core.file.upload_failed": "上傳失敗",
|
|
36
31
|
"core.home.account_capability": "賬戶能力",
|
|
37
|
-
"core.home.account_capability_description": "
|
|
32
|
+
"core.home.account_capability_description": "登入、個人資料、多因素認證與應用設定開箱即用",
|
|
38
33
|
"core.home.cross_platform": "跨端頁面",
|
|
39
34
|
"core.home.cross_platform_description": "同一套頁面程式碼適配 H5 和微信小程式",
|
|
40
35
|
"core.home.demo_scope": "功能概覽",
|
|
36
|
+
"core.home.dynamic_config": "動態設定",
|
|
37
|
+
"core.home.dynamic_config_description": "首頁 Logo 與標語支援後台遠端設定",
|
|
41
38
|
"core.home.framework": "應用框架",
|
|
42
39
|
"core.home.language": "開發語言",
|
|
43
40
|
"core.home.main_title": "應用首頁",
|
|
41
|
+
"core.home.message_center": "訊息中心",
|
|
42
|
+
"core.home.message_center_description": "未讀訊息統一查看,紅點提醒不錯過",
|
|
44
43
|
"core.home.state_management": "狀態管理",
|
|
45
|
-
"core.home.static_home": "靜態首頁",
|
|
46
|
-
"core.home.static_home_description": "當前首頁用於展示框架資訊,不依賴業務介面",
|
|
47
44
|
"core.home.style_solution": "樣式方案",
|
|
48
|
-
"core.home.sub_title": "
|
|
45
|
+
"core.home.sub_title": "帳號、訊息與智慧助手,一站觸達",
|
|
49
46
|
"core.home.tech_stack": "當前技術棧",
|
|
50
47
|
"core.login.agree_prefix": "我已閱讀並同意",
|
|
51
48
|
"core.login.agree_separator": "和",
|
|
@@ -121,12 +118,5 @@
|
|
|
121
118
|
"core.webview.embed_blocked": "當前 H5 頁面可能被目標站點限制嵌入",
|
|
122
119
|
"core.webview.invalid_url": "缺少有效連結地址",
|
|
123
120
|
"core.webview.open_failed": "連結無法開啟",
|
|
124
|
-
"common.
|
|
125
|
-
"common.resource.default": "{resource}",
|
|
126
|
-
"common.action.delete_resource": "刪除{resource}",
|
|
127
|
-
"common.action.set_field_status": "設定{field}",
|
|
128
|
-
"common.action.load_more": "載入更多",
|
|
129
|
-
"common.status.loading": "載入中…",
|
|
130
|
-
"core.navigation.analytics": "分析",
|
|
131
|
-
"core.navigation.report": "報表"
|
|
121
|
+
"common.action.load_more": "載入更多"
|
|
132
122
|
}
|
package/src/navigation.ts
CHANGED
|
@@ -101,6 +101,16 @@ export const defaultAppMenus: AppMenu[] = [
|
|
|
101
101
|
icon: 'MESSAGE_DEFAULT',
|
|
102
102
|
selectedIcon: 'MESSAGE_SELECTED',
|
|
103
103
|
},
|
|
104
|
+
{
|
|
105
|
+
id: 99050100,
|
|
106
|
+
parentId: 99040000,
|
|
107
|
+
name: 'AppMessageDetail',
|
|
108
|
+
path: 'app/message/detail',
|
|
109
|
+
viewKey: 'MESSAGE_DETAIL',
|
|
110
|
+
title: '',
|
|
111
|
+
access: 'AUTHENTICATED',
|
|
112
|
+
inTabBar: false,
|
|
113
|
+
},
|
|
104
114
|
{
|
|
105
115
|
id: 99090000,
|
|
106
116
|
parentId: APP_MENU_ROOT_ID,
|
|
@@ -222,8 +232,9 @@ export async function initializeAppNavigation(): Promise<void> {
|
|
|
222
232
|
const cached = readCachedMenus(cacheKey)
|
|
223
233
|
useAppNavigation.setState(createNavigationState(filterUnavailableMenus(cached ?? localizedDefaultAppMenus()), false))
|
|
224
234
|
try {
|
|
225
|
-
const nextMenus =
|
|
226
|
-
|
|
235
|
+
const nextMenus = validateMenus(
|
|
236
|
+
filterUnavailableMenus(normalizeMenuResponse(await adapter.list())),
|
|
237
|
+
)
|
|
227
238
|
Taro.setStorageSync(cacheKey, nextMenus)
|
|
228
239
|
useAppNavigation.setState(createNavigationState(nextMenus, true))
|
|
229
240
|
} catch (error) {
|
|
@@ -240,8 +251,7 @@ function filterUnavailableMenus(nextMenus: AppMenu[]): AppMenu[] {
|
|
|
240
251
|
|
|
241
252
|
/** 使用已经校验的整份配置进行原子切换。 */
|
|
242
253
|
export function installAppNavigation(nextMenus: AppMenu[]): void {
|
|
243
|
-
validateMenus(nextMenus)
|
|
244
|
-
const snapshot = nextMenus.map((menu) => ({ ...menu }))
|
|
254
|
+
const snapshot = validateMenus(nextMenus).map((menu) => ({ ...menu }))
|
|
245
255
|
Taro.setStorageSync(resolveCacheKey(), snapshot)
|
|
246
256
|
useAppNavigation.setState(createNavigationState(snapshot, true))
|
|
247
257
|
}
|
|
@@ -382,9 +392,16 @@ function readCachedMenus(cacheKey: string): AppMenu[] | undefined {
|
|
|
382
392
|
const cached = Taro.getStorageSync<unknown>(cacheKey)
|
|
383
393
|
if (!Array.isArray(cached)) return
|
|
384
394
|
try {
|
|
385
|
-
const cachedMenus = cached as AppMenu[]
|
|
386
|
-
|
|
387
|
-
|
|
395
|
+
const cachedMenus = (cached as AppMenu[]).map((menu) => ({
|
|
396
|
+
...menu,
|
|
397
|
+
title:
|
|
398
|
+
menu.viewKey === 'HOME'
|
|
399
|
+
? t('core.navigation.home')
|
|
400
|
+
: menu.viewKey === 'MESSAGE_INBOX'
|
|
401
|
+
? t('core.navigation.message')
|
|
402
|
+
: menu.title,
|
|
403
|
+
}))
|
|
404
|
+
return validateMenus(cachedMenus)
|
|
388
405
|
} catch {
|
|
389
406
|
Taro.removeStorageSync(cacheKey)
|
|
390
407
|
}
|
|
@@ -403,7 +420,12 @@ function normalizeMenu(item: BaseMenu): AppMenu {
|
|
|
403
420
|
name: item.name,
|
|
404
421
|
path: item.path,
|
|
405
422
|
viewKey: app?.view_key ?? '',
|
|
406
|
-
title:
|
|
423
|
+
title:
|
|
424
|
+
app?.view_key === 'HOME'
|
|
425
|
+
? t('core.navigation.home')
|
|
426
|
+
: app?.view_key === 'MESSAGE_INBOX'
|
|
427
|
+
? t('core.navigation.message')
|
|
428
|
+
: meta?.title ?? '',
|
|
407
429
|
access: (app?.access ?? 'AUTHENTICATED') as AppMenuAccess,
|
|
408
430
|
inTabBar: app?.in_tab_bar ?? false,
|
|
409
431
|
icon: meta?.icon,
|
|
@@ -411,11 +433,17 @@ function normalizeMenu(item: BaseMenu): AppMenu {
|
|
|
411
433
|
}
|
|
412
434
|
}
|
|
413
435
|
|
|
414
|
-
function validateMenus(nextMenus: AppMenu[]):
|
|
436
|
+
function validateMenus(nextMenus: AppMenu[]): AppMenu[] {
|
|
437
|
+
// 多端共用后端时菜单可能携带本应用未注册的视图键,过滤跳过而不是让整份菜单失效。
|
|
438
|
+
const knownMenus = nextMenus.filter((menu) => {
|
|
439
|
+
if (resolveStaticView(menu.viewKey)) return true
|
|
440
|
+
console.warn(t('core.navigation.error.view_key', { viewKey: menu.viewKey }))
|
|
441
|
+
return false
|
|
442
|
+
})
|
|
415
443
|
const ids = new Set<number>()
|
|
416
444
|
const names = new Set<string>()
|
|
417
445
|
const paths = new Set<string>()
|
|
418
|
-
for (const menu of
|
|
446
|
+
for (const menu of knownMenus) {
|
|
419
447
|
if (!Number.isSafeInteger(menu.id) || !menu.name || !menu.path.startsWith('app/')) {
|
|
420
448
|
throw new Error(t('core.navigation.error.identity'))
|
|
421
449
|
}
|
|
@@ -425,9 +453,6 @@ function validateMenus(nextMenus: AppMenu[]): void {
|
|
|
425
453
|
if (!['PUBLIC', 'GUEST_ONLY', 'AUTHENTICATED'].includes(menu.access)) {
|
|
426
454
|
throw new Error(t('core.navigation.error.access', { access: menu.access }))
|
|
427
455
|
}
|
|
428
|
-
if (!resolveStaticView(menu.viewKey)) {
|
|
429
|
-
throw new Error(t('core.navigation.error.view_key', { viewKey: menu.viewKey }))
|
|
430
|
-
}
|
|
431
456
|
if (ids.has(menu.id) || names.has(menu.name) || paths.has(menu.path)) {
|
|
432
457
|
throw new Error(t('core.navigation.error.unique'))
|
|
433
458
|
}
|
|
@@ -435,8 +460,8 @@ function validateMenus(nextMenus: AppMenu[]): void {
|
|
|
435
460
|
names.add(menu.name)
|
|
436
461
|
paths.add(menu.path)
|
|
437
462
|
}
|
|
438
|
-
const menuMap = new Map(
|
|
439
|
-
for (const menu of
|
|
463
|
+
const menuMap = new Map(knownMenus.map((menu) => [menu.id, menu]))
|
|
464
|
+
for (const menu of knownMenus) {
|
|
440
465
|
if (menu.parentId === undefined) {
|
|
441
466
|
throw new Error(t('core.navigation.error.parent_missing', { name: menu.name }))
|
|
442
467
|
}
|
|
@@ -457,6 +482,7 @@ function validateMenus(nextMenus: AppMenu[]): void {
|
|
|
457
482
|
parentId = parent.parentId
|
|
458
483
|
}
|
|
459
484
|
}
|
|
460
|
-
const tabCount =
|
|
485
|
+
const tabCount = knownMenus.filter((menu) => menu.parentId === APP_MENU_ROOT_ID).length
|
|
461
486
|
if (tabCount === 1 || tabCount > 5) throw new Error(t('core.navigation.error.tab_count'))
|
|
487
|
+
return knownMenus
|
|
462
488
|
}
|
package/src/pages.ts
CHANGED
package/src/styles/base.scss
CHANGED
|
@@ -131,3 +131,16 @@ html.theme-dark .taro__toast > div:last-child {
|
|
|
131
131
|
color: var(--kratos-color-text) !important;
|
|
132
132
|
}
|
|
133
133
|
/* #endif */
|
|
134
|
+
|
|
135
|
+
/* H5 隐藏滚动条但保留滚动能力,视觉对齐小程序;小程序端滚动条本就不存在,无副作用。 */
|
|
136
|
+
/* #ifdef h5 */
|
|
137
|
+
::-webkit-scrollbar {
|
|
138
|
+
width: 0;
|
|
139
|
+
height: 0;
|
|
140
|
+
display: none;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
* {
|
|
144
|
+
scrollbar-width: none;
|
|
145
|
+
}
|
|
146
|
+
/* #endif */
|
|
@@ -19,40 +19,6 @@
|
|
|
19
19
|
box-shadow: var(--kratos-glow-soft);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/* 四角 L 形角标:渲染为真实 view 节点(weapp 伪元素仅支持 view) */
|
|
23
|
-
.k-cnr {
|
|
24
|
-
position: absolute;
|
|
25
|
-
width: 24px;
|
|
26
|
-
height: 24px;
|
|
27
|
-
border: 3px solid var(--kratos-color-primary);
|
|
28
|
-
opacity: 0.5;
|
|
29
|
-
pointer-events: none;
|
|
30
|
-
}
|
|
31
|
-
.k-cnr--tl {
|
|
32
|
-
top: 16px;
|
|
33
|
-
left: 16px;
|
|
34
|
-
border-right: 0;
|
|
35
|
-
border-bottom: 0;
|
|
36
|
-
}
|
|
37
|
-
.k-cnr--tr {
|
|
38
|
-
top: 16px;
|
|
39
|
-
right: 16px;
|
|
40
|
-
border-left: 0;
|
|
41
|
-
border-bottom: 0;
|
|
42
|
-
}
|
|
43
|
-
.k-cnr--bl {
|
|
44
|
-
bottom: 16px;
|
|
45
|
-
left: 16px;
|
|
46
|
-
border-right: 0;
|
|
47
|
-
border-top: 0;
|
|
48
|
-
}
|
|
49
|
-
.k-cnr--br {
|
|
50
|
-
bottom: 16px;
|
|
51
|
-
right: 16px;
|
|
52
|
-
border-left: 0;
|
|
53
|
-
border-top: 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
22
|
/* 装饰网格背景 */
|
|
57
23
|
.k-grid-bg {
|
|
58
24
|
position: absolute;
|
|
@@ -181,31 +147,44 @@
|
|
|
181
147
|
max-width: calc(100% - 190px);
|
|
182
148
|
overflow: hidden;
|
|
183
149
|
color: var(--kratos-color-text);
|
|
184
|
-
font-size:
|
|
185
|
-
font-weight:
|
|
150
|
+
font-size: 16PX;
|
|
151
|
+
font-weight: 700;
|
|
186
152
|
text-overflow: ellipsis;
|
|
187
153
|
transform: translateX(-50%);
|
|
188
154
|
white-space: nowrap;
|
|
189
155
|
}
|
|
190
156
|
.k-top {
|
|
157
|
+
position: relative;
|
|
191
158
|
display: flex;
|
|
192
159
|
align-items: center;
|
|
193
|
-
|
|
160
|
+
/* 标题绝对居中后,流内子元素(右侧动作按钮)统一靠右排布 */
|
|
161
|
+
justify-content: flex-end;
|
|
194
162
|
gap: 16px;
|
|
195
163
|
/* weapp 原生胶囊避让:页面通过 getMpCapsuleRightGap() 写入该变量,
|
|
196
164
|
其余平台未设置时回退 0,不影响布局。 */
|
|
197
165
|
padding-right: var(--mp-capsule-gap, 0px);
|
|
198
166
|
}
|
|
167
|
+
/* #ifdef weapp */
|
|
168
|
+
/* 双写类名提高优先级:页面级 padding 简写(如 .message-header)不得压掉导航带 */
|
|
169
|
+
.k-top.k-top {
|
|
170
|
+
/* 小程序右上角是原生胶囊:操作按钮靠左,标题行与胶囊同行(同 k-page-nav--weapp 几何) */
|
|
171
|
+
justify-content: flex-start;
|
|
172
|
+
height: calc(88px + 44PX);
|
|
173
|
+
padding-top: 44PX;
|
|
174
|
+
padding-right: 0;
|
|
175
|
+
}
|
|
176
|
+
/* #endif */
|
|
199
177
|
.k-top__title {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
178
|
+
position: absolute;
|
|
179
|
+
left: 50%;
|
|
180
|
+
max-width: calc(100% - 190px);
|
|
181
|
+
overflow: hidden;
|
|
203
182
|
color: var(--kratos-color-text);
|
|
204
|
-
|
|
205
|
-
.k-top__t2 {
|
|
206
|
-
font-size: 32px;
|
|
183
|
+
font-size: 16PX;
|
|
207
184
|
font-weight: 700;
|
|
208
|
-
|
|
185
|
+
text-overflow: ellipsis;
|
|
186
|
+
transform: translateX(-50%);
|
|
187
|
+
white-space: nowrap;
|
|
209
188
|
}
|
|
210
189
|
|
|
211
190
|
/* 方形图标按钮 + 角标 */
|
|
@@ -306,12 +285,20 @@
|
|
|
306
285
|
box-shadow: var(--kratos-glow-soft);
|
|
307
286
|
}
|
|
308
287
|
|
|
309
|
-
/*
|
|
288
|
+
/* 过滤芯片:单行横向滚动,超出隐藏滚动条 */
|
|
310
289
|
.k-chips {
|
|
311
290
|
display: flex;
|
|
312
|
-
flex-wrap:
|
|
291
|
+
flex-wrap: nowrap;
|
|
313
292
|
gap: 16px;
|
|
314
293
|
margin-bottom: 24px;
|
|
294
|
+
overflow-x: auto;
|
|
295
|
+
scrollbar-width: none;
|
|
296
|
+
}
|
|
297
|
+
.k-chips::-webkit-scrollbar {
|
|
298
|
+
display: none;
|
|
299
|
+
}
|
|
300
|
+
.k-chips .k-ch {
|
|
301
|
+
flex: 0 0 auto;
|
|
315
302
|
}
|
|
316
303
|
.k-ch {
|
|
317
304
|
display: inline-flex;
|
|
@@ -437,6 +424,8 @@
|
|
|
437
424
|
align-items: center;
|
|
438
425
|
justify-content: center;
|
|
439
426
|
gap: 16px;
|
|
427
|
+
/* 各端 H5 原生 button 默认宽度不一,显式通栏保证双端一致 */
|
|
428
|
+
width: 100%;
|
|
440
429
|
height: 100px;
|
|
441
430
|
margin-top: 36px;
|
|
442
431
|
border-radius: 20px;
|
|
@@ -734,15 +723,30 @@
|
|
|
734
723
|
background: var(--kratos-color-primary-soft);
|
|
735
724
|
}
|
|
736
725
|
.k-msg-item__title {
|
|
726
|
+
display: -webkit-box;
|
|
727
|
+
overflow: hidden;
|
|
737
728
|
font-size: 25px;
|
|
738
729
|
font-weight: 700;
|
|
739
730
|
color: var(--kratos-color-text);
|
|
731
|
+
-webkit-box-orient: vertical;
|
|
732
|
+
-webkit-line-clamp: 2;
|
|
733
|
+
word-break: break-word;
|
|
740
734
|
}
|
|
741
|
-
.k-msg-
|
|
735
|
+
.k-msg-item__foot {
|
|
736
|
+
display: flex;
|
|
737
|
+
align-items: center;
|
|
738
|
+
gap: 24px;
|
|
742
739
|
margin-top: 10px;
|
|
740
|
+
}
|
|
741
|
+
.k-msg-item__meta {
|
|
742
|
+
flex: 1;
|
|
743
|
+
min-width: 0;
|
|
744
|
+
overflow: hidden;
|
|
743
745
|
font-size: 20px;
|
|
744
746
|
color: var(--kratos-color-text-muted);
|
|
745
747
|
font-family: var(--kratos-font-mono);
|
|
748
|
+
text-overflow: ellipsis;
|
|
749
|
+
white-space: nowrap;
|
|
746
750
|
}
|
|
747
751
|
.k-msg-item__time {
|
|
748
752
|
flex-shrink: 0;
|
|
@@ -763,3 +767,48 @@
|
|
|
763
767
|
color: var(--kratos-color-primary);
|
|
764
768
|
font-family: var(--kratos-font-mono);
|
|
765
769
|
}
|
|
770
|
+
|
|
771
|
+
/* ============================================================
|
|
772
|
+
* 标准空态词汇(列表无数据时统一使用)
|
|
773
|
+
* ============================================================ */
|
|
774
|
+
.k-empty {
|
|
775
|
+
display: flex;
|
|
776
|
+
flex-direction: column;
|
|
777
|
+
align-items: center;
|
|
778
|
+
padding: 140px 32px;
|
|
779
|
+
}
|
|
780
|
+
.k-empty__icon {
|
|
781
|
+
display: flex;
|
|
782
|
+
align-items: center;
|
|
783
|
+
justify-content: center;
|
|
784
|
+
width: 112px;
|
|
785
|
+
height: 112px;
|
|
786
|
+
border-radius: 26px;
|
|
787
|
+
border: 1PX solid var(--kratos-color-border-accent);
|
|
788
|
+
background: var(--kratos-color-primary-soft-2);
|
|
789
|
+
color: var(--kratos-color-primary);
|
|
790
|
+
box-shadow: var(--kratos-glow-soft);
|
|
791
|
+
}
|
|
792
|
+
.k-empty__title {
|
|
793
|
+
margin-top: 28px;
|
|
794
|
+
color: var(--kratos-color-text);
|
|
795
|
+
font-size: 27px;
|
|
796
|
+
font-weight: 700;
|
|
797
|
+
}
|
|
798
|
+
.k-empty__desc {
|
|
799
|
+
margin-top: 12px;
|
|
800
|
+
color: var(--kratos-color-text-muted);
|
|
801
|
+
font-size: 22px;
|
|
802
|
+
font-family: var(--kratos-font-mono);
|
|
803
|
+
text-align: center;
|
|
804
|
+
}
|
|
805
|
+
.k-empty__act {
|
|
806
|
+
margin-top: 32px;
|
|
807
|
+
padding: 16px 40px;
|
|
808
|
+
border-radius: 999px;
|
|
809
|
+
border: 1PX solid var(--kratos-color-border-accent);
|
|
810
|
+
background: var(--kratos-color-primary-soft-2);
|
|
811
|
+
color: var(--kratos-color-primary);
|
|
812
|
+
font-size: 24px;
|
|
813
|
+
font-family: var(--kratos-font-mono);
|
|
814
|
+
}
|
package/src/utils/http.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Taro from '@tarojs/taro'
|
|
1
|
+
import Taro, { getCurrentPages } from '@tarojs/taro'
|
|
2
2
|
import {
|
|
3
3
|
clearToken,
|
|
4
4
|
getRefreshToken,
|
|
@@ -262,23 +262,41 @@ async function refreshAccessToken(): Promise<void> {
|
|
|
262
262
|
setTokenExpiresIn(data.expires_in)
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
/** 会话过期弹窗等待时长:H5 端路由切换会自动隐藏模态且 Promise 永不结算,超时释放避免提示永久卡死。 */
|
|
266
|
+
const RELOGIN_PROMPT_TIMEOUT = 60_000
|
|
267
|
+
|
|
268
|
+
/** 提示会话过期并进入携带本次业务回跳标记的登录页。 */
|
|
265
269
|
async function promptRelogin(): Promise<void> {
|
|
270
|
+
// 401 来自旧会话的在途请求而当前已是有效登录态时直接忽略,避免刚登录成功被弹窗打断。
|
|
271
|
+
if (hasValidToken()) return
|
|
272
|
+
const pages = getCurrentPages()
|
|
273
|
+
// 登录页自身的请求失效只清理残留登录态,避免确认后重载登录页丢失已填写的表单。
|
|
274
|
+
if (pages[pages.length - 1]?.route === 'pages/login/login') {
|
|
275
|
+
if (getToken() || getRefreshToken() || Taro.getStorageSync('user')) silentClearAuthData()
|
|
276
|
+
return
|
|
277
|
+
}
|
|
266
278
|
if (isPromptingRelogin) return
|
|
267
279
|
isPromptingRelogin = true
|
|
268
280
|
try {
|
|
269
281
|
const theme = useThemeStore.getState().theme
|
|
270
|
-
const modal = await
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
282
|
+
const modal = await Promise.race([
|
|
283
|
+
// 按钮文案复用通用确认键:平台对 confirmText 限制 4 个汉字/8 字符,长文案会被整体拒绝。
|
|
284
|
+
Taro.showModal({
|
|
285
|
+
title: t('common.title.notice'),
|
|
286
|
+
content: t('core.auth.session_expired'),
|
|
287
|
+
showCancel: false,
|
|
288
|
+
confirmText: t('common.action.confirm'),
|
|
289
|
+
confirmColor: theme === 'light' ? '#039e8c' : '#28f0d4',
|
|
290
|
+
}).catch(() => undefined as undefined),
|
|
291
|
+
new Promise<undefined>((resolve) =>
|
|
292
|
+
setTimeout(() => resolve(undefined), RELOGIN_PROMPT_TIMEOUT),
|
|
293
|
+
),
|
|
294
|
+
])
|
|
295
|
+
if (!modal?.confirm) return
|
|
278
296
|
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
279
297
|
silentClearAuthData()
|
|
280
298
|
saveCurrentRoute()
|
|
281
|
-
await Taro.reLaunch({ url: '/pages/login/login' })
|
|
299
|
+
await Taro.reLaunch({ url: '/pages/login/login?redirect=1' })
|
|
282
300
|
} finally {
|
|
283
301
|
isPromptingRelogin = false
|
|
284
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
|
}
|
|
@@ -10,8 +10,21 @@ page {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.home-page--weapp .home-content {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
padding-top: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* 小程序端首页标题占导航带:与 k-page-nav--weapp 同几何,标题与胶囊同行 */
|
|
17
|
+
.home-page--weapp .home-top {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
height: calc(88px + 44PX);
|
|
20
|
+
padding: 44PX 4px 0;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.home-page--weapp .home-top__title {
|
|
27
|
+
margin-top: 0;
|
|
15
28
|
}
|
|
16
29
|
|
|
17
30
|
.home-content {
|
|
@@ -24,13 +37,13 @@ page {
|
|
|
24
37
|
padding: 8px 4px 24px;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
/*
|
|
40
|
+
/* 页面大标题:与其他页面导航标题同规格,向原生导航栏看齐 */
|
|
28
41
|
.home-top__title {
|
|
29
42
|
margin-top: 14px;
|
|
30
43
|
color: var(--kratos-color-text);
|
|
31
|
-
font-size:
|
|
32
|
-
font-weight:
|
|
33
|
-
|
|
44
|
+
font-size: 16PX;
|
|
45
|
+
font-weight: 700;
|
|
46
|
+
text-align: center;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
.home-hero {
|
|
@@ -13,7 +13,7 @@ export default function HomePage() {
|
|
|
13
13
|
const themeStore = useThemeStore()
|
|
14
14
|
const settings = useSettingStore((state) => state.data)
|
|
15
15
|
const loadData = useSettingStore((state) => state.loadData)
|
|
16
|
-
const mainTitle =
|
|
16
|
+
const mainTitle = t('core.navigation.home')
|
|
17
17
|
const subTitle = settings?.get('subTitle') || t('core.home.sub_title')
|
|
18
18
|
const configuredAppLogo = settings?.get('appLogo')
|
|
19
19
|
const configuredLogoUrl = configuredAppLogo ? formatSrc(configuredAppLogo) : defaultLogo
|
|
@@ -36,7 +36,8 @@ export default function HomePage() {
|
|
|
36
36
|
const demos = [
|
|
37
37
|
[t('core.home.cross_platform'), t('core.home.cross_platform_description')],
|
|
38
38
|
[t('core.home.account_capability'), t('core.home.account_capability_description')],
|
|
39
|
-
[t('core.home.
|
|
39
|
+
[t('core.home.message_center'), t('core.home.message_center_description')],
|
|
40
|
+
[t('core.home.dynamic_config'), t('core.home.dynamic_config_description')],
|
|
40
41
|
]
|
|
41
42
|
return (
|
|
42
43
|
<View
|
|
@@ -51,10 +52,6 @@ export default function HomePage() {
|
|
|
51
52
|
</View>
|
|
52
53
|
|
|
53
54
|
<View className='k-glass k-glass--lit home-hero'>
|
|
54
|
-
<View className='k-cnr k-cnr--tl' aria-hidden />
|
|
55
|
-
<View className='k-cnr k-cnr--tr' aria-hidden />
|
|
56
|
-
<View className='k-cnr k-cnr--bl' aria-hidden />
|
|
57
|
-
<View className='k-cnr k-cnr--br' aria-hidden />
|
|
58
55
|
<View className='home-hero__main'>
|
|
59
56
|
<Image
|
|
60
57
|
className='home-logo'
|
|
@@ -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
|
|
|
@@ -639,10 +642,6 @@ export default function LoginPage() {
|
|
|
639
642
|
</View>
|
|
640
643
|
</View>
|
|
641
644
|
<View className='k-glass k-glass--lit login-panel'>
|
|
642
|
-
<View className='k-cnr k-cnr--tl' aria-hidden />
|
|
643
|
-
<View className='k-cnr k-cnr--tr' aria-hidden />
|
|
644
|
-
<View className='k-cnr k-cnr--bl' aria-hidden />
|
|
645
|
-
<View className='k-cnr k-cnr--br' aria-hidden />
|
|
646
645
|
{process.env.TARO_ENV === 'h5' ? (
|
|
647
646
|
<View className='login-form'>
|
|
648
647
|
{showTenantCode ? (
|