@liujitcn/kratos-uni-app-core 0.0.81 → 0.0.82

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 CHANGED
@@ -3,7 +3,6 @@ var corePages = {
3
3
  "pages/index/index": {
4
4
  style: {
5
5
  navigationStyle: "custom",
6
- navigationBarTextStyle: "white",
7
6
  navigationBarTitleText: ""
8
7
  }
9
8
  },
@@ -61,18 +60,20 @@ var zh_CN_default = {
61
60
  "core.password.verify_label": "\u5F53\u524D\u5BC6\u7801",
62
61
  "core.file.upload_failed": "\u4E0A\u4F20\u5931\u8D25",
63
62
  "core.home.account_capability": "\u8D26\u6237\u80FD\u529B",
64
- "core.home.account_capability_description": "\u4E2A\u4EBA\u8D44\u6599\u3001\u5E94\u7528\u8BBE\u7F6E\u4E0E\u667A\u80FD\u52A9\u624B\u5165\u53E3\u5DF2\u4FDD\u7559",
63
+ "core.home.account_capability_description": "\u767B\u5F55\u3001\u4E2A\u4EBA\u8D44\u6599\u3001\u591A\u56E0\u7D20\u8BA4\u8BC1\u4E0E\u5E94\u7528\u8BBE\u7F6E\u5F00\u7BB1\u5373\u7528",
65
64
  "core.home.cross_platform": "\u8DE8\u7AEF\u9875\u9762",
66
65
  "core.home.cross_platform_description": "\u540C\u4E00\u5957\u9875\u9762\u4EE3\u7801\u9002\u914D H5 \u548C\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F",
67
66
  "core.home.demo_scope": "\u529F\u80FD\u6982\u89C8",
67
+ "core.home.dynamic_config": "\u52A8\u6001\u914D\u7F6E",
68
+ "core.home.dynamic_config_description": "\u9996\u9875 Logo \u4E0E\u6807\u8BED\u652F\u6301\u540E\u53F0\u8FDC\u7A0B\u914D\u7F6E",
68
69
  "core.home.framework": "\u5E94\u7528\u6846\u67B6",
69
70
  "core.home.language": "\u5F00\u53D1\u8BED\u8A00",
70
71
  "core.home.main_title": "\u5E94\u7528\u9996\u9875",
72
+ "core.home.message_center": "\u6D88\u606F\u4E2D\u5FC3",
73
+ "core.home.message_center_description": "\u672A\u8BFB\u6D88\u606F\u7EDF\u4E00\u6536\u7EB3\uFF0C\u7EA2\u70B9\u63D0\u9192\u4E0D\u9519\u8FC7",
71
74
  "core.home.state_management": "\u72B6\u6001\u7BA1\u7406",
72
- "core.home.static_home": "\u9759\u6001\u9996\u9875",
73
- "core.home.static_home_description": "\u5F53\u524D\u9996\u9875\u7528\u4E8E\u5C55\u793A\u6846\u67B6\u4FE1\u606F\uFF0C\u4E0D\u4F9D\u8D56\u4E1A\u52A1\u63A5\u53E3",
74
75
  "core.home.style_solution": "\u6837\u5F0F\u65B9\u6848",
75
- "core.home.sub_title": "\u4FDD\u7559\u901A\u7528\u5BFC\u822A\u4E0E\u4E2A\u4EBA\u4E2D\u5FC3\u4F53\u9A8C",
76
+ "core.home.sub_title": "\u8D26\u53F7\u3001\u6D88\u606F\u4E0E\u667A\u80FD\u52A9\u624B\uFF0C\u4E00\u7AD9\u89E6\u8FBE",
76
77
  "core.home.tech_stack": "\u5F53\u524D\u6280\u672F\u6808",
77
78
  "core.login.agree_prefix": "\u6211\u5DF2\u9605\u8BFB\u5E76\u540C\u610F",
78
79
  "core.login.agree_separator": "\u548C",
@@ -218,18 +219,20 @@ var en_US_default = {
218
219
  "core.password.verify_label": "Current password",
219
220
  "core.file.upload_failed": "Upload failed",
220
221
  "core.home.account_capability": "Account features",
221
- "core.home.account_capability_description": "Profile, app settings, and AI assistant access are included",
222
+ "core.home.account_capability_description": "Sign-in, profile, multi-factor authentication, and app settings out of the box",
222
223
  "core.home.cross_platform": "Cross-platform pages",
223
224
  "core.home.cross_platform_description": "One codebase for H5 and WeChat Mini Program",
224
225
  "core.home.demo_scope": "Overview",
226
+ "core.home.dynamic_config": "Dynamic config",
227
+ "core.home.dynamic_config_description": "Home logo and tagline configurable from the admin console",
225
228
  "core.home.framework": "App framework",
226
229
  "core.home.language": "Language",
227
230
  "core.home.main_title": "Application Home",
231
+ "core.home.message_center": "Message center",
232
+ "core.home.message_center_description": "Unread messages in one inbox with badge reminders",
228
233
  "core.home.state_management": "State management",
229
- "core.home.static_home": "Static home",
230
- "core.home.static_home_description": "This page presents framework details without business APIs",
231
234
  "core.home.style_solution": "Styling",
232
- "core.home.sub_title": "Shared navigation and account experience",
235
+ "core.home.sub_title": "Accounts, messages, and AI assistant in one place",
233
236
  "core.home.tech_stack": "Technology stack",
234
237
  "core.login.agree_prefix": "I have read and agree to the",
235
238
  "core.login.agree_separator": "and",
@@ -375,18 +378,20 @@ var ja_JP_default = {
375
378
  "core.password.verify_label": "\u73FE\u5728\u306E\u30D1\u30B9\u30EF\u30FC\u30C9",
376
379
  "core.file.upload_failed": "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
377
380
  "core.home.account_capability": "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u6A5F\u80FD",
378
- "core.home.account_capability_description": "\u500B\u4EBA\u60C5\u5831\u3001\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u8A2D\u5B9A\u3001\u30B9\u30DE\u30FC\u30C8\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u5165\u53E3\u304C\u4E88\u7D04\u3055\u308C\u3066\u3044\u307E\u3059",
381
+ "core.home.account_capability_description": "\u30ED\u30B0\u30A4\u30F3\u3001\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3001\u591A\u8981\u7D20\u8A8D\u8A3C\u3001\u30A2\u30D7\u30EA\u8A2D\u5B9A\u3092\u540C\u68B1",
379
382
  "core.home.cross_platform": "\u30AF\u30ED\u30B9\u30A8\u30F3\u30C9 \u30DA\u30FC\u30B8",
380
383
  "core.home.cross_platform_description": "\u540C\u3058\u30DA\u30FC\u30B8 \u30B3\u30FC\u30C9 \u30BB\u30C3\u30C8\u304C H5 \u304A\u3088\u3073 WeChat \u30DF\u30CB \u30D7\u30ED\u30B0\u30E9\u30E0\u306B\u9069\u5FDC\u3057\u307E\u3059",
381
384
  "core.home.demo_scope": "\u6A5F\u80FD\u6982\u8981",
385
+ "core.home.dynamic_config": "\u52D5\u7684\u8A2D\u5B9A",
386
+ "core.home.dynamic_config_description": "\u30DB\u30FC\u30E0\u306E\u30ED\u30B4\u3068\u30AD\u30E3\u30C3\u30C1\u30B3\u30D4\u30FC\u306F\u7BA1\u7406\u753B\u9762\u304B\u3089\u5909\u66F4\u53EF\u80FD",
382
387
  "core.home.framework": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3 \u30D5\u30EC\u30FC\u30E0\u30EF\u30FC\u30AF",
383
388
  "core.home.language": "\u958B\u767A\u8A00\u8A9E",
384
389
  "core.home.main_title": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30DB\u30FC\u30E0",
390
+ "core.home.message_center": "\u30E1\u30C3\u30BB\u30FC\u30B8\u30BB\u30F3\u30BF\u30FC",
391
+ "core.home.message_center_description": "\u672A\u8AAD\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u4E00\u62EC\u7BA1\u7406\u3057\u3001\u30D0\u30C3\u30B8\u3067\u304A\u77E5\u3089\u305B",
385
392
  "core.home.state_management": "\u30B9\u30C6\u30FC\u30BF\u30B9\u7BA1\u7406",
386
- "core.home.static_home": "\u9759\u7684\u30DB\u30FC\u30E0\u30DA\u30FC\u30B8",
387
- "core.home.static_home_description": "\u73FE\u5728\u306E\u30DB\u30FC\u30E0\u30DA\u30FC\u30B8\u306F\u30D5\u30EC\u30FC\u30E0\u60C5\u5831\u3092\u8868\u793A\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3055\u308C\u3066\u304A\u308A\u3001\u30D3\u30B8\u30CD\u30B9 \u30A4\u30F3\u30BF\u30FC\u30D5\u30A7\u30A4\u30B9\u306B\u306F\u4F9D\u5B58\u3057\u3066\u3044\u307E\u305B\u3093",
388
393
  "core.home.style_solution": "\u30B9\u30BF\u30A4\u30EB \u30B9\u30AD\u30FC\u30E0",
389
- "core.home.sub_title": "\u30E6\u30CB\u30D0\u30FC\u30B5\u30EB \u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u3068\u30D1\u30FC\u30BD\u30CA\u30EB \u30BB\u30F3\u30BF\u30FC\u306E\u30A8\u30AF\u30B9\u30DA\u30EA\u30A8\u30F3\u30B9\u3092\u7DAD\u6301\u3057\u307E\u3059",
394
+ "core.home.sub_title": "\u30A2\u30AB\u30A6\u30F3\u30C8\u30FB\u30E1\u30C3\u30BB\u30FC\u30B8\u30FB\u30B9\u30DE\u30FC\u30C8\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3092\u3072\u3068\u3064\u306B",
390
395
  "core.home.tech_stack": "\u73FE\u5728\u306E\u30C6\u30AF\u30CE\u30ED\u30B8\u30FC\u30B9\u30BF\u30C3\u30AF",
391
396
  "core.login.agree_prefix": "\u8AAD\u3093\u3067\u540C\u610F\u3057\u307E\u3059",
392
397
  "core.login.agree_separator": "\u304A\u3088\u3073",
@@ -532,18 +537,20 @@ var zh_TW_default = {
532
537
  "core.password.verify_label": "\u7576\u524D\u5BC6\u78BC",
533
538
  "core.file.upload_failed": "\u4E0A\u50B3\u5931\u6557",
534
539
  "core.home.account_capability": "\u8CEC\u6236\u80FD\u529B",
535
- "core.home.account_capability_description": "\u500B\u4EBA\u8CC7\u6599\u3001\u61C9\u7528\u8A2D\u5B9A\u8207\u667A\u6167\u52A9\u624B\u5165\u53E3\u5DF2\u4FDD\u7559",
540
+ "core.home.account_capability_description": "\u767B\u5165\u3001\u500B\u4EBA\u8CC7\u6599\u3001\u591A\u56E0\u7D20\u8A8D\u8B49\u8207\u61C9\u7528\u8A2D\u5B9A\u958B\u7BB1\u5373\u7528",
536
541
  "core.home.cross_platform": "\u8DE8\u7AEF\u9801\u9762",
537
542
  "core.home.cross_platform_description": "\u540C\u4E00\u5957\u9801\u9762\u7A0B\u5F0F\u78BC\u9069\u914D H5 \u548C\u5FAE\u4FE1\u5C0F\u7A0B\u5F0F",
538
543
  "core.home.demo_scope": "\u529F\u80FD\u6982\u89BD",
544
+ "core.home.dynamic_config": "\u52D5\u614B\u8A2D\u5B9A",
545
+ "core.home.dynamic_config_description": "\u9996\u9801 Logo \u8207\u6A19\u8A9E\u652F\u63F4\u5F8C\u53F0\u9060\u7AEF\u8A2D\u5B9A",
539
546
  "core.home.framework": "\u61C9\u7528\u6846\u67B6",
540
547
  "core.home.language": "\u958B\u767C\u8A9E\u8A00",
541
548
  "core.home.main_title": "\u61C9\u7528\u9996\u9801",
549
+ "core.home.message_center": "\u8A0A\u606F\u4E2D\u5FC3",
550
+ "core.home.message_center_description": "\u672A\u8B80\u8A0A\u606F\u7D71\u4E00\u67E5\u770B\uFF0C\u7D05\u9EDE\u63D0\u9192\u4E0D\u932F\u904E",
542
551
  "core.home.state_management": "\u72C0\u614B\u7BA1\u7406",
543
- "core.home.static_home": "\u975C\u614B\u9996\u9801",
544
- "core.home.static_home_description": "\u7576\u524D\u9996\u9801\u7528\u65BC\u5C55\u793A\u6846\u67B6\u8CC7\u8A0A\uFF0C\u4E0D\u4F9D\u8CF4\u696D\u52D9\u4ECB\u9762",
545
552
  "core.home.style_solution": "\u6A23\u5F0F\u65B9\u6848",
546
- "core.home.sub_title": "\u4FDD\u7559\u901A\u7528\u5C0E\u822A\u8207\u500B\u4EBA\u4E2D\u5FC3\u9AD4\u9A57",
553
+ "core.home.sub_title": "\u5E33\u865F\u3001\u8A0A\u606F\u8207\u667A\u6167\u52A9\u624B\uFF0C\u4E00\u7AD9\u89F8\u9054",
547
554
  "core.home.tech_stack": "\u7576\u524D\u6280\u8853\u68E7",
548
555
  "core.login.agree_prefix": "\u6211\u5DF2\u95B1\u8B80\u4E26\u540C\u610F",
549
556
  "core.login.agree_separator": "\u548C",
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git@github.com:liujitcn/kratos-admin.git"
9
9
  },
10
- "version": "0.0.81",
10
+ "version": "0.0.82",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ defineOptions({ name: 'KratosEmpty' })
3
+
4
+ withDefaults(
5
+ defineProps<{
6
+ /** 图标名称(uni-icons),不传则不渲染图标位。 */
7
+ icon?: string
8
+ /** 主文案。 */
9
+ title?: string
10
+ /** 次级提示文案。 */
11
+ description?: string
12
+ }>(),
13
+ {
14
+ icon: '',
15
+ title: '',
16
+ description: '',
17
+ },
18
+ )
19
+
20
+ defineSlots<{
21
+ /** 操作区插槽:渲染引导按钮等自定义内容。 */
22
+ default?: () => unknown
23
+ }>()
24
+ </script>
25
+
26
+ <template>
27
+ <view class="k-empty">
28
+ <view v-if="icon" class="k-empty__icon">
29
+ <uni-icons :type="icon" size="26" color="var(--kratos-color-primary)" aria-hidden="true" />
30
+ </view>
31
+ <text v-if="title" class="k-empty__title">{{ title }}</text>
32
+ <text v-if="description" class="k-empty__desc">{{ description }}</text>
33
+ <slot />
34
+ </view>
35
+ </template>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, onMounted } from 'vue'
2
+ import { computed, onBeforeMount, onMounted } from 'vue'
3
3
  import { resolveModuleIcon, resolveStaticView } from '../module'
4
4
  import { APP_MENU_ROOT_ID, useAppMenuBadge, useAppNavigation } from '../navigation'
5
5
  import { useThemeStore } from '../stores'
@@ -43,9 +43,12 @@ const visible = computed(() => Boolean(activeMenu.value && tabBar.value.length))
43
43
  const messageBadge = useAppMenuBadge('MESSAGE_INBOX')
44
44
  // #ifdef H5
45
45
  // H5 端 tabBar.custom 不隐藏内置 tabbar,需手动隐藏,否则其边线从自定义栏上方露出形成双线。
46
- onMounted(() => {
46
+ const hideNativeTabBar = () => {
47
47
  uni.hideTabBar({ fail: () => {} })
48
- })
48
+ }
49
+ onBeforeMount(hideNativeTabBar)
50
+ // H5 runtime 可能在页面挂载时重新同步内置 tabbar 状态,挂载后幂等重试避免首次切换露出一帧。
51
+ onMounted(hideNativeTabBar)
49
52
  // #endif
50
53
  </script>
51
54
 
@@ -105,7 +108,7 @@ onMounted(() => {
105
108
  display: flex;
106
109
  height: 104rpx;
107
110
  padding: 0 8rpx;
108
- border: 1rpx solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
111
+ border: 1px solid var(--kratos-color-border-accent, rgba(120, 230, 215, 0.16));
109
112
  border-radius: 30rpx;
110
113
  background: var(--kratos-dock-bg, rgba(10, 18, 30, 0.72));
111
114
  box-shadow: 0 24rpx 64rpx rgba(0, 0, 0, 0.45);
@@ -123,20 +126,8 @@ onMounted(() => {
123
126
  line-height: 12px;
124
127
  }
125
128
 
126
- /* 设计稿:激活项顶部霓虹发光条 */
127
- .kratos-tab-bar__item--active::before {
128
- position: absolute;
129
- top: 0;
130
- left: 50%;
131
- width: 26px;
132
- height: 3px;
133
- border-radius: 3px;
134
- background: var(--kratos-tab-bar-active-color);
135
- box-shadow: 0 0 12px var(--kratos-tab-bar-active-color);
136
- content: '';
137
- transform: translateX(-50%);
138
- }
139
129
  .kratos-tab-bar__icon {
130
+ flex-shrink: 0;
140
131
  width: 28px;
141
132
  height: 28px;
142
133
  margin-bottom: 4px;
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ export * from './navigation'
5
5
  /** KratosSettingsPage 可通过顶部插槽扩展业务内容的公共设置页。 */
6
6
  export { default as KratosSettingsPage } from './components/KratosSettingsPage.vue'
7
7
  export { default as KratosTabBar } from './components/KratosTabBar.vue'
8
+ /** KratosEmpty 标准空态组件,支持自定义图标、主文案与提示文案。 */
9
+ export { default as KratosEmpty } from './components/KratosEmpty.vue'
8
10
  export { default as pinia } from './stores'
9
11
  export * from './stores'
10
12
  export * from './utils/secretCrypto'
@@ -33,18 +33,20 @@
33
33
  "core.password.verify_label": "Current password",
34
34
  "core.file.upload_failed": "Upload failed",
35
35
  "core.home.account_capability": "Account features",
36
- "core.home.account_capability_description": "Profile, app settings, and AI assistant access are included",
36
+ "core.home.account_capability_description": "Sign-in, profile, multi-factor authentication, and app settings out of the box",
37
37
  "core.home.cross_platform": "Cross-platform pages",
38
38
  "core.home.cross_platform_description": "One codebase for H5 and WeChat Mini Program",
39
39
  "core.home.demo_scope": "Overview",
40
+ "core.home.dynamic_config": "Dynamic config",
41
+ "core.home.dynamic_config_description": "Home logo and tagline configurable from the admin console",
40
42
  "core.home.framework": "App framework",
41
43
  "core.home.language": "Language",
42
44
  "core.home.main_title": "Application Home",
45
+ "core.home.message_center": "Message center",
46
+ "core.home.message_center_description": "Unread messages in one inbox with badge reminders",
43
47
  "core.home.state_management": "State management",
44
- "core.home.static_home": "Static home",
45
- "core.home.static_home_description": "This page presents framework details without business APIs",
46
48
  "core.home.style_solution": "Styling",
47
- "core.home.sub_title": "Shared navigation and account experience",
49
+ "core.home.sub_title": "Accounts, messages, and AI assistant in one place",
48
50
  "core.home.tech_stack": "Technology stack",
49
51
  "core.login.agree_prefix": "I have read and agree to the",
50
52
  "core.login.agree_separator": "and",
@@ -33,18 +33,20 @@
33
33
  "core.password.verify_label": "現在のパスワード",
34
34
  "core.file.upload_failed": "アップロードに失敗しました",
35
35
  "core.home.account_capability": "アカウントの機能",
36
- "core.home.account_capability_description": "個人情報、アプリケーション設定、スマートアシスタント入口が予約されています",
36
+ "core.home.account_capability_description": "ログイン、プロフィール、多要素認証、アプリ設定を同梱",
37
37
  "core.home.cross_platform": "クロスエンド ページ",
38
38
  "core.home.cross_platform_description": "同じページ コード セットが H5 および WeChat ミニ プログラムに適応します",
39
39
  "core.home.demo_scope": "機能概要",
40
+ "core.home.dynamic_config": "動的設定",
41
+ "core.home.dynamic_config_description": "ホームのロゴとキャッチコピーは管理画面から変更可能",
40
42
  "core.home.framework": "アプリケーション フレームワーク",
41
43
  "core.home.language": "開発言語",
42
44
  "core.home.main_title": "アプリケーションホーム",
45
+ "core.home.message_center": "メッセージセンター",
46
+ "core.home.message_center_description": "未読メッセージを一括管理し、バッジでお知らせ",
43
47
  "core.home.state_management": "ステータス管理",
44
- "core.home.static_home": "静的ホームページ",
45
- "core.home.static_home_description": "現在のホームページはフレーム情報を表示するために使用されており、ビジネス インターフェイスには依存していません",
46
48
  "core.home.style_solution": "スタイル スキーム",
47
- "core.home.sub_title": "ユニバーサル ナビゲーションとパーソナル センターのエクスペリエンスを維持します",
49
+ "core.home.sub_title": "アカウント・メッセージ・スマートアシスタントをひとつに",
48
50
  "core.home.tech_stack": "現在のテクノロジースタック",
49
51
  "core.login.agree_prefix": "読んで同意します",
50
52
  "core.login.agree_separator": "および",
@@ -33,18 +33,20 @@
33
33
  "core.password.verify_label": "当前密码",
34
34
  "core.file.upload_failed": "上传失败",
35
35
  "core.home.account_capability": "账户能力",
36
- "core.home.account_capability_description": "个人资料、应用设置与智能助手入口已保留",
36
+ "core.home.account_capability_description": "登录、个人资料、多因素认证与应用设置开箱即用",
37
37
  "core.home.cross_platform": "跨端页面",
38
38
  "core.home.cross_platform_description": "同一套页面代码适配 H5 和微信小程序",
39
39
  "core.home.demo_scope": "功能概览",
40
+ "core.home.dynamic_config": "动态配置",
41
+ "core.home.dynamic_config_description": "首页 Logo 与标语支持后台远程配置",
40
42
  "core.home.framework": "应用框架",
41
43
  "core.home.language": "开发语言",
42
44
  "core.home.main_title": "应用首页",
45
+ "core.home.message_center": "消息中心",
46
+ "core.home.message_center_description": "未读消息统一收纳,红点提醒不错过",
43
47
  "core.home.state_management": "状态管理",
44
- "core.home.static_home": "静态首页",
45
- "core.home.static_home_description": "当前首页用于展示框架信息,不依赖业务接口",
46
48
  "core.home.style_solution": "样式方案",
47
- "core.home.sub_title": "保留通用导航与个人中心体验",
49
+ "core.home.sub_title": "账号、消息与智能助手,一站触达",
48
50
  "core.home.tech_stack": "当前技术栈",
49
51
  "core.login.agree_prefix": "我已阅读并同意",
50
52
  "core.login.agree_separator": "和",
@@ -33,18 +33,20 @@
33
33
  "core.password.verify_label": "當前密碼",
34
34
  "core.file.upload_failed": "上傳失敗",
35
35
  "core.home.account_capability": "賬戶能力",
36
- "core.home.account_capability_description": "個人資料、應用設定與智慧助手入口已保留",
36
+ "core.home.account_capability_description": "登入、個人資料、多因素認證與應用設定開箱即用",
37
37
  "core.home.cross_platform": "跨端頁面",
38
38
  "core.home.cross_platform_description": "同一套頁面程式碼適配 H5 和微信小程式",
39
39
  "core.home.demo_scope": "功能概覽",
40
+ "core.home.dynamic_config": "動態設定",
41
+ "core.home.dynamic_config_description": "首頁 Logo 與標語支援後台遠端設定",
40
42
  "core.home.framework": "應用框架",
41
43
  "core.home.language": "開發語言",
42
44
  "core.home.main_title": "應用首頁",
45
+ "core.home.message_center": "訊息中心",
46
+ "core.home.message_center_description": "未讀訊息統一查看,紅點提醒不錯過",
43
47
  "core.home.state_management": "狀態管理",
44
- "core.home.static_home": "靜態首頁",
45
- "core.home.static_home_description": "當前首頁用於展示框架資訊,不依賴業務介面",
46
48
  "core.home.style_solution": "樣式方案",
47
- "core.home.sub_title": "保留通用導航與個人中心體驗",
49
+ "core.home.sub_title": "帳號、訊息與智慧助手,一站觸達",
48
50
  "core.home.tech_stack": "當前技術棧",
49
51
  "core.login.agree_prefix": "我已閱讀並同意",
50
52
  "core.login.agree_separator": "和",
package/src/navigation.ts CHANGED
@@ -73,6 +73,16 @@ const createDefaultMenus = (): AppMenu[] => [
73
73
  icon: 'MESSAGE_DEFAULT',
74
74
  selectedIcon: 'MESSAGE_SELECTED',
75
75
  },
76
+ {
77
+ id: 99050100,
78
+ parentId: 99040000,
79
+ name: 'AppMessageDetail',
80
+ path: 'app/message/detail',
81
+ viewKey: 'MESSAGE_DETAIL',
82
+ title: '',
83
+ access: 'AUTHENTICATED',
84
+ inTabBar: false,
85
+ },
76
86
  {
77
87
  id: 99090000,
78
88
  parentId: APP_MENU_ROOT_ID,
@@ -181,8 +191,9 @@ export async function initializeAppNavigation(): Promise<void> {
181
191
  const cached = readCachedMenus(cacheKey)
182
192
  menus.value = filterUnavailableMenus(cached ?? createDefaultMenus())
183
193
  try {
184
- const nextMenus = filterUnavailableMenus(normalizeMenuResponse(await adapter.list()))
185
- validateMenus(nextMenus)
194
+ const nextMenus = validateMenus(
195
+ filterUnavailableMenus(normalizeMenuResponse(await adapter.list())),
196
+ )
186
197
  uni.setStorageSync(cacheKey, nextMenus)
187
198
  menus.value = nextMenus
188
199
  } catch (error) {
@@ -202,8 +213,7 @@ function filterUnavailableMenus(nextMenus: AppMenu[]): AppMenu[] {
202
213
 
203
214
  /** 使用已经校验的整份配置进行原子切换。 */
204
215
  export function installAppNavigation(nextMenus: AppMenu[]): void {
205
- validateMenus(nextMenus)
206
- const snapshot = nextMenus.map((menu) => ({ ...menu }))
216
+ const snapshot = validateMenus(nextMenus).map((menu) => ({ ...menu }))
207
217
  uni.setStorageSync(resolveCacheKey(), snapshot)
208
218
  menus.value = snapshot
209
219
  ready.value = true
@@ -394,9 +404,16 @@ function readCachedMenus(cacheKey: string): AppMenu[] | undefined {
394
404
  const cached = uni.getStorageSync(cacheKey) as unknown
395
405
  if (!Array.isArray(cached)) return
396
406
  try {
397
- const cachedMenus = cached as AppMenu[]
398
- validateMenus(cachedMenus)
399
- return cachedMenus
407
+ const cachedMenus = (cached as AppMenu[]).map((menu) => ({
408
+ ...menu,
409
+ title:
410
+ menu.viewKey === 'HOME'
411
+ ? t('core.navigation.home')
412
+ : menu.viewKey === 'MESSAGE_INBOX'
413
+ ? t('core.navigation.message')
414
+ : menu.title,
415
+ }))
416
+ return validateMenus(cachedMenus)
400
417
  } catch {
401
418
  uni.removeStorageSync(cacheKey)
402
419
  }
@@ -415,7 +432,12 @@ function normalizeMenu(item: BaseMenu): AppMenu {
415
432
  name: item.name,
416
433
  path: item.path,
417
434
  viewKey: app?.view_key ?? '',
418
- title: meta?.title ?? '',
435
+ title:
436
+ app?.view_key === 'HOME'
437
+ ? t('core.navigation.home')
438
+ : app?.view_key === 'MESSAGE_INBOX'
439
+ ? t('core.navigation.message')
440
+ : (meta?.title ?? ''),
419
441
  access: (app?.access ?? 'AUTHENTICATED') as AppMenuAccess,
420
442
  inTabBar: app?.in_tab_bar ?? false,
421
443
  icon: meta?.icon,
@@ -423,11 +445,17 @@ function normalizeMenu(item: BaseMenu): AppMenu {
423
445
  }
424
446
  }
425
447
 
426
- function validateMenus(nextMenus: AppMenu[]): void {
448
+ function validateMenus(nextMenus: AppMenu[]): AppMenu[] {
449
+ // 多端共用后端时菜单可能携带本应用未注册的视图键,过滤跳过而不是让整份菜单失效。
450
+ const knownMenus = nextMenus.filter((menu) => {
451
+ if (resolveStaticView(menu.viewKey)) return true
452
+ console.warn(t('core.navigation.error.view_key', { viewKey: menu.viewKey }))
453
+ return false
454
+ })
427
455
  const ids = new Set<number>()
428
456
  const names = new Set<string>()
429
457
  const paths = new Set<string>()
430
- for (const menu of nextMenus) {
458
+ for (const menu of knownMenus) {
431
459
  if (!Number.isSafeInteger(menu.id) || !menu.name || !menu.path.startsWith('app/')) {
432
460
  throw new Error(t('core.navigation.error.identity'))
433
461
  }
@@ -437,9 +465,6 @@ function validateMenus(nextMenus: AppMenu[]): void {
437
465
  if (!['PUBLIC', 'GUEST_ONLY', 'AUTHENTICATED'].includes(menu.access)) {
438
466
  throw new Error(t('core.navigation.error.access', { access: menu.access }))
439
467
  }
440
- if (!resolveStaticView(menu.viewKey)) {
441
- throw new Error(t('core.navigation.error.view_key', { viewKey: menu.viewKey }))
442
- }
443
468
  if (ids.has(menu.id) || names.has(menu.name) || paths.has(menu.path)) {
444
469
  throw new Error(t('core.navigation.error.unique'))
445
470
  }
@@ -447,8 +472,8 @@ function validateMenus(nextMenus: AppMenu[]): void {
447
472
  names.add(menu.name)
448
473
  paths.add(menu.path)
449
474
  }
450
- const menuMap = new Map(nextMenus.map((menu) => [menu.id, menu]))
451
- for (const menu of nextMenus) {
475
+ const menuMap = new Map(knownMenus.map((menu) => [menu.id, menu]))
476
+ for (const menu of knownMenus) {
452
477
  if (menu.parentId === undefined) {
453
478
  throw new Error(t('core.navigation.error.parent_missing', { name: menu.name }))
454
479
  }
@@ -471,6 +496,7 @@ function validateMenus(nextMenus: AppMenu[]): void {
471
496
  parentId = parent.parentId
472
497
  }
473
498
  }
474
- const tabCount = nextMenus.filter((menu) => menu.parentId === APP_MENU_ROOT_ID).length
499
+ const tabCount = knownMenus.filter((menu) => menu.parentId === APP_MENU_ROOT_ID).length
475
500
  if (tabCount === 1 || tabCount > 5) throw new Error(t('core.navigation.error.tab_count'))
501
+ return knownMenus
476
502
  }
package/src/pages.ts CHANGED
@@ -5,7 +5,6 @@ export const corePages: Record<string, KratosAppPageConfig> = {
5
5
  'pages/index/index': {
6
6
  style: {
7
7
  navigationStyle: 'custom',
8
- navigationBarTextStyle: 'white',
9
8
  navigationBarTitleText: '',
10
9
  },
11
10
  },
@@ -1,6 +1,14 @@
1
1
  @use './theme.scss';
2
2
  @use './components.scss';
3
3
 
4
+ page,
5
+ body,
6
+ uni-page-body {
7
+ font-family:
8
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
9
+ 'Microsoft YaHei', sans-serif;
10
+ }
11
+
4
12
  view,
5
13
  navigator,
6
14
  input,
@@ -97,3 +105,16 @@ html.theme-light .uni-input-input:-webkit-autofill {
97
105
  caret-color: #102232;
98
106
  }
99
107
  /* #endif */
108
+
109
+ /* H5 隐藏滚动条但保留滚动能力,视觉对齐小程序;小程序端滚动条本就不存在,无副作用。 */
110
+ /* #ifdef H5 */
111
+ ::-webkit-scrollbar {
112
+ width: 0;
113
+ height: 0;
114
+ display: none;
115
+ }
116
+
117
+ * {
118
+ scrollbar-width: none;
119
+ }
120
+ /* #endif */
@@ -12,50 +12,16 @@
12
12
  .k-glass {
13
13
  position: relative;
14
14
  overflow: hidden;
15
- border: 1rpx solid var(--kratos-color-border);
15
+ border: 1px solid var(--kratos-color-border);
16
16
  border-radius: 26rpx;
17
17
  background: var(--kratos-color-surface);
18
- box-shadow: inset 0 1rpx 0 rgba(255, 255, 255, 0.05);
18
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
19
19
  }
20
20
  .k-glass--lit {
21
21
  border-color: var(--kratos-color-border-accent);
22
22
  box-shadow: var(--kratos-glow-soft);
23
23
  }
24
24
 
25
- /* 四角 L 形角标:渲染为真实 view 节点(weapp 伪元素仅支持 view) */
26
- .k-cnr {
27
- position: absolute;
28
- width: 24rpx;
29
- height: 24rpx;
30
- border: 3rpx solid var(--kratos-color-primary);
31
- opacity: 0.5;
32
- pointer-events: none;
33
- }
34
- .k-cnr--tl {
35
- top: 16rpx;
36
- left: 16rpx;
37
- border-right: 0;
38
- border-bottom: 0;
39
- }
40
- .k-cnr--tr {
41
- top: 16rpx;
42
- right: 16rpx;
43
- border-left: 0;
44
- border-bottom: 0;
45
- }
46
- .k-cnr--bl {
47
- bottom: 16rpx;
48
- left: 16rpx;
49
- border-right: 0;
50
- border-top: 0;
51
- }
52
- .k-cnr--br {
53
- bottom: 16rpx;
54
- right: 16rpx;
55
- border-left: 0;
56
- border-top: 0;
57
- }
58
-
59
25
  /* 装饰网格背景 */
60
26
  .k-grid-bg {
61
27
  position: absolute;
@@ -105,7 +71,7 @@
105
71
  justify-content: space-between;
106
72
  gap: 24rpx;
107
73
  padding: 28rpx 0;
108
- border-top: 1rpx solid var(--kratos-color-border);
74
+ border-top: 1px solid var(--kratos-color-border);
109
75
  }
110
76
  .k-field:first-child {
111
77
  border-top: 0;
@@ -168,15 +134,15 @@
168
134
  height: 76rpx;
169
135
  }
170
136
  .k-page-nav__back {
171
- border: 1rpx solid var(--kratos-color-border);
137
+ border: 1px solid var(--kratos-color-border);
172
138
  border-radius: 24rpx;
173
139
  background: var(--kratos-color-surface);
174
140
  }
175
141
  .k-page-nav__back::before {
176
142
  width: 16rpx;
177
143
  height: 16rpx;
178
- border-left: 2rpx solid var(--kratos-color-primary);
179
- border-bottom: 2rpx solid var(--kratos-color-primary);
144
+ border-left: 2px solid var(--kratos-color-primary);
145
+ border-bottom: 2px solid var(--kratos-color-primary);
180
146
  content: '';
181
147
  transform: rotate(45deg);
182
148
  }
@@ -186,31 +152,44 @@
186
152
  max-width: calc(100% - 190rpx);
187
153
  overflow: hidden;
188
154
  color: var(--kratos-color-text);
189
- font-size: 36rpx;
190
- font-weight: 800;
155
+ font-size: 32rpx;
156
+ font-weight: 700;
191
157
  text-overflow: ellipsis;
192
158
  transform: translateX(-50%);
193
159
  white-space: nowrap;
194
160
  }
195
161
  .k-top {
162
+ position: relative;
196
163
  display: flex;
197
164
  align-items: center;
198
- justify-content: space-between;
165
+ /* 标题绝对居中后,流内子元素(右侧动作按钮)统一靠右排布 */
166
+ justify-content: flex-end;
199
167
  gap: 16rpx;
200
168
  /* MP-WEIXIN 原生胶囊避让:页面通过 getMpCapsuleRightGap() 写入该变量,
201
169
  其余平台未设置时回退 0,不影响布局。 */
202
170
  padding-right: var(--mp-capsule-gap, 0px);
203
171
  }
172
+ /* #ifdef MP-WEIXIN */
173
+ /* 双写类名提高优先级:页面级 padding 简写(如 .message-header)不得压掉导航带 */
174
+ .k-top.k-top {
175
+ /* 小程序右上角是原生胶囊:操作按钮靠左,标题行与胶囊同行(同 k-page-nav 几何) */
176
+ justify-content: flex-start;
177
+ height: calc(88rpx + 44px);
178
+ padding-top: 44px;
179
+ padding-right: 0;
180
+ }
181
+ /* #endif */
204
182
  .k-top__title {
205
- font-size: 38rpx;
206
- font-weight: 800;
207
- letter-spacing: -0.3rpx;
183
+ position: absolute;
184
+ left: 50%;
185
+ max-width: calc(100% - 190rpx);
186
+ overflow: hidden;
208
187
  color: var(--kratos-color-text);
209
- }
210
- .k-top__t2 {
211
188
  font-size: 32rpx;
212
189
  font-weight: 700;
213
- color: var(--kratos-color-text);
190
+ text-overflow: ellipsis;
191
+ transform: translateX(-50%);
192
+ white-space: nowrap;
214
193
  }
215
194
 
216
195
  /* 方形图标按钮 + 角标 */
@@ -223,7 +202,7 @@
223
202
  height: 76rpx;
224
203
  border-radius: 24rpx;
225
204
  background: var(--kratos-color-surface);
226
- border: 1rpx solid var(--kratos-color-border);
205
+ border: 1px solid var(--kratos-color-border);
227
206
  color: var(--kratos-color-text);
228
207
  }
229
208
  .k-ic--gh {
@@ -254,7 +233,7 @@
254
233
  justify-content: space-between;
255
234
  gap: 28rpx;
256
235
  padding: 26rpx 0;
257
- border-top: 1rpx solid var(--kratos-color-border);
236
+ border-top: 1px solid var(--kratos-color-border);
258
237
  }
259
238
  .k-kv:first-child {
260
239
  border-top: 0;
@@ -281,7 +260,7 @@
281
260
  padding: 8rpx 20rpx;
282
261
  border-radius: 999rpx;
283
262
  background: var(--kratos-color-primary-soft-2);
284
- border: 1rpx solid var(--kratos-color-border-accent);
263
+ border: 1px solid var(--kratos-color-border-accent);
285
264
  color: var(--kratos-color-primary);
286
265
  font-size: 20rpx;
287
266
  font-family: var(--kratos-font-mono);
@@ -294,7 +273,7 @@
294
273
  padding: 6rpx;
295
274
  border-radius: 22rpx;
296
275
  background: var(--kratos-color-track);
297
- border: 1rpx solid var(--kratos-color-border);
276
+ border: 1px solid var(--kratos-color-border);
298
277
  }
299
278
  .k-pills__item {
300
279
  padding: 12rpx 24rpx;
@@ -311,12 +290,20 @@
311
290
  box-shadow: var(--kratos-glow-soft);
312
291
  }
313
292
 
314
- /* 过滤芯片 */
293
+ /* 过滤芯片:单行横向滚动,超出隐藏滚动条 */
315
294
  .k-chips {
316
295
  display: flex;
317
- flex-wrap: wrap;
296
+ flex-wrap: nowrap;
318
297
  gap: 16rpx;
319
298
  margin-bottom: 24rpx;
299
+ overflow-x: auto;
300
+ scrollbar-width: none;
301
+ }
302
+ .k-chips::-webkit-scrollbar {
303
+ display: none;
304
+ }
305
+ .k-chips .k-ch {
306
+ flex: 0 0 auto;
320
307
  }
321
308
  .k-ch {
322
309
  display: inline-flex;
@@ -325,7 +312,7 @@
325
312
  padding: 14rpx 24rpx;
326
313
  border-radius: 999rpx;
327
314
  background: var(--kratos-color-surface);
328
- border: 1rpx solid var(--kratos-color-border);
315
+ border: 1px solid var(--kratos-color-border);
329
316
  color: var(--kratos-color-text-muted);
330
317
  font-size: 22rpx;
331
318
  font-family: var(--kratos-font-mono);
@@ -355,7 +342,7 @@
355
342
  padding: 12rpx 24rpx;
356
343
  border-radius: 999rpx;
357
344
  background: var(--kratos-color-surface);
358
- border: 1rpx solid var(--kratos-color-border);
345
+ border: 1px solid var(--kratos-color-border);
359
346
  color: var(--kratos-color-text-muted);
360
347
  font-size: 22rpx;
361
348
  font-family: var(--kratos-font-mono);
@@ -382,7 +369,7 @@
382
369
  right: -12rpx;
383
370
  bottom: -12rpx;
384
371
  left: -12rpx;
385
- border: 1rpx solid var(--kratos-color-border-accent);
372
+ border: 1px solid var(--kratos-color-border-accent);
386
373
  border-radius: 38rpx;
387
374
  opacity: 0.55;
388
375
  }
@@ -408,7 +395,7 @@
408
395
  margin-top: 24rpx;
409
396
  border-radius: 20rpx;
410
397
  background: var(--kratos-color-surface);
411
- border: 1rpx solid var(--kratos-color-border);
398
+ border: 1px solid var(--kratos-color-border);
412
399
  }
413
400
  .k-lg-field__input {
414
401
  flex: 1;
@@ -430,7 +417,7 @@
430
417
  align-items: center;
431
418
  justify-content: center;
432
419
  background: var(--kratos-color-primary-soft-2);
433
- border: 1rpx solid var(--kratos-color-border);
420
+ border: 1px solid var(--kratos-color-border);
434
421
  color: var(--kratos-color-primary);
435
422
  font-family: var(--kratos-font-mono);
436
423
  font-size: 34rpx;
@@ -442,6 +429,8 @@
442
429
  align-items: center;
443
430
  justify-content: center;
444
431
  gap: 16rpx;
432
+ /* 各端 H5 原生 button 默认宽度不一,显式通栏保证双端一致 */
433
+ width: 100%;
445
434
  height: 100rpx;
446
435
  margin-top: 36rpx;
447
436
  border-radius: 20rpx;
@@ -455,7 +444,7 @@
455
444
  }
456
445
  .k-lg-btn--wx {
457
446
  background: var(--kratos-color-surface);
458
- border: 1rpx solid var(--kratos-color-border);
447
+ border: 1px solid var(--kratos-color-border);
459
448
  color: var(--kratos-color-text);
460
449
  font-weight: 700;
461
450
  }
@@ -472,7 +461,7 @@
472
461
  .k-lg-or::after {
473
462
  content: '';
474
463
  flex: 1;
475
- height: 1rpx;
464
+ height: 1px;
476
465
  background: var(--kratos-color-border);
477
466
  }
478
467
  .k-lg-agree {
@@ -543,7 +532,7 @@
543
532
  padding: 14rpx 22rpx;
544
533
  border-radius: 999rpx;
545
534
  background: var(--kratos-color-primary-soft);
546
- border: 1rpx solid var(--kratos-color-border-accent);
535
+ border: 1px solid var(--kratos-color-border-accent);
547
536
  color: var(--kratos-color-primary);
548
537
  font-size: 22rpx;
549
538
  font-family: var(--kratos-font-mono);
@@ -555,7 +544,7 @@
555
544
  padding: 30rpx;
556
545
  border-radius: 24rpx;
557
546
  background: linear-gradient(135deg, var(--kratos-color-primary-soft-2), var(--kratos-color-primary-soft));
558
- border: 1rpx solid var(--kratos-color-border-accent);
547
+ border: 1px solid var(--kratos-color-border-accent);
559
548
  }
560
549
  .k-aix__ic {
561
550
  flex-shrink: 0;
@@ -586,7 +575,7 @@
586
575
  padding: 14rpx 24rpx;
587
576
  border-radius: 999rpx;
588
577
  background: var(--kratos-color-primary-soft-2);
589
- border: 1rpx solid var(--kratos-color-border-accent);
578
+ border: 1px solid var(--kratos-color-border-accent);
590
579
  color: var(--kratos-color-primary);
591
580
  font-size: 22rpx;
592
581
  font-family: var(--kratos-font-mono);
@@ -610,7 +599,7 @@
610
599
  width: 92rpx;
611
600
  height: 92rpx;
612
601
  border-radius: 20rpx;
613
- border: 1rpx solid var(--kratos-color-border);
602
+ border: 1px solid var(--kratos-color-border);
614
603
  color: var(--kratos-color-primary);
615
604
  background: var(--kratos-color-primary-soft);
616
605
  }
@@ -639,7 +628,7 @@
639
628
  margin-bottom: 20rpx;
640
629
  border-radius: 20rpx;
641
630
  background: var(--kratos-color-surface);
642
- border: 1rpx solid var(--kratos-color-border);
631
+ border: 1px solid var(--kratos-color-border);
643
632
  }
644
633
  .k-set-row--arrow::after {
645
634
  content: '';
@@ -664,7 +653,7 @@
664
653
  width: 76rpx;
665
654
  height: 76rpx;
666
655
  border-radius: 22rpx;
667
- border: 1rpx solid var(--kratos-color-border);
656
+ border: 1px solid var(--kratos-color-border);
668
657
  color: var(--kratos-color-primary);
669
658
  }
670
659
  .k-set-row__title {
@@ -690,7 +679,7 @@
690
679
  border-radius: 20rpx;
691
680
  color: var(--kratos-color-danger);
692
681
  background: var(--kratos-color-danger-soft);
693
- border: 1rpx solid var(--kratos-color-danger);
682
+ border: 1px solid var(--kratos-color-danger);
694
683
  font-size: 30rpx;
695
684
  font-weight: 700;
696
685
  }
@@ -709,7 +698,7 @@
709
698
  padding: 26rpx 28rpx;
710
699
  border-radius: 20rpx;
711
700
  background: var(--kratos-color-surface);
712
- border: 1rpx solid var(--kratos-color-border);
701
+ border: 1px solid var(--kratos-color-border);
713
702
  }
714
703
  .k-msg-item--unread::before {
715
704
  content: '';
@@ -734,20 +723,35 @@
734
723
  width: 76rpx;
735
724
  height: 76rpx;
736
725
  border-radius: 22rpx;
737
- border: 1rpx solid var(--kratos-color-border);
726
+ border: 1px solid var(--kratos-color-border);
738
727
  color: var(--kratos-color-primary);
739
728
  background: var(--kratos-color-primary-soft);
740
729
  }
741
730
  .k-msg-item__title {
731
+ display: -webkit-box;
732
+ overflow: hidden;
742
733
  font-size: 25rpx;
743
734
  font-weight: 700;
744
735
  color: var(--kratos-color-text);
736
+ -webkit-box-orient: vertical;
737
+ -webkit-line-clamp: 2;
738
+ word-break: break-word;
745
739
  }
746
- .k-msg-item__meta {
740
+ .k-msg-item__foot {
741
+ display: flex;
742
+ align-items: center;
743
+ gap: 24rpx;
747
744
  margin-top: 10rpx;
745
+ }
746
+ .k-msg-item__meta {
747
+ flex: 1;
748
+ min-width: 0;
749
+ overflow: hidden;
748
750
  font-size: 20rpx;
749
751
  color: var(--kratos-color-text-muted);
750
752
  font-family: var(--kratos-font-mono);
753
+ text-overflow: ellipsis;
754
+ white-space: nowrap;
751
755
  }
752
756
  .k-msg-item__time {
753
757
  flex-shrink: 0;
@@ -761,10 +765,55 @@
761
765
  gap: 36rpx;
762
766
  margin-top: 22rpx;
763
767
  padding-top: 22rpx;
764
- border-top: 1rpx solid var(--kratos-color-border);
768
+ border-top: 1px solid var(--kratos-color-border);
765
769
  }
766
770
  .k-msg-item__action {
767
771
  font-size: 22rpx;
768
772
  color: var(--kratos-color-primary);
769
773
  font-family: var(--kratos-font-mono);
770
774
  }
775
+
776
+ /* ============================================================
777
+ * 标准空态词汇(列表无数据时统一使用)
778
+ * ============================================================ */
779
+ .k-empty {
780
+ display: flex;
781
+ flex-direction: column;
782
+ align-items: center;
783
+ padding: 140rpx 32rpx;
784
+ }
785
+ .k-empty__icon {
786
+ display: flex;
787
+ align-items: center;
788
+ justify-content: center;
789
+ width: 112rpx;
790
+ height: 112rpx;
791
+ border-radius: 26rpx;
792
+ border: 1px solid var(--kratos-color-border-accent);
793
+ background: var(--kratos-color-primary-soft-2);
794
+ color: var(--kratos-color-primary);
795
+ box-shadow: var(--kratos-glow-soft);
796
+ }
797
+ .k-empty__title {
798
+ margin-top: 28rpx;
799
+ color: var(--kratos-color-text);
800
+ font-size: 27rpx;
801
+ font-weight: 700;
802
+ }
803
+ .k-empty__desc {
804
+ margin-top: 12rpx;
805
+ color: var(--kratos-color-text-muted);
806
+ font-size: 22rpx;
807
+ font-family: var(--kratos-font-mono);
808
+ text-align: center;
809
+ }
810
+ .k-empty__act {
811
+ margin-top: 32rpx;
812
+ padding: 16rpx 40rpx;
813
+ border-radius: 999rpx;
814
+ border: 1px solid var(--kratos-color-border-accent);
815
+ background: var(--kratos-color-primary-soft-2);
816
+ color: var(--kratos-color-primary);
817
+ font-size: 24rpx;
818
+ font-family: var(--kratos-font-mono);
819
+ }
package/src/utils/http.ts CHANGED
@@ -398,8 +398,15 @@ async function refreshAccessToken() {
398
398
  setTokenExpiresIn(expires_in)
399
399
  }
400
400
 
401
+ /** 会话过期弹窗等待时长:H5 端路由切换会卸载弹窗且 Promise 永不结算,超时释放避免提示永久卡死。 */
402
+ const RELOGIN_PROMPT_TIMEOUT = 60_000
403
+
401
404
  /** 业务页提示重新登录;登录页只清理残留登录态,避免后台请求触发循环弹窗。 */
402
405
  async function promptRelogin() {
406
+ // 401 来自旧会话的在途请求而当前已是有效登录态时直接忽略,避免刚登录成功被弹窗打断。
407
+ if (hasValidToken()) {
408
+ return
409
+ }
403
410
  const pages = getCurrentPages()
404
411
  if (pages[pages.length - 1]?.route === 'pages/login/login') {
405
412
  if (getToken() || getRefreshToken() || uni.getStorageSync('user')) {
@@ -412,14 +419,22 @@ async function promptRelogin() {
412
419
  }
413
420
  isPromptingRelogin = true
414
421
  try {
415
- const modalRes = await uni.showModal({
416
- title: t('common.title.notice'),
417
- content: t('core.auth.session_expired'),
418
- showCancel: false,
419
- confirmText: t('core.auth.login_again'),
420
- })
422
+ const modalRes = await Promise.race([
423
+ // 按钮文案复用通用确认键:平台对 confirmText 限制 4 个汉字/8 字符,长文案会被整体拒绝。
424
+ uni
425
+ .showModal({
426
+ title: t('common.title.notice'),
427
+ content: t('core.auth.session_expired'),
428
+ showCancel: false,
429
+ confirmText: t('common.action.confirm'),
430
+ })
431
+ .catch(() => ({ confirm: false })),
432
+ new Promise<{ confirm: boolean }>((resolve) =>
433
+ setTimeout(() => resolve({ confirm: false }), RELOGIN_PROMPT_TIMEOUT),
434
+ ),
435
+ ])
421
436
 
422
- if (!modalRes.confirm) {
437
+ if (!modalRes?.confirm) {
423
438
  return
424
439
  }
425
440
 
@@ -9,7 +9,7 @@ import { useI18n } from '../../../locales'
9
9
  const settingStore = useSettingStore()
10
10
  const themeStore = useThemeStore()
11
11
  const { t } = useI18n()
12
- const mainTitle = computed(() => settingStore.getData('mainTitle') || t('core.home.main_title'))
12
+ const mainTitle = computed(() => t('core.navigation.home'))
13
13
  const subTitle = computed(() => settingStore.getData('subTitle') || t('core.home.sub_title'))
14
14
  const configuredAppLogo = computed(() => {
15
15
  const configuredLogo = settingStore.getData('appLogo')
@@ -46,10 +46,6 @@ onLoad(() => {
46
46
  </view>
47
47
 
48
48
  <view class="k-glass k-glass--lit home-hero">
49
- <view class="k-cnr k-cnr--tl" aria-hidden="true"></view>
50
- <view class="k-cnr k-cnr--tr" aria-hidden="true"></view>
51
- <view class="k-cnr k-cnr--bl" aria-hidden="true"></view>
52
- <view class="k-cnr k-cnr--br" aria-hidden="true"></view>
53
49
  <view class="home-hero__main">
54
50
  <image class="home-logo" :src="appLogo" mode="aspectFit" @error="handleLogoError" />
55
51
  <view class="home-hero__copy">
@@ -102,8 +98,15 @@ onLoad(() => {
102
98
  <view class="home-demo-item">
103
99
  <view class="home-demo-dot">3</view>
104
100
  <view class="home-demo-copy">
105
- <text class="home-demo-title">{{ t('core.home.static_home') }}</text>
106
- <text class="home-demo-desc">{{ t('core.home.static_home_description') }}</text>
101
+ <text class="home-demo-title">{{ t('core.home.message_center') }}</text>
102
+ <text class="home-demo-desc">{{ t('core.home.message_center_description') }}</text>
103
+ </view>
104
+ </view>
105
+ <view class="home-demo-item">
106
+ <view class="home-demo-dot">4</view>
107
+ <view class="home-demo-copy">
108
+ <text class="home-demo-title">{{ t('core.home.dynamic_config') }}</text>
109
+ <text class="home-demo-desc">{{ t('core.home.dynamic_config_description') }}</text>
107
110
  </view>
108
111
  </view>
109
112
  </view>
@@ -126,21 +129,38 @@ onLoad(() => {
126
129
  padding: calc(env(safe-area-inset-top) + 20rpx) 32rpx 220rpx;
127
130
 
128
131
  /* #ifdef MP-WEIXIN */
129
- padding-top: calc(44px + 24rpx);
132
+ /* 首页标题在小程序端占导航带,页面不再额外让出顶部空间 */
133
+ padding-top: 0;
130
134
  /* #endif */
131
135
  }
132
136
 
137
+ /* 小程序端首页标题占导航带:与 k-page-nav 同几何,标题与胶囊同行 */
138
+ /* #ifdef MP-WEIXIN */
139
+ .home-top {
140
+ box-sizing: border-box;
141
+ height: calc(88rpx + 44px);
142
+ padding: 44px 4rpx 0;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ }
147
+
148
+ .home-top__title {
149
+ margin-top: 0;
150
+ }
151
+ /* #endif */
152
+
133
153
  .home-top {
134
154
  padding: 8rpx 4rpx 24rpx;
135
155
  }
136
156
 
137
- /* 页面大标题 */
157
+ /* 页面大标题:与其他页面导航标题同规格,向原生导航栏看齐 */
138
158
  .home-top__title {
139
159
  margin-top: 14rpx;
140
160
  color: var(--kratos-color-text);
141
- font-size: 44rpx;
142
- font-weight: 800;
143
- letter-spacing: -0.5rpx;
161
+ font-size: 32rpx;
162
+ font-weight: 700;
163
+ text-align: center;
144
164
  }
145
165
 
146
166
  .home-hero {
@@ -158,7 +178,7 @@ onLoad(() => {
158
178
  width: 104rpx;
159
179
  height: 104rpx;
160
180
  border-radius: 26rpx;
161
- border: 1rpx solid var(--kratos-color-border-accent);
181
+ border: 1px solid var(--kratos-color-border-accent);
162
182
  background: var(--kratos-color-surface-2);
163
183
  }
164
184
 
@@ -198,7 +218,7 @@ onLoad(() => {
198
218
  display: flex;
199
219
  align-items: flex-start;
200
220
  padding: 28rpx 0;
201
- border-top: 1rpx solid var(--kratos-color-border);
221
+ border-top: 1px solid var(--kratos-color-border);
202
222
 
203
223
  &:first-child {
204
224
  border-top: 0;
@@ -211,7 +231,7 @@ onLoad(() => {
211
231
  height: 48rpx;
212
232
  border-radius: 14rpx;
213
233
  background: var(--kratos-color-primary-soft);
214
- border: 1rpx solid var(--kratos-color-primary-edge);
234
+ border: 1px solid var(--kratos-color-primary-edge);
215
235
  color: var(--kratos-color-primary);
216
236
  font-size: 24rpx;
217
237
  font-family: var(--kratos-font-mono);
@@ -918,10 +918,6 @@ onUnload(() => {
918
918
  </view>
919
919
  </view>
920
920
  <view class="k-glass k-glass--lit login-panel">
921
- <view class="k-cnr k-cnr--tl" aria-hidden="true"></view>
922
- <view class="k-cnr k-cnr--tr" aria-hidden="true"></view>
923
- <view class="k-cnr k-cnr--bl" aria-hidden="true"></view>
924
- <view class="k-cnr k-cnr--br" aria-hidden="true"></view>
925
921
  <view class="login-form">
926
922
  <!-- 网页端表单登录 -->
927
923
  <!-- #ifdef H5 -->