@liujitcn/kratos-uni-app-core 0.0.29 → 0.0.32
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 +30 -7
- package/dist/module.mjs +164 -16
- package/dist/vite.mjs +4 -5
- package/package.json +3 -1
- package/src/api/base/{config.ts → v1/config.ts} +6 -2
- package/src/api/base/{file.ts → v1/file.ts} +6 -2
- package/src/api/base/{language.ts → v1/language.ts} +2 -2
- package/src/api/base/{login.ts → v1/login.ts} +3 -3
- package/src/api/base/v1/mfa.ts +127 -0
- package/src/api/base/{oauth.ts → v1/oauth.ts} +3 -3
- package/src/api/system/{auth.ts → app/v1/auth.ts} +8 -15
- package/src/api/system/{base_area.ts → app/v1/base_area.ts} +2 -2
- package/src/api/system/app/v1/base_dict.ts +21 -0
- package/src/api/{base/menu.ts → system/app/v1/base_menu.ts} +2 -2
- package/src/bootstrap.ts +1 -1
- package/src/components/KratosSettingsPage.vue +292 -0
- package/src/components/KratosTabBar.vue +71 -1
- package/src/components/MfaManageDialog.vue +469 -0
- package/src/components/MfaRecoveryCodesDialog.vue +192 -0
- package/src/components/MfaSetupPanel.vue +160 -0
- package/src/components/PasswordVerifyDialog.vue +277 -0
- package/src/index.ts +2 -0
- package/src/locales/README.md +2 -1
- package/src/locales/en-US.json +41 -4
- package/src/locales/index.ts +10 -1
- package/src/locales/ja-JP.json +41 -4
- package/src/locales/zh-CN.json +41 -4
- package/src/locales/zh-TW.json +41 -4
- package/src/navigation.ts +115 -53
- package/src/rpc/base/v1/login.ts +37 -4
- package/src/rpc/base/v1/mfa.ts +180 -0
- package/src/rpc/base/v1/oauth.ts +25 -0
- package/src/rpc/system/app/v1/auth.ts +7 -0
- package/src/rpc/system/common/v1/common.ts +35 -0
- package/src/stores/modules/setting.ts +1 -1
- package/src/stores/modules/user.ts +29 -10
- package/src/utils/clipboard.ts +33 -0
- package/src/utils/http.ts +10 -13
- package/src/utils/passwordCrypto.ts +1 -1
- package/src/utils/webauthn.ts +101 -0
- package/src/views/pages/login/login.vue +299 -13
- package/src/vite.ts +4 -7
- package/src/api/system/base_dict.ts +0 -24
package/src/locales/zh-CN.json
CHANGED
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"core.crypto.public_key_invalid": "公钥格式无效",
|
|
29
29
|
"core.crypto.random_failed": "随机数生成失败",
|
|
30
30
|
"core.crypto.unsupported": "当前环境不支持密码加密",
|
|
31
|
+
"core.password.verify_title": "验证密码",
|
|
32
|
+
"core.password.verify_label": "当前密码",
|
|
31
33
|
"core.file.upload_failed": "上传失败",
|
|
32
34
|
"core.home.account_capability": "账户能力",
|
|
33
35
|
"core.home.account_capability_description": "个人资料、应用设置与智能助手入口已保留",
|
|
@@ -58,6 +60,23 @@
|
|
|
58
60
|
"core.login.captcha_write_failed": "验证码图片写入失败",
|
|
59
61
|
"core.login.default_sub_title": "欢迎使用本应用",
|
|
60
62
|
"core.login.login_success": "登录成功",
|
|
63
|
+
"core.login.mfa_title": "多因素认证",
|
|
64
|
+
"core.login.mfa_code": "请输入动态口令",
|
|
65
|
+
"core.login.mfa_recovery_code": "恢复码(可选)",
|
|
66
|
+
"core.login.mfa_setup_title": "绑定多因素认证",
|
|
67
|
+
"core.login.mfa_copy_uri": "复制绑定地址",
|
|
68
|
+
"core.login.mfa_copy_success": "绑定地址已复制",
|
|
69
|
+
"core.login.mfa_recovery_codes_copied": "恢复码已复制",
|
|
70
|
+
"core.login.mfa_recovery_codes_title": "保存 MFA 恢复码",
|
|
71
|
+
"core.login.mfa_recovery_codes_warning": "每个恢复码只能使用一次,此弹窗关闭后不会再次显示。",
|
|
72
|
+
"core.login.mfa_copy_recovery_codes": "复制恢复码",
|
|
73
|
+
"core.login.mfa_recovery_codes_confirm": "我已保存",
|
|
74
|
+
"core.login.mfa_setup_success": "多因素认证绑定成功,请重新登录。",
|
|
75
|
+
"core.login.mfa_webauthn_action": "使用 Passkey",
|
|
76
|
+
"core.login.mfa_webauthn_setup_unsupported": "当前平台不支持 WebAuthn,请使用 TOTP 或恢复码。",
|
|
77
|
+
"core.login.mfa_webauthn_setup_invalid": "WebAuthn 注册未返回有效凭据。",
|
|
78
|
+
"core.login.mfa_webauthn_login_unsupported": "当前平台不支持 WebAuthn,请使用恢复码。",
|
|
79
|
+
"core.login.mfa_webauthn_login_invalid": "WebAuthn 登录未返回有效凭据。",
|
|
61
80
|
"core.login.password": "请输入密码",
|
|
62
81
|
"core.login.privacy": "《隐私协议》",
|
|
63
82
|
"core.login.protocol_missing": "服务条款和隐私协议未配置",
|
|
@@ -71,17 +90,15 @@
|
|
|
71
90
|
"core.login.wechat_bind_tip": "该微信尚未绑定,请登录已有账号完成绑定",
|
|
72
91
|
"core.login.wechat_failed": "微信登录失败,请稍后重试",
|
|
73
92
|
"core.navigation.home": "首页",
|
|
93
|
+
"core.navigation.message": "消息",
|
|
74
94
|
"core.navigation.error.access": "菜单访问模式无效:{access}",
|
|
75
95
|
"core.navigation.error.child_tab": "只有移动端二级菜单可以作为 tab:{name}",
|
|
76
96
|
"core.navigation.error.cycle": "移动端菜单存在循环父级:{name}",
|
|
77
97
|
"core.navigation.error.identity": "菜单 id、name 或 app/ 路径无效",
|
|
78
|
-
"core.navigation.error.item_object": "菜单项不是对象",
|
|
79
|
-
"core.navigation.error.list_missing": "菜单响应缺少扁平列表",
|
|
80
98
|
"core.navigation.error.name_prefix": "菜单名称必须使用 App 前缀:{name}",
|
|
81
99
|
"core.navigation.error.parent_invalid": "移动端菜单父级编号无效:{name}",
|
|
82
100
|
"core.navigation.error.parent_missing": "菜单缺少父级编号:{name}",
|
|
83
101
|
"core.navigation.error.parent_not_found": "移动端菜单父级不存在:{name}",
|
|
84
|
-
"core.navigation.error.response_object": "菜单响应不是对象",
|
|
85
102
|
"core.navigation.error.root_tab": "移动端二级菜单必须作为 tab:{name}",
|
|
86
103
|
"core.navigation.error.tab_count": "tabBar 只能配置 0 或 2-5 项",
|
|
87
104
|
"core.navigation.error.unique": "菜单 id、name、path 必须唯一",
|
|
@@ -92,6 +109,22 @@
|
|
|
92
109
|
"core.protocol.not_configured": "{title}未配置,暂时无法查看",
|
|
93
110
|
"core.protocol.privacy": "隐私协议",
|
|
94
111
|
"core.protocol.service": "服务条款",
|
|
112
|
+
"core.settings.authorization": "授权管理",
|
|
113
|
+
"core.settings.contact": "联系我们",
|
|
114
|
+
"core.settings.feedback": "问题反馈",
|
|
115
|
+
"core.settings.logout_confirm": "是否退出登录?",
|
|
116
|
+
"core.settings.logout_failed": "退出登录失败",
|
|
117
|
+
"core.settings.mfa_bind_success": "多因素认证绑定成功",
|
|
118
|
+
"core.settings.mfa_disable_action": "禁用",
|
|
119
|
+
"core.settings.mfa_disable_factor_required": "请输入动态口令或恢复码。",
|
|
120
|
+
"core.settings.mfa_disable_success": "多因素认证已禁用",
|
|
121
|
+
"core.settings.mfa_disable_title": "禁用多因素认证",
|
|
122
|
+
"core.settings.mfa_disabled": "未启用",
|
|
123
|
+
"core.settings.mfa_enabled_method": "已启用({method})",
|
|
124
|
+
"core.settings.mfa_method_totp": "TOTP",
|
|
125
|
+
"core.settings.mfa_method_webauthn": "WebAuthn",
|
|
126
|
+
"core.settings.mfa_title": "多因素认证",
|
|
127
|
+
"core.settings.title": "设置",
|
|
95
128
|
"core.status.CONFIG_ERROR": "导航配置无效",
|
|
96
129
|
"core.status.forbidden": "暂无访问权限",
|
|
97
130
|
"core.status.NOT_FOUND": "页面不存在",
|
|
@@ -100,5 +133,9 @@
|
|
|
100
133
|
"core.status.loading": "正在加载",
|
|
101
134
|
"core.webview.embed_blocked": "当前 H5 页面可能被目标站点限制嵌入",
|
|
102
135
|
"core.webview.invalid_url": "缺少有效链接地址",
|
|
103
|
-
"core.webview.open_failed": "链接无法打开"
|
|
136
|
+
"core.webview.open_failed": "链接无法打开",
|
|
137
|
+
"common.value.top_level": "顶级节点",
|
|
138
|
+
"common.resource.default": "{resource}",
|
|
139
|
+
"common.action.delete_resource": "删除{resource}",
|
|
140
|
+
"common.action.set_field_status": "设置{field}"
|
|
104
141
|
}
|
package/src/locales/zh-TW.json
CHANGED
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"core.crypto.public_key_invalid": "公鑰格式無效",
|
|
29
29
|
"core.crypto.random_failed": "隨機數生成失敗",
|
|
30
30
|
"core.crypto.unsupported": "當前環境不支援密碼加密",
|
|
31
|
+
"core.password.verify_title": "驗證密碼",
|
|
32
|
+
"core.password.verify_label": "當前密碼",
|
|
31
33
|
"core.file.upload_failed": "上傳失敗",
|
|
32
34
|
"core.home.account_capability": "賬戶能力",
|
|
33
35
|
"core.home.account_capability_description": "個人資料、應用設定與智慧助手入口已保留",
|
|
@@ -58,6 +60,23 @@
|
|
|
58
60
|
"core.login.captcha_write_failed": "驗證碼圖片寫入失敗",
|
|
59
61
|
"core.login.default_sub_title": "歡迎使用本應用",
|
|
60
62
|
"core.login.login_success": "登入成功",
|
|
63
|
+
"core.login.mfa_title": "多因素認證",
|
|
64
|
+
"core.login.mfa_code": "請輸入動態密碼",
|
|
65
|
+
"core.login.mfa_recovery_code": "恢復碼(可選)",
|
|
66
|
+
"core.login.mfa_setup_title": "綁定多因素認證",
|
|
67
|
+
"core.login.mfa_copy_uri": "複製綁定地址",
|
|
68
|
+
"core.login.mfa_copy_success": "綁定地址已複製",
|
|
69
|
+
"core.login.mfa_recovery_codes_copied": "恢復碼已複製",
|
|
70
|
+
"core.login.mfa_recovery_codes_title": "儲存 MFA 恢復碼",
|
|
71
|
+
"core.login.mfa_recovery_codes_warning": "每個恢復碼只能使用一次,關閉此視窗後不會再次顯示。",
|
|
72
|
+
"core.login.mfa_copy_recovery_codes": "複製恢復碼",
|
|
73
|
+
"core.login.mfa_recovery_codes_confirm": "我已儲存",
|
|
74
|
+
"core.login.mfa_setup_success": "多因素認證綁定成功,請重新登入。",
|
|
75
|
+
"core.login.mfa_webauthn_action": "使用 Passkey",
|
|
76
|
+
"core.login.mfa_webauthn_setup_unsupported": "目前平台不支援 WebAuthn,請使用 TOTP 或恢復碼。",
|
|
77
|
+
"core.login.mfa_webauthn_setup_invalid": "WebAuthn 註冊未回傳有效憑證。",
|
|
78
|
+
"core.login.mfa_webauthn_login_unsupported": "目前平台不支援 WebAuthn,請使用恢復碼。",
|
|
79
|
+
"core.login.mfa_webauthn_login_invalid": "WebAuthn 登入未回傳有效憑證。",
|
|
61
80
|
"core.login.password": "請輸入密碼",
|
|
62
81
|
"core.login.privacy": "《隱私協議》",
|
|
63
82
|
"core.login.protocol_missing": "服務條款和隱私協議未配置",
|
|
@@ -71,17 +90,15 @@
|
|
|
71
90
|
"core.login.wechat_bind_tip": "該微信尚未繫結,請登入已有賬號完成繫結",
|
|
72
91
|
"core.login.wechat_failed": "微信登入失敗,請稍後重試",
|
|
73
92
|
"core.navigation.home": "首頁",
|
|
93
|
+
"core.navigation.message": "消息",
|
|
74
94
|
"core.navigation.error.access": "選單訪問模式無效:{access}",
|
|
75
95
|
"core.navigation.error.child_tab": "只有移動端二級選單可以作為 tab:{name}",
|
|
76
96
|
"core.navigation.error.cycle": "移動端選單存在迴圈父級:{name}",
|
|
77
97
|
"core.navigation.error.identity": "選單 id、name 或 app/ 路徑無效",
|
|
78
|
-
"core.navigation.error.item_object": "選單項不是物件",
|
|
79
|
-
"core.navigation.error.list_missing": "選單響應缺少扁平列表",
|
|
80
98
|
"core.navigation.error.name_prefix": "選單名稱必須使用 App 字首:{name}",
|
|
81
99
|
"core.navigation.error.parent_invalid": "移動端選單父級編號無效:{name}",
|
|
82
100
|
"core.navigation.error.parent_missing": "選單缺少父級編號:{name}",
|
|
83
101
|
"core.navigation.error.parent_not_found": "移動端選單父級不存在:{name}",
|
|
84
|
-
"core.navigation.error.response_object": "選單響應不是物件",
|
|
85
102
|
"core.navigation.error.root_tab": "移動端二級選單必須作為 tab:{name}",
|
|
86
103
|
"core.navigation.error.tab_count": "tabBar 只能配置 0 或 2-5 項",
|
|
87
104
|
"core.navigation.error.unique": "選單 id、name、path 必須唯一",
|
|
@@ -92,6 +109,22 @@
|
|
|
92
109
|
"core.protocol.not_configured": "{title}未配置,暫時無法檢視",
|
|
93
110
|
"core.protocol.privacy": "隱私協議",
|
|
94
111
|
"core.protocol.service": "服務條款",
|
|
112
|
+
"core.settings.authorization": "授權管理",
|
|
113
|
+
"core.settings.contact": "聯絡我們",
|
|
114
|
+
"core.settings.feedback": "問題反饋",
|
|
115
|
+
"core.settings.logout_confirm": "是否退出登入?",
|
|
116
|
+
"core.settings.logout_failed": "退出登入失敗",
|
|
117
|
+
"core.settings.mfa_bind_success": "多因素認證綁定成功",
|
|
118
|
+
"core.settings.mfa_disable_action": "停用",
|
|
119
|
+
"core.settings.mfa_disable_factor_required": "請輸入動態密碼或恢復碼。",
|
|
120
|
+
"core.settings.mfa_disable_success": "多因素認證已停用",
|
|
121
|
+
"core.settings.mfa_disable_title": "停用多因素認證",
|
|
122
|
+
"core.settings.mfa_disabled": "未啟用",
|
|
123
|
+
"core.settings.mfa_enabled_method": "已啟用({method})",
|
|
124
|
+
"core.settings.mfa_method_totp": "TOTP",
|
|
125
|
+
"core.settings.mfa_method_webauthn": "WebAuthn",
|
|
126
|
+
"core.settings.mfa_title": "多因素認證",
|
|
127
|
+
"core.settings.title": "設定",
|
|
95
128
|
"core.status.CONFIG_ERROR": "導航配置無效",
|
|
96
129
|
"core.status.forbidden": "暫無訪問許可權",
|
|
97
130
|
"core.status.NOT_FOUND": "頁面不存在",
|
|
@@ -100,5 +133,9 @@
|
|
|
100
133
|
"core.status.loading": "正在載入",
|
|
101
134
|
"core.webview.embed_blocked": "當前 H5 頁面可能被目標站點限制嵌入",
|
|
102
135
|
"core.webview.invalid_url": "缺少有效連結地址",
|
|
103
|
-
"core.webview.open_failed": "連結無法開啟"
|
|
136
|
+
"core.webview.open_failed": "連結無法開啟",
|
|
137
|
+
"common.value.top_level": "頂級節點",
|
|
138
|
+
"common.resource.default": "{resource}",
|
|
139
|
+
"common.action.delete_resource": "刪除{resource}",
|
|
140
|
+
"common.action.set_field_status": "設定{field}"
|
|
104
141
|
}
|
package/src/navigation.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computed, readonly, ref } from 'vue'
|
|
2
|
-
import { defBaseMenuService } from './api/
|
|
2
|
+
import { defBaseMenuService } from './api/system/app/v1/base_menu'
|
|
3
|
+
import type { BaseMenu, ListBaseMenuResponse } from './rpc/system/app/v1/base_menu'
|
|
3
4
|
import { hasValidToken } from './utils/auth'
|
|
4
5
|
import { navigateToLogin } from './utils/navigation'
|
|
5
6
|
import { resolveStaticView } from './module'
|
|
@@ -32,7 +33,7 @@ export interface AppMenuNode extends AppMenu {
|
|
|
32
33
|
/** 菜单获取适配器。 */
|
|
33
34
|
export interface AppNavigationAdapter {
|
|
34
35
|
/** 获取扁平移动菜单配置。 */
|
|
35
|
-
list(): Promise<
|
|
36
|
+
list(): Promise<ListBaseMenuResponse>
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** 逻辑路由解析结果。 */
|
|
@@ -45,10 +46,10 @@ export interface ResolvedAppRoute {
|
|
|
45
46
|
const ANONYMOUS_CACHE_KEY = 'kratos-uni-app:navigation:anonymous'
|
|
46
47
|
const AUTHENTICATED_CACHE_KEY = 'kratos-uni-app:navigation:authenticated'
|
|
47
48
|
/** 固定移动端菜单根目录编号。 */
|
|
48
|
-
export const APP_MENU_ROOT_ID =
|
|
49
|
+
export const APP_MENU_ROOT_ID = 99000000
|
|
49
50
|
const createDefaultMenus = (): AppMenu[] => [
|
|
50
51
|
{
|
|
51
|
-
id:
|
|
52
|
+
id: 99010000,
|
|
52
53
|
parentId: APP_MENU_ROOT_ID,
|
|
53
54
|
name: 'AppHome',
|
|
54
55
|
path: 'app/home',
|
|
@@ -60,7 +61,19 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
60
61
|
selectedIcon: 'HOME_SELECTED',
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
|
-
id:
|
|
64
|
+
id: 99040000,
|
|
65
|
+
parentId: APP_MENU_ROOT_ID,
|
|
66
|
+
name: 'AppMessage',
|
|
67
|
+
path: 'app/message',
|
|
68
|
+
viewKey: 'MESSAGE_INBOX',
|
|
69
|
+
title: t('core.navigation.message'),
|
|
70
|
+
access: 'AUTHENTICATED',
|
|
71
|
+
inTabBar: true,
|
|
72
|
+
icon: 'MESSAGE_DEFAULT',
|
|
73
|
+
selectedIcon: 'MESSAGE_SELECTED',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 99090000,
|
|
64
77
|
parentId: APP_MENU_ROOT_ID,
|
|
65
78
|
name: 'AppMy',
|
|
66
79
|
path: 'app/my',
|
|
@@ -72,8 +85,8 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
72
85
|
selectedIcon: 'USER_SELECTED',
|
|
73
86
|
},
|
|
74
87
|
{
|
|
75
|
-
id:
|
|
76
|
-
parentId:
|
|
88
|
+
id: 99010100,
|
|
89
|
+
parentId: 99010000,
|
|
77
90
|
name: 'AppLogin',
|
|
78
91
|
path: 'app/login',
|
|
79
92
|
viewKey: 'LOGIN',
|
|
@@ -82,8 +95,8 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
82
95
|
inTabBar: false,
|
|
83
96
|
},
|
|
84
97
|
{
|
|
85
|
-
id:
|
|
86
|
-
parentId:
|
|
98
|
+
id: 99010101,
|
|
99
|
+
parentId: 99010100,
|
|
87
100
|
name: 'AppProtocol',
|
|
88
101
|
path: 'app/protocol/:type',
|
|
89
102
|
viewKey: 'PROTOCOL',
|
|
@@ -92,8 +105,8 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
92
105
|
inTabBar: false,
|
|
93
106
|
},
|
|
94
107
|
{
|
|
95
|
-
id:
|
|
96
|
-
parentId:
|
|
108
|
+
id: 99090100,
|
|
109
|
+
parentId: 99090000,
|
|
97
110
|
name: 'AppProfile',
|
|
98
111
|
path: 'app/profile',
|
|
99
112
|
viewKey: 'PROFILE',
|
|
@@ -102,18 +115,18 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
102
115
|
inTabBar: false,
|
|
103
116
|
},
|
|
104
117
|
{
|
|
105
|
-
id:
|
|
106
|
-
parentId:
|
|
118
|
+
id: 99090200,
|
|
119
|
+
parentId: 99090000,
|
|
107
120
|
name: 'AppSettings',
|
|
108
121
|
path: 'app/settings',
|
|
109
122
|
viewKey: 'SETTINGS',
|
|
110
|
-
title: t('
|
|
123
|
+
title: t('core.settings.title'),
|
|
111
124
|
access: 'AUTHENTICATED',
|
|
112
125
|
inTabBar: false,
|
|
113
126
|
},
|
|
114
127
|
{
|
|
115
|
-
id:
|
|
116
|
-
parentId:
|
|
128
|
+
id: 99090300,
|
|
129
|
+
parentId: 99090000,
|
|
117
130
|
name: 'AppAi',
|
|
118
131
|
path: 'app/ai',
|
|
119
132
|
viewKey: 'AI',
|
|
@@ -122,8 +135,8 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
122
135
|
inTabBar: false,
|
|
123
136
|
},
|
|
124
137
|
{
|
|
125
|
-
id:
|
|
126
|
-
parentId:
|
|
138
|
+
id: 99010200,
|
|
139
|
+
parentId: 99010000,
|
|
127
140
|
name: 'AppWebView',
|
|
128
141
|
path: 'app/webview',
|
|
129
142
|
viewKey: 'WEBVIEW',
|
|
@@ -135,12 +148,26 @@ const createDefaultMenus = (): AppMenu[] => [
|
|
|
135
148
|
|
|
136
149
|
const menus = ref<AppMenu[]>([])
|
|
137
150
|
const ready = ref(false)
|
|
151
|
+
const appMenuBadges = ref<Record<string, number>>({})
|
|
152
|
+
const isH5Runtime = typeof window !== 'undefined'
|
|
138
153
|
let tabNavigationTarget: string | undefined
|
|
139
154
|
let adapter: AppNavigationAdapter = {
|
|
140
155
|
list: () => defBaseMenuService.ListBaseMenu(),
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
const nativeTabViewKeys = ['HOME', 'PROFILE_HOME']
|
|
159
|
+
const pageTitleKeys: Record<string, string> = {
|
|
160
|
+
'pages/index/index': 'core.home.main_title',
|
|
161
|
+
'pages/login/login': 'common.action.login',
|
|
162
|
+
'pages/login/protocal': 'core.navigation.protocol',
|
|
163
|
+
'pages/my/my': 'core.navigation.my',
|
|
164
|
+
'pagesMember/ai/index': 'system.ai.chat_title',
|
|
165
|
+
'pagesMember/message/detail': 'system.notification.title',
|
|
166
|
+
'pagesMember/message/index': 'system.notification.title',
|
|
167
|
+
'pagesMember/profile/profile': 'system.profile.title',
|
|
168
|
+
'pagesMember/settings/settings': 'core.settings.title',
|
|
169
|
+
}
|
|
170
|
+
const resetTabViewKeys = ['MESSAGE_INBOX']
|
|
144
171
|
|
|
145
172
|
/** 替换导航远端适配器,供宿主接入自定义契约。 */
|
|
146
173
|
export function setAppNavigationAdapter(nextAdapter: AppNavigationAdapter): void {
|
|
@@ -228,20 +255,29 @@ function navigateTabRoute(url: string): void {
|
|
|
228
255
|
const pages = getCurrentPages()
|
|
229
256
|
const currentIndex = pages.length - 1
|
|
230
257
|
const currentRoute = pages[currentIndex]?.route?.replace(/^\/+/, '')
|
|
231
|
-
if (currentRoute === targetRoute || tabNavigationTarget) return
|
|
258
|
+
if (currentRoute === targetRoute || tabNavigationTarget === targetRoute) return
|
|
232
259
|
|
|
233
260
|
tabNavigationTarget = targetRoute
|
|
234
261
|
const release = () => {
|
|
235
262
|
if (tabNavigationTarget === targetRoute) tabNavigationTarget = undefined
|
|
236
263
|
}
|
|
264
|
+
if (isH5Runtime) {
|
|
265
|
+
uni.reLaunch({ url, success: release, fail: release, complete: release })
|
|
266
|
+
return
|
|
267
|
+
}
|
|
237
268
|
if (nativeTabViewKeys.some((viewKey) => resolveStaticView(viewKey) === targetRoute)) {
|
|
238
269
|
uni.switchTab({
|
|
239
270
|
url,
|
|
240
271
|
success: release,
|
|
241
272
|
fail: () => navigateTabRouteInStack(url, targetRoute, pages, currentIndex, release),
|
|
273
|
+
complete: release,
|
|
242
274
|
})
|
|
243
275
|
return
|
|
244
276
|
}
|
|
277
|
+
if (resetTabViewKeys.some((viewKey) => resolveStaticView(viewKey) === targetRoute)) {
|
|
278
|
+
uni.reLaunch({ url, success: release, fail: release, complete: release })
|
|
279
|
+
return
|
|
280
|
+
}
|
|
245
281
|
navigateTabRouteInStack(url, targetRoute, pages, currentIndex, release)
|
|
246
282
|
}
|
|
247
283
|
|
|
@@ -272,6 +308,48 @@ function navigateTabRouteInStack(
|
|
|
272
308
|
})
|
|
273
309
|
}
|
|
274
310
|
|
|
311
|
+
/** 按稳定 viewKey 跳转,并将动作参数作为查询参数传递。 */
|
|
312
|
+
export function navigateAppView(viewKey: string, params: Record<string, string> = {}): void {
|
|
313
|
+
const menu = menus.value.find((item) => item.viewKey === viewKey)
|
|
314
|
+
if (!menu) {
|
|
315
|
+
launchAppStatus('NOT_FOUND')
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
const remaining = { ...params }
|
|
319
|
+
const path = menu.path.replace(/:([A-Za-z0-9_]+)/g, (placeholder, key: string) => {
|
|
320
|
+
if (!(key in remaining)) return placeholder
|
|
321
|
+
const value = remaining[key]
|
|
322
|
+
delete remaining[key]
|
|
323
|
+
return encodeURIComponent(value)
|
|
324
|
+
})
|
|
325
|
+
const query = Object.entries(remaining)
|
|
326
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
327
|
+
.join('&')
|
|
328
|
+
navigateAppRoute(`${path}${query ? `?${query}` : ''}`)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** 设置移动端菜单 badge 数量,供业务模块注册未读状态。 */
|
|
332
|
+
export function setAppMenuBadge(viewKey: string, count: number): void {
|
|
333
|
+
const next = { ...appMenuBadges.value }
|
|
334
|
+
if (count > 0) next[viewKey] = Math.min(Math.floor(count), 99)
|
|
335
|
+
else delete next[viewKey]
|
|
336
|
+
appMenuBadges.value = next
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** 读取移动端菜单 badge 数量。 */
|
|
340
|
+
export function useAppMenuBadge(viewKey: string) {
|
|
341
|
+
return computed(() => appMenuBadges.value[viewKey] ?? 0)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** 根据物理页面路由更新 H5 和原生导航栏标题。 */
|
|
345
|
+
export function setAppPageTitle(route: string): void {
|
|
346
|
+
const titleKey = pageTitleKeys[route]
|
|
347
|
+
const menuTitle = menus.value.find((menu) => resolveStaticView(menu.viewKey) === route)?.title
|
|
348
|
+
const title = titleKey ? t(titleKey) : menuTitle
|
|
349
|
+
if (!title) return
|
|
350
|
+
void uni.setNavigationBarTitle({ title })
|
|
351
|
+
}
|
|
352
|
+
|
|
275
353
|
/** 获取导航响应式状态。 */
|
|
276
354
|
export function useAppNavigation() {
|
|
277
355
|
const menuTree = computed<AppMenuNode[]>(() => buildMenuTree(menus.value, APP_MENU_ROOT_ID))
|
|
@@ -301,48 +379,32 @@ function readCachedMenus(cacheKey: string): AppMenu[] | undefined {
|
|
|
301
379
|
const cached = uni.getStorageSync(cacheKey) as unknown
|
|
302
380
|
if (!Array.isArray(cached)) return
|
|
303
381
|
try {
|
|
304
|
-
const
|
|
305
|
-
validateMenus(
|
|
306
|
-
return
|
|
382
|
+
const cachedMenus = cached as AppMenu[]
|
|
383
|
+
validateMenus(cachedMenus)
|
|
384
|
+
return cachedMenus
|
|
307
385
|
} catch {
|
|
308
386
|
uni.removeStorageSync(cacheKey)
|
|
309
387
|
}
|
|
310
388
|
}
|
|
311
389
|
|
|
312
|
-
function normalizeMenuResponse(response:
|
|
313
|
-
|
|
314
|
-
if (!response || typeof response !== 'object')
|
|
315
|
-
throw new Error(t('core.navigation.error.response_object'))
|
|
316
|
-
const record = response as Record<string, unknown>
|
|
317
|
-
const list = record.items ?? record.list ?? record.data
|
|
318
|
-
if (!Array.isArray(list)) throw new Error(t('core.navigation.error.list_missing'))
|
|
319
|
-
return list.map(normalizeMenu)
|
|
390
|
+
function normalizeMenuResponse(response: ListBaseMenuResponse): AppMenu[] {
|
|
391
|
+
return response.items.map(normalizeMenu)
|
|
320
392
|
}
|
|
321
393
|
|
|
322
|
-
function normalizeMenu(
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
const meta = (item.meta ?? {}) as Record<string, unknown>
|
|
326
|
-
const app = (meta.app ?? item.app ?? {}) as Record<string, unknown>
|
|
394
|
+
function normalizeMenu(item: BaseMenu): AppMenu {
|
|
395
|
+
const meta = item.meta
|
|
396
|
+
const app = meta?.app
|
|
327
397
|
return {
|
|
328
|
-
id:
|
|
329
|
-
parentId:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
inTabBar: Boolean(app.in_tab_bar ?? app.inTabBar),
|
|
339
|
-
icon: typeof meta.icon === 'string' ? meta.icon : undefined,
|
|
340
|
-
selectedIcon:
|
|
341
|
-
typeof app.selected_icon === 'string'
|
|
342
|
-
? app.selected_icon
|
|
343
|
-
: typeof app.selectedIcon === 'string'
|
|
344
|
-
? app.selectedIcon
|
|
345
|
-
: undefined,
|
|
398
|
+
id: item.id,
|
|
399
|
+
parentId: item.parent_id,
|
|
400
|
+
name: item.name,
|
|
401
|
+
path: item.path,
|
|
402
|
+
viewKey: app?.view_key ?? '',
|
|
403
|
+
title: meta?.title ?? '',
|
|
404
|
+
access: (app?.access ?? 'AUTHENTICATED') as AppMenuAccess,
|
|
405
|
+
inTabBar: app?.in_tab_bar ?? false,
|
|
406
|
+
icon: meta?.icon,
|
|
407
|
+
selectedIcon: app?.selected_icon,
|
|
346
408
|
}
|
|
347
409
|
}
|
|
348
410
|
|
package/src/rpc/base/v1/login.ts
CHANGED
|
@@ -20,6 +20,27 @@ export enum PasswordCryptoScene {
|
|
|
20
20
|
PASSWORD_CRYPTO_SCENE_RESET_BASE_USER_PASSWORD = 3,
|
|
21
21
|
/** PASSWORD_CRYPTO_SCENE_UPDATE_USER_PASSWORD - 个人修改密码加密场景。 */
|
|
22
22
|
PASSWORD_CRYPTO_SCENE_UPDATE_USER_PASSWORD = 4,
|
|
23
|
+
/** PASSWORD_CRYPTO_SCENE_MFA - 多因素认证敏感操作场景。 */
|
|
24
|
+
PASSWORD_CRYPTO_SCENE_MFA = 5,
|
|
25
|
+
/** PASSWORD_CRYPTO_SCENE_CONFIGURE_PASSWORD_POLICY - 密码策略初始化密码配置场景。 */
|
|
26
|
+
PASSWORD_CRYPTO_SCENE_CONFIGURE_PASSWORD_POLICY = 6,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* LoginStatus 表示登录请求当前所处的认证阶段。
|
|
31
|
+
* 只有 AUTHENTICATED 或 PASSWORD_CHANGE_REQUIRED 才会携带正式访问令牌,MFA 状态必须继续完成挑战。
|
|
32
|
+
*/
|
|
33
|
+
export enum LoginStatus {
|
|
34
|
+
/** LOGIN_STATUS_UNSPECIFIED - 未指定登录状态。 */
|
|
35
|
+
LOGIN_STATUS_UNSPECIFIED = 0,
|
|
36
|
+
/** LOGIN_STATUS_AUTHENTICATED - 已完成认证并签发令牌。 */
|
|
37
|
+
LOGIN_STATUS_AUTHENTICATED = 1,
|
|
38
|
+
/** LOGIN_STATUS_MFA_REQUIRED - 需要继续校验多因素认证。 */
|
|
39
|
+
LOGIN_STATUS_MFA_REQUIRED = 2,
|
|
40
|
+
/** LOGIN_STATUS_MFA_ENROLLMENT_REQUIRED - 必须先完成多因素认证绑定。 */
|
|
41
|
+
LOGIN_STATUS_MFA_ENROLLMENT_REQUIRED = 3,
|
|
42
|
+
/** LOGIN_STATUS_PASSWORD_CHANGE_REQUIRED - 下次操作前必须修改临时密码。 */
|
|
43
|
+
LOGIN_STATUS_PASSWORD_CHANGE_REQUIRED = 4,
|
|
23
44
|
}
|
|
24
45
|
|
|
25
46
|
/** 验证码获取条件 */
|
|
@@ -80,15 +101,15 @@ export interface PasswordPublicKeyResponse {
|
|
|
80
101
|
|
|
81
102
|
/** 刷新令牌请求参数 */
|
|
82
103
|
export interface RefreshTokenRequest {
|
|
83
|
-
/**
|
|
84
|
-
refresh_token
|
|
104
|
+
/** 更新令牌;未填写时由服务端从HttpOnly Cookie读取。 */
|
|
105
|
+
refresh_token?: string | undefined;
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
/** 刷新令牌响应 */
|
|
88
109
|
export interface RefreshTokenResponse {
|
|
89
110
|
/** 访问令牌,必选项。 */
|
|
90
111
|
access_token: string;
|
|
91
|
-
/**
|
|
112
|
+
/** 更新令牌;管理端 Cookie-only 请求不在响应体返回,应用端请求可返回。 */
|
|
92
113
|
refresh_token: string;
|
|
93
114
|
/** 令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。 */
|
|
94
115
|
token_type: string;
|
|
@@ -116,12 +137,24 @@ export interface LoginRequest {
|
|
|
116
137
|
export interface LoginResponse {
|
|
117
138
|
/** 访问令牌,必选项。 */
|
|
118
139
|
access_token: string;
|
|
119
|
-
/**
|
|
140
|
+
/** 更新令牌;管理端 Cookie-only 请求不在响应体返回,应用端请求可返回。 */
|
|
120
141
|
refresh_token: string;
|
|
121
142
|
/** 令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。 */
|
|
122
143
|
token_type: string;
|
|
123
144
|
/** 令牌有效时间,单位为秒。如果访问令牌过期,服务器应回复授予访问令牌的持续时间。如果省略该参数,必须其他方式设置过期时间。 */
|
|
124
145
|
expires_in: number;
|
|
146
|
+
/** 登录认证状态 */
|
|
147
|
+
status: LoginStatus;
|
|
148
|
+
/** 多因素认证挑战ID */
|
|
149
|
+
mfa_challenge_id: string;
|
|
150
|
+
/** 多因素认证绑定临时票据 */
|
|
151
|
+
mfa_setup_ticket: string;
|
|
152
|
+
/** 多因素认证挑战或绑定票据有效时间,单位秒 */
|
|
153
|
+
mfa_expires_in: number;
|
|
154
|
+
/** 多因素认证方式:totp、webauthn */
|
|
155
|
+
mfa_method: string;
|
|
156
|
+
/** WebAuthn认证选项JSON */
|
|
157
|
+
mfa_webauthn_options_json: string;
|
|
125
158
|
}
|
|
126
159
|
|
|
127
160
|
/** Base登录公共服务 */
|