@liujitcn/kratos-uni-app-core 0.0.79 → 0.0.80
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/module.mjs +36 -6
- package/package.json +1 -1
- package/src/components/BootstrapStatus.vue +6 -4
- package/src/components/KratosSettingsPage.vue +161 -109
- package/src/components/KratosTabBar.vue +67 -16
- package/src/components/MfaManageDialog.vue +14 -13
- package/src/components/MfaRecoveryCodesDialog.vue +13 -12
- package/src/components/MfaSetupPanel.vue +8 -8
- package/src/components/PasswordVerifyDialog.vue +15 -14
- package/src/locales/en-US.json +8 -1
- package/src/locales/ja-JP.json +8 -1
- package/src/locales/zh-CN.json +8 -1
- package/src/locales/zh-TW.json +8 -1
- package/src/navigation.ts +10 -3
- package/src/pages.ts +4 -2
- package/src/stores/index.ts +1 -0
- package/src/stores/modules/theme.ts +111 -0
- package/src/styles/base.scss +57 -0
- package/src/styles/components.scss +770 -0
- package/src/styles/theme.scss +66 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/navigation.ts +16 -0
- package/src/views/pages/index/index.vue +137 -141
- package/src/views/pages/login/login.vue +266 -274
- package/src/views/pages/login/protocal.vue +30 -20
- package/src/views/pages/webview/webview.vue +12 -6
package/dist/module.mjs
CHANGED
|
@@ -7,8 +7,10 @@ var corePages = {
|
|
|
7
7
|
navigationBarTitleText: ""
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
|
-
"pages/login/login": {
|
|
11
|
-
|
|
10
|
+
"pages/login/login": {
|
|
11
|
+
style: { navigationStyle: "custom", navigationBarTitleText: "" }
|
|
12
|
+
},
|
|
13
|
+
"pages/login/protocal": { style: { navigationStyle: "custom", navigationBarTitleText: "" } },
|
|
12
14
|
"pages/webview/webview": {
|
|
13
15
|
style: {
|
|
14
16
|
navigationBarTitleText: "",
|
|
@@ -141,6 +143,9 @@ var zh_CN_default = {
|
|
|
141
143
|
"core.settings.authorization": "\u6388\u6743\u7BA1\u7406",
|
|
142
144
|
"core.settings.contact": "\u8054\u7CFB\u6211\u4EEC",
|
|
143
145
|
"core.settings.feedback": "\u95EE\u9898\u53CD\u9988",
|
|
146
|
+
"core.settings.section_appearance": "\u5916\u89C2",
|
|
147
|
+
"core.settings.section_security": "\u8D26\u6237\u5B89\u5168",
|
|
148
|
+
"core.settings.section_more": "\u66F4\u591A \xB7 \u5C0F\u7A0B\u5E8F\u7AEF",
|
|
144
149
|
"core.settings.logout_confirm": "\u662F\u5426\u9000\u51FA\u767B\u5F55\uFF1F",
|
|
145
150
|
"core.settings.logout_failed": "\u9000\u51FA\u767B\u5F55\u5931\u8D25",
|
|
146
151
|
"core.settings.mfa_bind_success": "\u591A\u56E0\u7D20\u8BA4\u8BC1\u7ED1\u5B9A\u6210\u529F",
|
|
@@ -170,7 +175,11 @@ var zh_CN_default = {
|
|
|
170
175
|
"common.action.load_more": "\u52A0\u8F7D\u66F4\u591A",
|
|
171
176
|
"common.status.loading": "\u52A0\u8F7D\u4E2D\u2026",
|
|
172
177
|
"core.navigation.analytics": "\u5206\u6790",
|
|
173
|
-
"core.navigation.report": "\u62A5\u8868"
|
|
178
|
+
"core.navigation.report": "\u62A5\u8868",
|
|
179
|
+
"core.settings.theme": "\u4E3B\u9898\u5916\u89C2",
|
|
180
|
+
"core.theme.auto": "\u8DDF\u968F\u7CFB\u7EDF",
|
|
181
|
+
"core.theme.dark": "\u6697\u9ED1",
|
|
182
|
+
"core.theme.light": "\u660E\u4EAE"
|
|
174
183
|
};
|
|
175
184
|
|
|
176
185
|
// src/locales/en-US.json
|
|
@@ -291,6 +300,9 @@ var en_US_default = {
|
|
|
291
300
|
"core.settings.authorization": "Authorization settings",
|
|
292
301
|
"core.settings.contact": "Contact us",
|
|
293
302
|
"core.settings.feedback": "Feedback",
|
|
303
|
+
"core.settings.section_appearance": "Appearance",
|
|
304
|
+
"core.settings.section_security": "Account security",
|
|
305
|
+
"core.settings.section_more": "More \xB7 Mini Program",
|
|
294
306
|
"core.settings.logout_confirm": "Log out now?",
|
|
295
307
|
"core.settings.logout_failed": "Logout failed",
|
|
296
308
|
"core.settings.mfa_bind_success": "Multi-factor authentication enabled",
|
|
@@ -320,7 +332,11 @@ var en_US_default = {
|
|
|
320
332
|
"common.action.load_more": "Load more",
|
|
321
333
|
"common.status.loading": "Loading\u2026",
|
|
322
334
|
"core.navigation.analytics": "Analytics",
|
|
323
|
-
"core.navigation.report": "Reports"
|
|
335
|
+
"core.navigation.report": "Reports",
|
|
336
|
+
"core.settings.theme": "Theme Appearance",
|
|
337
|
+
"core.theme.auto": "Follow System",
|
|
338
|
+
"core.theme.dark": "Dark",
|
|
339
|
+
"core.theme.light": "Light"
|
|
324
340
|
};
|
|
325
341
|
|
|
326
342
|
// src/locales/ja-JP.json
|
|
@@ -441,6 +457,9 @@ var ja_JP_default = {
|
|
|
441
457
|
"core.settings.authorization": "\u8A8D\u53EF\u7BA1\u7406",
|
|
442
458
|
"core.settings.contact": "\u304A\u554F\u3044\u5408\u308F\u305B",
|
|
443
459
|
"core.settings.feedback": "\u554F\u984C\u306E\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF",
|
|
460
|
+
"core.settings.section_appearance": "\u5916\u89B3",
|
|
461
|
+
"core.settings.section_security": "\u30A2\u30AB\u30A6\u30F3\u30C8\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3",
|
|
462
|
+
"core.settings.section_more": "\u305D\u306E\u4ED6\u30FB\u30DF\u30CB\u30D7\u30ED\u30B0\u30E9\u30E0",
|
|
444
463
|
"core.settings.logout_confirm": "\u30ED\u30B0\u30A2\u30A6\u30C8\u3057\u307E\u3059\u304B?",
|
|
445
464
|
"core.settings.logout_failed": "\u30ED\u30B0\u30A2\u30A6\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
446
465
|
"core.settings.mfa_bind_success": "\u591A\u8981\u7D20\u8A8D\u8A3C\u3092\u6709\u52B9\u306B\u3057\u307E\u3057\u305F",
|
|
@@ -470,7 +489,11 @@ var ja_JP_default = {
|
|
|
470
489
|
"common.action.load_more": "\u3055\u3089\u306B\u8AAD\u307F\u8FBC\u3080",
|
|
471
490
|
"common.status.loading": "\u8AAD\u307F\u8FBC\u307F\u4E2D\u2026",
|
|
472
491
|
"core.navigation.analytics": "\u5206\u6790",
|
|
473
|
-
"core.navigation.report": "\u30EC\u30DD\u30FC\u30C8"
|
|
492
|
+
"core.navigation.report": "\u30EC\u30DD\u30FC\u30C8",
|
|
493
|
+
"core.settings.theme": "\u30C6\u30FC\u30DE\u5916\u89B3",
|
|
494
|
+
"core.theme.auto": "\u30B7\u30B9\u30C6\u30E0\u306B\u5F93\u3046",
|
|
495
|
+
"core.theme.dark": "\u30C0\u30FC\u30AF",
|
|
496
|
+
"core.theme.light": "\u30E9\u30A4\u30C8"
|
|
474
497
|
};
|
|
475
498
|
|
|
476
499
|
// src/locales/zh-TW.json
|
|
@@ -591,6 +614,9 @@ var zh_TW_default = {
|
|
|
591
614
|
"core.settings.authorization": "\u6388\u6B0A\u7BA1\u7406",
|
|
592
615
|
"core.settings.contact": "\u806F\u7D61\u6211\u5011",
|
|
593
616
|
"core.settings.feedback": "\u554F\u984C\u53CD\u994B",
|
|
617
|
+
"core.settings.section_appearance": "\u5916\u89C0",
|
|
618
|
+
"core.settings.section_security": "\u5E33\u6236\u5B89\u5168",
|
|
619
|
+
"core.settings.section_more": "\u66F4\u591A\u30FB\u5C0F\u7A0B\u5F0F\u7AEF",
|
|
594
620
|
"core.settings.logout_confirm": "\u662F\u5426\u9000\u51FA\u767B\u5165\uFF1F",
|
|
595
621
|
"core.settings.logout_failed": "\u9000\u51FA\u767B\u5165\u5931\u6557",
|
|
596
622
|
"core.settings.mfa_bind_success": "\u591A\u56E0\u7D20\u8A8D\u8B49\u7D81\u5B9A\u6210\u529F",
|
|
@@ -620,7 +646,11 @@ var zh_TW_default = {
|
|
|
620
646
|
"common.action.load_more": "\u8F09\u5165\u66F4\u591A",
|
|
621
647
|
"common.status.loading": "\u8F09\u5165\u4E2D\u2026",
|
|
622
648
|
"core.navigation.analytics": "\u5206\u6790",
|
|
623
|
-
"core.navigation.report": "\u5831\u8868"
|
|
649
|
+
"core.navigation.report": "\u5831\u8868",
|
|
650
|
+
"core.settings.theme": "\u4E3B\u984C\u5916\u89C0",
|
|
651
|
+
"core.theme.auto": "\u8DDF\u96A8\u7CFB\u7D71",
|
|
652
|
+
"core.theme.dark": "\u6697\u9ED1",
|
|
653
|
+
"core.theme.light": "\u660E\u4EAE"
|
|
624
654
|
};
|
|
625
655
|
|
|
626
656
|
// src/locales/generated.ts
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useI18n } from '../locales'
|
|
3
|
+
import { useThemeStore } from '../stores'
|
|
3
4
|
|
|
4
5
|
defineProps<{ title?: string; detail?: string }>()
|
|
5
6
|
const { t } = useI18n()
|
|
7
|
+
const themeStore = useThemeStore()
|
|
6
8
|
</script>
|
|
7
9
|
|
|
8
10
|
<template>
|
|
9
|
-
<view class="bootstrap-status">
|
|
11
|
+
<view class="bootstrap-status" :class="themeStore.themeClass">
|
|
10
12
|
<image class="bootstrap-status__logo" src="../static/images/logo_icon.png" mode="aspectFit" />
|
|
11
13
|
<text class="bootstrap-status__title">{{ title || t('core.status.loading') }}</text>
|
|
12
14
|
<text v-if="detail" class="bootstrap-status__detail">{{ detail }}</text>
|
|
@@ -21,7 +23,7 @@ const { t } = useI18n()
|
|
|
21
23
|
justify-content: center;
|
|
22
24
|
flex-direction: column;
|
|
23
25
|
padding: 48rpx;
|
|
24
|
-
background:
|
|
26
|
+
background: var(--kratos-bg-gradient);
|
|
25
27
|
box-sizing: border-box;
|
|
26
28
|
}
|
|
27
29
|
.bootstrap-status__logo {
|
|
@@ -30,12 +32,12 @@ const { t } = useI18n()
|
|
|
30
32
|
margin-bottom: 32rpx;
|
|
31
33
|
}
|
|
32
34
|
.bootstrap-status__title {
|
|
33
|
-
color:
|
|
35
|
+
color: var(--kratos-color-text);
|
|
34
36
|
font-size: 32rpx;
|
|
35
37
|
}
|
|
36
38
|
.bootstrap-status__detail {
|
|
37
39
|
margin-top: 16rpx;
|
|
38
|
-
color:
|
|
40
|
+
color: var(--kratos-color-text-muted);
|
|
39
41
|
font-size: 24rpx;
|
|
40
42
|
text-align: center;
|
|
41
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onMounted, ref
|
|
3
|
-
import { useUserStore } from '../stores'
|
|
2
|
+
import { computed, onMounted, ref } from 'vue'
|
|
3
|
+
import { useThemeStore, useUserStore, type ThemeMode } from '../stores'
|
|
4
4
|
import { navigateToLogin } from '../utils/navigation'
|
|
5
5
|
import { getLanguageOptions, useI18n, type SupportedLocale } from '../locales'
|
|
6
6
|
import { isAuthExpiredError } from '../utils/http'
|
|
@@ -15,6 +15,7 @@ defineSlots<{
|
|
|
15
15
|
}>()
|
|
16
16
|
|
|
17
17
|
const userStore = useUserStore()
|
|
18
|
+
const themeStore = useThemeStore()
|
|
18
19
|
const { locale, setLocale, t } = useI18n()
|
|
19
20
|
|
|
20
21
|
const logoutLoading = ref(false)
|
|
@@ -24,6 +25,17 @@ const mfaEnabled = ref(false)
|
|
|
24
25
|
const mfaMethod = ref('totp')
|
|
25
26
|
const mfaStatusLoading = ref(false)
|
|
26
27
|
const localeOptions = computed(() => getLanguageOptions())
|
|
28
|
+
const themeModeOptions: ThemeMode[] = ['auto', 'dark', 'light']
|
|
29
|
+
const themeModeLabels = computed(() =>
|
|
30
|
+
themeModeOptions.map((mode) =>
|
|
31
|
+
mode === 'auto'
|
|
32
|
+
? t('core.theme.auto')
|
|
33
|
+
: mode === 'dark'
|
|
34
|
+
? t('core.theme.dark')
|
|
35
|
+
: t('core.theme.light'),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
const themeModeIndex = computed(() => Math.max(0, themeModeOptions.indexOf(themeStore.mode)))
|
|
27
39
|
const localeLabels = computed(() => localeOptions.value.map((item) => item.native_name))
|
|
28
40
|
const localeIndex = computed(() =>
|
|
29
41
|
Math.max(
|
|
@@ -32,10 +44,41 @@ const localeIndex = computed(() =>
|
|
|
32
44
|
),
|
|
33
45
|
)
|
|
34
46
|
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
const settingsIcons = computed(() => {
|
|
48
|
+
const primary = themeStore.theme === 'light' ? '#039e8c' : '#28f0d4'
|
|
49
|
+
const warning = themeStore.theme === 'light' ? '#d9880f' : '#ffb648'
|
|
50
|
+
const violet = themeStore.theme === 'light' ? '#5f4fe0' : '#9b8cff'
|
|
51
|
+
const createIcon = (body: string, color: string) =>
|
|
52
|
+
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`)}`
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
theme: createIcon('<path d="M21 12.8A8.5 8.5 0 1 1 11.2 3a6.5 6.5 0 0 0 9.8 9.8Z"/>', primary),
|
|
56
|
+
mfa: createIcon(
|
|
57
|
+
'<path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6l-8-3Z"/><path d="m9 12 2 2 4-4"/>',
|
|
58
|
+
primary,
|
|
59
|
+
),
|
|
60
|
+
language: createIcon(
|
|
61
|
+
'<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18"/>',
|
|
62
|
+
primary,
|
|
63
|
+
),
|
|
64
|
+
authorization: createIcon(
|
|
65
|
+
'<circle cx="8" cy="15" r="4"/><path d="m11 12 7-7 3 3-7 7"/><path d="m15 8 2 2"/>',
|
|
66
|
+
warning,
|
|
67
|
+
),
|
|
68
|
+
feedback: createIcon('<path d="M4 5h16v12H8l-4 4z"/>', violet),
|
|
69
|
+
contact: createIcon(
|
|
70
|
+
'<path d="M4 5c0 9 6 15 15 15 1 0 2-1 2-2v-2l-4-2-2 2a11 11 0 0 1-5-5l2-2-2-4H6c-1 0-2 1-2 2Z"/>',
|
|
71
|
+
primary,
|
|
72
|
+
),
|
|
73
|
+
}
|
|
37
74
|
})
|
|
38
75
|
|
|
76
|
+
/** 处理主题外观选择并持久化。 */
|
|
77
|
+
const onThemeModeChange = (event: { detail: { value: string | number } }) => {
|
|
78
|
+
const nextMode = themeModeOptions[Number(event.detail.value)]
|
|
79
|
+
if (nextMode) themeStore.setMode(nextMode)
|
|
80
|
+
}
|
|
81
|
+
|
|
39
82
|
/** 处理语言选择并刷新动态本地化数据。 */
|
|
40
83
|
const onLocaleChange = (event: { detail: { value: string | number } }) => {
|
|
41
84
|
const nextLocale = localeOptions.value[Number(event.detail.value)]?.language_code as
|
|
@@ -107,6 +150,15 @@ const onMfaDisabled = async () => {
|
|
|
107
150
|
uni.reLaunch({ url: '/pages/login/login' })
|
|
108
151
|
}
|
|
109
152
|
|
|
153
|
+
/** 返回个人中心,页面栈不可返回时回到首页。 */
|
|
154
|
+
const onBack = () => {
|
|
155
|
+
uni.navigateBack({
|
|
156
|
+
fail: () => {
|
|
157
|
+
uni.reLaunch({ url: '/pages/index/index' })
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
110
162
|
// 公共设置模块挂载时加载 MFA 状态,兼容页面包装和业务插槽场景。
|
|
111
163
|
onMounted(() => {
|
|
112
164
|
if (!userStore.ensureAuthenticated()) {
|
|
@@ -126,7 +178,8 @@ const onLogout = () => {
|
|
|
126
178
|
// 模态弹窗
|
|
127
179
|
uni.showModal({
|
|
128
180
|
content: t('core.settings.logout_confirm'),
|
|
129
|
-
confirmColor: '#
|
|
181
|
+
confirmColor: themeStore.theme === 'light' ? '#039e8c' : '#28f0d4',
|
|
182
|
+
cancelColor: themeStore.theme === 'light' ? '#5a7083' : '#8aa0b4',
|
|
130
183
|
success: async (res) => {
|
|
131
184
|
if (!res.confirm) {
|
|
132
185
|
return
|
|
@@ -153,37 +206,98 @@ const onLogout = () => {
|
|
|
153
206
|
</script>
|
|
154
207
|
|
|
155
208
|
<template>
|
|
156
|
-
<view class="
|
|
209
|
+
<view class="settings-page" :class="themeStore.themeClass">
|
|
157
210
|
<slot name="extensions" />
|
|
158
|
-
<view class="
|
|
159
|
-
<view
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
<view class="k-page-nav">
|
|
212
|
+
<view class="k-page-nav__back" :aria-label="t('common.action.back')" @tap="onBack"></view>
|
|
213
|
+
<view class="k-page-nav__title">{{ t('core.settings.title') }}</view>
|
|
214
|
+
<view class="k-page-nav__side" aria-hidden="true"></view>
|
|
215
|
+
</view>
|
|
216
|
+
<view class="settings-content">
|
|
217
|
+
<view class="k-set-cat">{{ t('core.settings.section_appearance') }}</view>
|
|
218
|
+
<picker :range="themeModeLabels" :value="themeModeIndex" @change="onThemeModeChange">
|
|
219
|
+
<view class="k-set-row k-set-row--arrow">
|
|
220
|
+
<view class="k-set-row__lead">
|
|
221
|
+
<view class="k-set-row__icon">
|
|
222
|
+
<image class="settings-row__icon" :src="settingsIcons.theme" mode="aspectFit" />
|
|
223
|
+
</view>
|
|
224
|
+
<text class="k-set-row__title">{{ t('core.settings.theme') }}</text>
|
|
225
|
+
</view>
|
|
226
|
+
<text class="k-set-row__value settings-row__value">{{
|
|
227
|
+
themeModeLabels[themeModeIndex]
|
|
228
|
+
}}</text>
|
|
229
|
+
</view>
|
|
230
|
+
</picker>
|
|
231
|
+
|
|
232
|
+
<view class="k-set-cat">{{ t('core.settings.section_security') }}</view>
|
|
233
|
+
<view v-if="userStore.isAuthenticated()" class="k-set-row k-set-row--arrow" @tap="onMfaTap">
|
|
234
|
+
<view class="k-set-row__lead">
|
|
235
|
+
<view class="k-set-row__icon">
|
|
236
|
+
<image class="settings-row__icon" :src="settingsIcons.mfa" mode="aspectFit" />
|
|
237
|
+
</view>
|
|
238
|
+
<text class="k-set-row__title">{{ t('core.settings.mfa_title') }}</text>
|
|
239
|
+
</view>
|
|
240
|
+
<text
|
|
241
|
+
class="k-set-row__value settings-row__value"
|
|
242
|
+
:class="{ 'k-set-row__value--on': mfaEnabled }"
|
|
243
|
+
>{{ mfaStatusText }}</text
|
|
244
|
+
>
|
|
162
245
|
</view>
|
|
163
246
|
<picker :range="localeLabels" :value="localeIndex" @change="onLocaleChange">
|
|
164
|
-
<view class="
|
|
165
|
-
<
|
|
166
|
-
|
|
247
|
+
<view class="k-set-row k-set-row--arrow">
|
|
248
|
+
<view class="k-set-row__lead">
|
|
249
|
+
<view class="k-set-row__icon">
|
|
250
|
+
<image class="settings-row__icon" :src="settingsIcons.language" mode="aspectFit" />
|
|
251
|
+
</view>
|
|
252
|
+
<text class="k-set-row__title">{{ t('common.field.language') }}</text>
|
|
253
|
+
</view>
|
|
254
|
+
<text class="k-set-row__value settings-row__value">{{ localeLabels[localeIndex] }}</text>
|
|
167
255
|
</view>
|
|
168
256
|
</picker>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
257
|
+
|
|
258
|
+
<!-- #ifdef MP-WEIXIN -->
|
|
259
|
+
<view class="k-set-cat">{{ t('core.settings.section_more') }}</view>
|
|
260
|
+
<button
|
|
261
|
+
hover-class="none"
|
|
262
|
+
class="k-set-row k-set-row--arrow settings-row--button"
|
|
263
|
+
open-type="openSetting"
|
|
264
|
+
>
|
|
265
|
+
<view class="k-set-row__lead">
|
|
266
|
+
<view class="k-set-row__icon">
|
|
267
|
+
<image class="settings-row__icon" :src="settingsIcons.authorization" mode="aspectFit" />
|
|
268
|
+
</view>
|
|
269
|
+
<text class="k-set-row__title">{{ t('core.settings.authorization') }}</text>
|
|
270
|
+
</view>
|
|
175
271
|
</button>
|
|
176
|
-
<button
|
|
177
|
-
|
|
272
|
+
<button
|
|
273
|
+
hover-class="none"
|
|
274
|
+
class="k-set-row k-set-row--arrow settings-row--button"
|
|
275
|
+
open-type="feedback"
|
|
276
|
+
>
|
|
277
|
+
<view class="k-set-row__lead">
|
|
278
|
+
<view class="k-set-row__icon">
|
|
279
|
+
<image class="settings-row__icon" :src="settingsIcons.feedback" mode="aspectFit" />
|
|
280
|
+
</view>
|
|
281
|
+
<text class="k-set-row__title">{{ t('core.settings.feedback') }}</text>
|
|
282
|
+
</view>
|
|
178
283
|
</button>
|
|
179
|
-
<button
|
|
180
|
-
|
|
284
|
+
<button
|
|
285
|
+
hover-class="none"
|
|
286
|
+
class="k-set-row k-set-row--arrow settings-row--button"
|
|
287
|
+
open-type="contact"
|
|
288
|
+
>
|
|
289
|
+
<view class="k-set-row__lead">
|
|
290
|
+
<view class="k-set-row__icon">
|
|
291
|
+
<image class="settings-row__icon" :src="settingsIcons.contact" mode="aspectFit" />
|
|
292
|
+
</view>
|
|
293
|
+
<text class="k-set-row__title">{{ t('core.settings.contact') }}</text>
|
|
294
|
+
</view>
|
|
181
295
|
</button>
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
296
|
+
<!-- #endif -->
|
|
297
|
+
|
|
298
|
+
<view v-if="userStore.isAuthenticated()" class="k-set-logout" @tap="onLogout">{{
|
|
299
|
+
t('common.action.logout')
|
|
300
|
+
}}</view>
|
|
187
301
|
</view>
|
|
188
302
|
</view>
|
|
189
303
|
<MfaManageDialog
|
|
@@ -196,101 +310,39 @@ const onLogout = () => {
|
|
|
196
310
|
</template>
|
|
197
311
|
|
|
198
312
|
<style scoped lang="scss">
|
|
199
|
-
.
|
|
313
|
+
.settings-page {
|
|
200
314
|
min-height: 100vh;
|
|
201
|
-
padding: 20rpx;
|
|
202
315
|
box-sizing: border-box;
|
|
203
|
-
|
|
316
|
+
padding: 0 32rpx calc(40rpx + env(safe-area-inset-bottom));
|
|
317
|
+
background: var(--kratos-bg-gradient);
|
|
204
318
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.list {
|
|
208
|
-
padding: 0 20rpx;
|
|
209
|
-
background-color: #fff;
|
|
210
|
-
margin-bottom: 20rpx;
|
|
211
|
-
border-radius: 10rpx;
|
|
212
|
-
.item {
|
|
213
|
-
line-height: 90rpx;
|
|
214
|
-
padding-left: 10rpx;
|
|
215
|
-
font-size: 30rpx;
|
|
216
|
-
color: #333;
|
|
217
|
-
border-top: 1rpx solid #ddd;
|
|
218
|
-
position: relative;
|
|
219
|
-
text-align: left;
|
|
220
|
-
border-radius: 0;
|
|
221
|
-
background-color: #fff;
|
|
222
|
-
&::after {
|
|
223
|
-
width: auto;
|
|
224
|
-
height: auto;
|
|
225
|
-
left: auto;
|
|
226
|
-
border: none;
|
|
227
|
-
}
|
|
228
|
-
&::after {
|
|
229
|
-
right: 5rpx;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
> .item:first-child {
|
|
233
|
-
border: none;
|
|
234
|
-
}
|
|
235
|
-
.arrow::after {
|
|
236
|
-
content: '›';
|
|
237
|
-
position: absolute;
|
|
238
|
-
top: 50%;
|
|
239
|
-
color: #ccc;
|
|
240
|
-
font-size: 36rpx;
|
|
241
|
-
transform: translateY(-50%);
|
|
242
|
-
}
|
|
319
|
+
.settings-content {
|
|
320
|
+
padding-bottom: 20rpx;
|
|
243
321
|
}
|
|
244
322
|
|
|
245
|
-
.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
padding-right: 42rpx;
|
|
250
|
-
|
|
251
|
-
> text:first-child {
|
|
252
|
-
flex-shrink: 0;
|
|
253
|
-
}
|
|
323
|
+
/* 行图标图片尺寸(挂在 .k-set-row__icon 内的 image 上) */
|
|
324
|
+
.settings-row__icon {
|
|
325
|
+
width: 38rpx;
|
|
326
|
+
height: 38rpx;
|
|
254
327
|
}
|
|
255
328
|
|
|
256
|
-
.
|
|
257
|
-
|
|
329
|
+
/* 行尾值文本截断(配合全局 .k-set-row__value 使用) */
|
|
330
|
+
.settings-row__value {
|
|
258
331
|
min-width: 0;
|
|
332
|
+
max-width: 45%;
|
|
259
333
|
overflow: hidden;
|
|
260
|
-
margin-left: 24rpx;
|
|
261
|
-
color: #888;
|
|
262
|
-
text-align: right;
|
|
263
334
|
text-overflow: ellipsis;
|
|
264
335
|
white-space: nowrap;
|
|
265
336
|
}
|
|
266
337
|
|
|
267
|
-
.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
justify-content: space-between;
|
|
271
|
-
padding-right: 42rpx;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
.mfa-value {
|
|
275
|
-
max-width: 55%;
|
|
276
|
-
overflow: hidden;
|
|
277
|
-
color: #888;
|
|
278
|
-
font-size: 26rpx;
|
|
279
|
-
text-overflow: ellipsis;
|
|
280
|
-
white-space: nowrap;
|
|
338
|
+
/* 小程序 button 皮肤为 .k-set-row:去掉原生边框,保留箭头 ::after */
|
|
339
|
+
.settings-row--button {
|
|
340
|
+
line-height: normal;
|
|
281
341
|
}
|
|
282
342
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
margin-top: 40rpx;
|
|
288
|
-
font-size: 32rpx;
|
|
289
|
-
color: #333;
|
|
290
|
-
.button {
|
|
291
|
-
background-color: #fff;
|
|
292
|
-
margin-bottom: 20rpx;
|
|
293
|
-
border-radius: 10rpx;
|
|
294
|
-
}
|
|
343
|
+
.settings-row--button::after {
|
|
344
|
+
position: static;
|
|
345
|
+
border-left: 0;
|
|
346
|
+
border-bottom: 0;
|
|
295
347
|
}
|
|
296
348
|
</style>
|
|
@@ -3,11 +3,13 @@ import { computed, onMounted } from 'vue'
|
|
|
3
3
|
import { resolveModuleIcon, resolveStaticView } from '../module'
|
|
4
4
|
import { APP_MENU_ROOT_ID, useAppMenuBadge, useAppNavigation } from '../navigation'
|
|
5
5
|
import { useI18n } from '../locales'
|
|
6
|
+
import { useThemeStore } from '../stores'
|
|
6
7
|
import { resolveRootMenuId } from '../navigation-tree.mjs'
|
|
7
8
|
|
|
8
9
|
const props = defineProps<{ route: string }>()
|
|
9
10
|
const { menus, tabBar, navigate } = useAppNavigation()
|
|
10
11
|
const { t } = useI18n()
|
|
12
|
+
const themeStore = useThemeStore()
|
|
11
13
|
|
|
12
14
|
/** 按稳定 viewKey 优先取界面语言标签,未配置的菜单回退后端标题。 */
|
|
13
15
|
const tabBarTitleKeys: Record<string, string> = {
|
|
@@ -19,6 +21,30 @@ const tabBarTitleKeys: Record<string, string> = {
|
|
|
19
21
|
}
|
|
20
22
|
const tabBarTitle = (item: { viewKey: string; title: string }): string =>
|
|
21
23
|
(tabBarTitleKeys[item.viewKey] ? t(tabBarTitleKeys[item.viewKey]) : '') || item.title
|
|
24
|
+
/** 设计稿导航坞线稿图标(viewKey → SVG path)。 */
|
|
25
|
+
const TAB_ICON_PATHS: Record<string, string> = {
|
|
26
|
+
HOME: 'M3 11 12 3l9 8M5 10v10h14V10',
|
|
27
|
+
APP_ANALYTIC: 'M4 20V10M10 20V4M16 20v-7M21 20H3',
|
|
28
|
+
APP_REPORT: 'M6 4h9l5 5v11H6zM14 4v5h5',
|
|
29
|
+
MESSAGE_INBOX: 'M4 5h16v12H8l-4 4z',
|
|
30
|
+
PROFILE_HOME: 'M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM4 21c0-4 3.5-6 8-6s8 2 8 6',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 按设计稿线稿生成随主题与激活态取色的图标地址,未知 viewKey 返回空串走后端图标兜底。 */
|
|
34
|
+
const tabIconSrc = (viewKey: string, active: boolean): string => {
|
|
35
|
+
const path = TAB_ICON_PATHS[viewKey]
|
|
36
|
+
if (!path) return ''
|
|
37
|
+
const color = active
|
|
38
|
+
? themeStore.theme === 'light'
|
|
39
|
+
? '#039e8c'
|
|
40
|
+
: '#28f0d4'
|
|
41
|
+
: themeStore.theme === 'light'
|
|
42
|
+
? '#9aabbb'
|
|
43
|
+
: '#4e6378'
|
|
44
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="${path}"/></svg>`
|
|
45
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
const activeMenu = computed(() => {
|
|
23
49
|
const routeMenu = menus.value.find((menu) => resolveStaticView(menu.viewKey) === props.route)
|
|
24
50
|
if (!routeMenu) return
|
|
@@ -36,11 +62,12 @@ onMounted(() => {
|
|
|
36
62
|
</script>
|
|
37
63
|
|
|
38
64
|
<template>
|
|
39
|
-
<view v-if="visible" class="kratos-tab-bar">
|
|
65
|
+
<view v-if="visible" class="kratos-tab-bar" :class="themeStore.themeClass">
|
|
40
66
|
<view
|
|
41
67
|
v-for="item in tabBar"
|
|
42
68
|
:key="item.id"
|
|
43
69
|
class="kratos-tab-bar__item"
|
|
70
|
+
:class="{ 'kratos-tab-bar__item--active': activeMenu?.id === item.id }"
|
|
44
71
|
@tap="navigate(item.path)"
|
|
45
72
|
>
|
|
46
73
|
<view
|
|
@@ -49,7 +76,13 @@ onMounted(() => {
|
|
|
49
76
|
aria-hidden="true"
|
|
50
77
|
/>
|
|
51
78
|
<image
|
|
52
|
-
v-if="
|
|
79
|
+
v-if="tabIconSrc(item.viewKey, activeMenu?.id === item.id)"
|
|
80
|
+
class="kratos-tab-bar__icon"
|
|
81
|
+
:src="tabIconSrc(item.viewKey, activeMenu?.id === item.id)"
|
|
82
|
+
mode="aspectFit"
|
|
83
|
+
/>
|
|
84
|
+
<image
|
|
85
|
+
v-else-if="resolveModuleIcon(item.icon)"
|
|
53
86
|
class="kratos-tab-bar__icon"
|
|
54
87
|
:src="
|
|
55
88
|
resolveModuleIcon(
|
|
@@ -73,20 +106,22 @@ onMounted(() => {
|
|
|
73
106
|
|
|
74
107
|
<style scoped>
|
|
75
108
|
.kratos-tab-bar {
|
|
76
|
-
--kratos-tab-bar-color: #
|
|
77
|
-
--kratos-tab-bar-active-color: #
|
|
109
|
+
--kratos-tab-bar-color: var(--kratos-color-text-dim, #4e6378);
|
|
110
|
+
--kratos-tab-bar-active-color: var(--kratos-color-primary, #28f0d4);
|
|
78
111
|
|
|
79
112
|
position: fixed;
|
|
80
|
-
right:
|
|
81
|
-
bottom:
|
|
82
|
-
left:
|
|
113
|
+
right: 20rpx;
|
|
114
|
+
bottom: env(safe-area-inset-bottom);
|
|
115
|
+
left: 20rpx;
|
|
83
116
|
z-index: 999;
|
|
84
|
-
box-sizing: border-box;
|
|
85
117
|
display: flex;
|
|
86
|
-
height:
|
|
87
|
-
padding
|
|
88
|
-
border
|
|
89
|
-
|
|
118
|
+
height: 104rpx;
|
|
119
|
+
padding: 0 8rpx;
|
|
120
|
+
border: 1rpx solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
|
|
121
|
+
border-radius: 30rpx;
|
|
122
|
+
background: var(--kratos-dock-bg, rgba(10, 18, 30, 0.72));
|
|
123
|
+
box-shadow: 0 24rpx 64rpx rgba(0, 0, 0, 0.45);
|
|
124
|
+
backdrop-filter: blur(32rpx);
|
|
90
125
|
}
|
|
91
126
|
.kratos-tab-bar__item {
|
|
92
127
|
position: relative;
|
|
@@ -95,11 +130,24 @@ onMounted(() => {
|
|
|
95
130
|
align-items: center;
|
|
96
131
|
justify-content: center;
|
|
97
132
|
flex-direction: column;
|
|
98
|
-
transform: translateY(clamp(1px, env(safe-area-inset-bottom), 7px));
|
|
99
133
|
color: var(--kratos-tab-bar-color);
|
|
100
134
|
font-size: 10px;
|
|
101
135
|
line-height: 12px;
|
|
102
136
|
}
|
|
137
|
+
|
|
138
|
+
/* 设计稿:激活项顶部霓虹发光条 */
|
|
139
|
+
.kratos-tab-bar__item--active::before {
|
|
140
|
+
position: absolute;
|
|
141
|
+
top: 0;
|
|
142
|
+
left: 50%;
|
|
143
|
+
width: 26px;
|
|
144
|
+
height: 3px;
|
|
145
|
+
border-radius: 3px;
|
|
146
|
+
background: var(--kratos-tab-bar-active-color);
|
|
147
|
+
box-shadow: 0 0 12px var(--kratos-tab-bar-active-color);
|
|
148
|
+
content: '';
|
|
149
|
+
transform: translateX(-50%);
|
|
150
|
+
}
|
|
103
151
|
.kratos-tab-bar__icon {
|
|
104
152
|
width: 28px;
|
|
105
153
|
height: 28px;
|
|
@@ -135,7 +183,7 @@ onMounted(() => {
|
|
|
135
183
|
height: 7px;
|
|
136
184
|
border-right: 2px solid var(--kratos-tab-bar-color);
|
|
137
185
|
border-bottom: 2px solid var(--kratos-tab-bar-color);
|
|
138
|
-
background:
|
|
186
|
+
background: var(--kratos-dock-bg, rgba(10, 18, 30, 0.72));
|
|
139
187
|
content: '';
|
|
140
188
|
transform: rotate(45deg);
|
|
141
189
|
}
|
|
@@ -153,6 +201,8 @@ onMounted(() => {
|
|
|
153
201
|
}
|
|
154
202
|
.kratos-tab-bar__text--active {
|
|
155
203
|
color: var(--kratos-tab-bar-active-color);
|
|
204
|
+
font-weight: 700;
|
|
205
|
+
text-shadow: 0 0 12px currentColor;
|
|
156
206
|
}
|
|
157
207
|
.kratos-tab-bar__badge {
|
|
158
208
|
position: absolute;
|
|
@@ -161,8 +211,9 @@ onMounted(() => {
|
|
|
161
211
|
width: 10px;
|
|
162
212
|
height: 10px;
|
|
163
213
|
box-sizing: border-box;
|
|
164
|
-
border: 2px solid
|
|
214
|
+
border: 2px solid var(--kratos-dock-bg, rgba(10, 18, 30, 0.72));
|
|
165
215
|
border-radius: 50%;
|
|
166
|
-
background: #
|
|
216
|
+
background: var(--kratos-color-danger, #ff4d6d);
|
|
217
|
+
box-shadow: 0 0 12px var(--kratos-color-danger, #ff4d6d);
|
|
167
218
|
}
|
|
168
219
|
</style>
|