@liujitcn/kratos-uni-app-system 0.0.78 → 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.
@@ -23,6 +23,7 @@ import Composer from './components/Composer.vue'
23
23
  import SessionDrawer from './components/SessionDrawer.vue'
24
24
  import WelcomePanel from './components/WelcomePanel.vue'
25
25
  import { navigateAppRoute, useI18n } from '@liujitcn/kratos-uni-app-core'
26
+ import { useThemeStore } from '@liujitcn/kratos-uni-app-core/stores'
26
27
  import {
27
28
  type AiStreamEvent,
28
29
  type AiStreamPayload,
@@ -72,6 +73,9 @@ const PENDING_MESSAGE_ID = 'pending'
72
73
  const MAX_ATTACHMENT_COUNT = 6
73
74
  const STARTER_PROMPT_PAGE_SIZE = 4
74
75
  const { locale, t } = useI18n()
76
+ const themeStore = useThemeStore()
77
+ /** 导航图标随主题取色,uni-icons 的 color 属性不接受 CSS 变量。 */
78
+ const navIconColor = computed(() => (themeStore.theme === 'light' ? '#102232' : '#e9f4f6'))
75
79
 
76
80
  const windowInfo = uni.getWindowInfo()
77
81
  let safeAreaTop =
@@ -260,7 +264,8 @@ const deleteSession = async (sessionID: string) => {
260
264
  title: session?.title || t('system.ai.current_session'),
261
265
  }),
262
266
  confirmText: t('common.action.delete'),
263
- confirmColor: '#cf4444',
267
+ confirmColor: themeStore.theme === 'light' ? '#e0324f' : '#ff4d6d',
268
+ cancelColor: themeStore.theme === 'light' ? '#5a7083' : '#8aa0b4',
264
269
  })
265
270
  if (!result.confirm) {
266
271
  return
@@ -476,13 +481,21 @@ async function loadAiShortcuts() {
476
481
  }
477
482
  loadingShortcuts.value = true
478
483
  try {
479
- const [response, providerResponse, knowledgeResponse] = await Promise.all([
484
+ const [shortcutResult, providerResult, knowledgeResult] = await Promise.allSettled([
480
485
  defAiToolService.ListAiShortcut({ terminal: AI_TERMINAL }),
481
486
  defAiModelService.ListAiProviderModelOptions({}),
482
487
  defAiKnowledgeService.ListAiKnowledgeOptions({}),
483
488
  ])
489
+ if (shortcutResult.status === 'rejected') throw shortcutResult.reason
490
+ if (providerResult.status === 'rejected') throw providerResult.reason
491
+ if (knowledgeResult.status === 'rejected' && isAuthExpiredError(knowledgeResult.reason)) {
492
+ throw knowledgeResult.reason
493
+ }
494
+ const response = shortcutResult.value
495
+ const providerResponse = providerResult.value
484
496
  modelProviders.value = providerResponse.providers ?? []
485
- knowledgeOptions.value = knowledgeResponse.options ?? []
497
+ knowledgeOptions.value =
498
+ knowledgeResult.status === 'fulfilled' ? (knowledgeResult.value.options ?? []) : []
486
499
  selectedKnowledgeIds.value = selectedKnowledgeIds.value.filter((id) =>
487
500
  knowledgeOptions.value.some((option) => option.id === id),
488
501
  )
@@ -497,6 +510,7 @@ async function loadAiShortcuts() {
497
510
  selectedProviderId.value = 0
498
511
  selectedModelName.value = ''
499
512
  knowledgeOptions.value = []
513
+ selectedKnowledgeIds.value = []
500
514
  showError(error, t('system.ai.load_shortcuts_failed'))
501
515
  } finally {
502
516
  loadingShortcuts.value = false
@@ -1262,15 +1276,21 @@ function showError(error: unknown, fallback: string) {
1262
1276
  </script>
1263
1277
 
1264
1278
  <template>
1265
- <view class="ai-page">
1266
- <view class="ai-navbar">
1267
- <button class="nav-back-button" hover-class="none" @tap="navigateBack">
1268
- <uni-icons type="left" size="24" color="#111" />
1269
- </button>
1270
- <view class="ai-navbar__title">{{ t('system.ai.chat_title') }}</view>
1271
- <button class="nav-menu-button" hover-class="none" @tap="toggleSessionDrawer">
1272
- <uni-icons type="bars" size="24" color="#111" />
1273
- </button>
1279
+ <view class="ai-page" :class="themeStore.themeClass">
1280
+ <view class="ai-navbar k-page-nav">
1281
+ <view
1282
+ class="k-page-nav__back"
1283
+ :aria-label="t('common.action.back')"
1284
+ @tap="navigateBack"
1285
+ ></view>
1286
+ <view class="k-page-nav__title">{{ t('system.ai.chat_title') }}</view>
1287
+ <view
1288
+ class="k-page-nav__side k-ic"
1289
+ :aria-label="t('system.ai.history')"
1290
+ @tap="toggleSessionDrawer"
1291
+ >
1292
+ <uni-icons type="bars" size="22" :color="navIconColor" />
1293
+ </view>
1274
1294
  </view>
1275
1295
 
1276
1296
  <scroll-view
@@ -1383,12 +1403,6 @@ function showError(error: unknown, fallback: string) {
1383
1403
  </template>
1384
1404
 
1385
1405
  <style lang="scss">
1386
- page {
1387
- height: 100%;
1388
- overflow: hidden;
1389
- background-color: #f6f6f6;
1390
- }
1391
-
1392
1406
  .ai-page {
1393
1407
  position: relative;
1394
1408
  display: flex;
@@ -1397,79 +1411,13 @@ page {
1397
1411
  height: 100vh;
1398
1412
  height: 100dvh;
1399
1413
  overflow: hidden;
1400
- color: #333;
1401
- background-color: #f6f6f6;
1414
+ color: var(--kratos-color-text);
1415
+ background: var(--kratos-bg-gradient);
1402
1416
  }
1403
1417
 
1404
1418
  .ai-navbar {
1405
1419
  flex-shrink: 0;
1406
- display: flex;
1407
- align-items: center;
1408
- justify-content: space-between;
1409
- box-sizing: border-box;
1410
- background-color: #fff;
1411
- border-bottom: 1rpx solid #eceef1;
1412
-
1413
- height: 44px;
1414
-
1415
- /* #ifdef MP-WEIXIN */
1416
- height: 88px;
1417
- padding-top: 44px;
1418
- /* #endif */
1419
- }
1420
-
1421
- /* #ifdef MP-WEIXIN */
1422
- .ai-navbar {
1423
- position: relative;
1424
- }
1425
-
1426
- .ai-navbar .nav-back-button,
1427
- .ai-navbar .nav-menu-button {
1428
- position: absolute;
1429
- bottom: 0;
1430
- }
1431
-
1432
- .ai-navbar .nav-back-button {
1433
- left: 0;
1434
- }
1435
-
1436
- .ai-navbar .nav-menu-button {
1437
- left: 56rpx;
1438
- right: auto;
1439
- }
1440
-
1441
- .ai-navbar__title {
1442
- width: 100%;
1443
- height: 44px;
1444
- line-height: 44px;
1445
- }
1446
- /* #endif */
1447
-
1448
- .nav-back-button,
1449
- .nav-menu-button {
1450
- display: flex;
1451
- align-items: center;
1452
- justify-content: center;
1453
- width: 88rpx;
1454
- height: 44px;
1455
- padding: 0;
1456
- border: 0;
1457
- border-radius: 0;
1458
- background: transparent;
1459
- line-height: normal;
1460
- }
1461
-
1462
- .nav-back-button::after,
1463
- .nav-menu-button::after {
1464
- border: 0;
1465
- }
1466
-
1467
- .ai-navbar__title {
1468
- flex: 1;
1469
- color: #111;
1470
- font-size: 32rpx;
1471
- font-weight: 600;
1472
- text-align: center;
1420
+ padding: 0 24rpx;
1473
1421
  }
1474
1422
 
1475
1423
  .ai-body {
@@ -1502,16 +1450,21 @@ page {
1502
1450
  }
1503
1451
 
1504
1452
  .user-bubble {
1505
- color: #fff;
1506
- background-color: #27ba9b;
1453
+ color: var(--kratos-color-on-primary);
1454
+ background: linear-gradient(
1455
+ 135deg,
1456
+ var(--kratos-color-primary-deep) 0%,
1457
+ var(--kratos-color-primary) 100%
1458
+ );
1507
1459
  border-bottom-right-radius: 6rpx;
1460
+ box-shadow: var(--kratos-glow);
1508
1461
  }
1509
1462
 
1510
1463
  .ai-bubble {
1511
- color: #333;
1512
- background-color: #fff;
1464
+ color: var(--kratos-color-text);
1465
+ background: var(--kratos-color-surface);
1466
+ border: 1rpx solid var(--kratos-color-border);
1513
1467
  border-bottom-left-radius: 6rpx;
1514
- box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.05);
1515
1468
  }
1516
1469
 
1517
1470
  .bubble.is-streaming {
@@ -1529,12 +1482,13 @@ page {
1529
1482
  align-items: center;
1530
1483
  gap: 12rpx;
1531
1484
  margin-bottom: 8rpx;
1532
- color: #8a8f99;
1485
+ color: var(--kratos-color-text-muted);
1533
1486
  font-size: 20rpx;
1487
+ font-family: var(--kratos-font-mono);
1534
1488
  }
1535
1489
 
1536
1490
  .reply-tag {
1537
- color: #16806d;
1491
+ color: var(--kratos-color-primary);
1538
1492
  }
1539
1493
 
1540
1494
  .attachment-list {
@@ -1548,8 +1502,9 @@ page {
1548
1502
  max-width: 100%;
1549
1503
  padding: 12rpx;
1550
1504
  margin-top: 8rpx;
1551
- border-radius: 10rpx;
1552
- background-color: rgba(255, 255, 255, 0.72);
1505
+ border: 1rpx solid var(--kratos-color-border);
1506
+ border-radius: 12rpx;
1507
+ background: var(--kratos-color-surface-2);
1553
1508
  box-sizing: border-box;
1554
1509
  }
1555
1510
 
@@ -1557,12 +1512,13 @@ page {
1557
1512
  flex-shrink: 0;
1558
1513
  width: 48rpx;
1559
1514
  height: 48rpx;
1560
- border-radius: 8rpx;
1561
- color: #16806d;
1562
- font-size: 22rpx;
1563
- line-height: 48rpx;
1515
+ border-radius: 10rpx;
1516
+ color: var(--kratos-color-primary);
1517
+ font-size: 20rpx;
1518
+ line-height: 46rpx;
1564
1519
  text-align: center;
1565
- background-color: #e8f8f4;
1520
+ background: var(--kratos-color-primary-soft);
1521
+ border: 1rpx solid var(--kratos-color-primary-edge);
1566
1522
  }
1567
1523
 
1568
1524
  .attachment-info {
@@ -1583,14 +1539,16 @@ page {
1583
1539
 
1584
1540
  .attachment-meta {
1585
1541
  margin-top: 4rpx;
1586
- color: #8a8f99;
1542
+ color: var(--kratos-color-text-muted);
1587
1543
  font-size: 19rpx;
1544
+ font-family: var(--kratos-font-mono);
1588
1545
  }
1589
1546
 
1590
1547
  .tool-row,
1591
1548
  .loading-session {
1592
- color: #8a8f99;
1549
+ color: var(--kratos-color-text-muted);
1593
1550
  font-size: 22rpx;
1551
+ font-family: var(--kratos-font-mono);
1594
1552
  line-height: 34rpx;
1595
1553
  }
1596
1554
 
@@ -2,11 +2,14 @@
2
2
  import { onLoad } from '@dcloudio/uni-app'
3
3
  import { ref } from 'vue'
4
4
  import { navigateAppView, t } from '@liujitcn/kratos-uni-app-core'
5
+ import { useThemeStore } from '@liujitcn/kratos-uni-app-core/stores'
6
+ import { homeTabPage } from '@liujitcn/kratos-uni-app-core/utils/navigation'
5
7
  import { defNotificationService } from '../../../api/base/v1/notification'
6
8
  import type { Notification } from '../../../rpc/base/v1/notification'
7
9
  import { MessageActionType, MessageContentFormat } from '../../../rpc/base/v1/notification'
8
10
  import { resolveMessageCategoryIcon } from './icons'
9
11
 
12
+ const themeStore = useThemeStore()
10
13
  const detail = ref<Notification>()
11
14
 
12
15
  onLoad((options) => {
@@ -49,40 +52,52 @@ function openAction() {
49
52
  }
50
53
  navigateAppView(detail.value.action_target, params)
51
54
  }
55
+
56
+ /** 返回消息列表,页面栈为空时回到首页。 */
57
+ function onBack() {
58
+ uni.navigateBack({ fail: () => uni.reLaunch({ url: homeTabPage }) })
59
+ }
52
60
  </script>
53
61
 
54
62
  <template>
55
- <view class="detail-page" v-if="detail">
56
- <text class="detail-title">{{ detail.title }}</text>
57
- <view class="detail-meta">
58
- <view class="detail-category" :style="{ color: detail.category_color || undefined }">
59
- <uni-icons
60
- class="detail-category__icon"
61
- :type="resolveMessageCategoryIcon(detail.category_icon)"
62
- size="18"
63
- :color="detail.category_color || '#64748b'"
64
- aria-hidden="true"
65
- />
66
- <text>{{ detail.category_name }}</text>
67
- </view>
68
- <text>{{ resolveSenderName(detail.sender_name) }}</text>
69
- <text>{{ detail.received_at }}</text>
63
+ <view class="detail-page" :class="themeStore.themeClass">
64
+ <view class="k-page-nav">
65
+ <view class="k-page-nav__back" :aria-label="t('common.action.back')" @tap="onBack"></view>
66
+ <view class="k-page-nav__title">{{ t('system.notification.title') }}</view>
67
+ <view class="k-page-nav__side" aria-hidden="true"></view>
70
68
  </view>
71
- <rich-text
72
- v-if="detail.content_format === MessageContentFormat.MESSAGE_CONTENT_FORMAT_RICH_TEXT"
73
- class="detail-content-rich"
74
- :nodes="detail.content"
75
- />
76
- <text v-else class="detail-content">{{ detail.content }}</text>
77
- <button
78
- v-if="detail.action_type === MessageActionType.MESSAGE_ACTION_TYPE_VIEW_KEY"
79
- class="detail-action"
80
- @tap="openAction"
81
- >
82
- {{ t('common.action.view') }}
83
- </button>
69
+ <template v-if="detail">
70
+ <text class="detail-title">{{ detail.title }}</text>
71
+ <view class="detail-meta">
72
+ <view class="detail-category" :style="{ color: detail.category_color || undefined }">
73
+ <uni-icons
74
+ class="detail-category__icon"
75
+ :type="resolveMessageCategoryIcon(detail.category_icon)"
76
+ size="18"
77
+ :color="detail.category_color || '#64748b'"
78
+ aria-hidden="true"
79
+ />
80
+ <text>{{ detail.category_name }}</text>
81
+ </view>
82
+ <text>{{ resolveSenderName(detail.sender_name) }}</text>
83
+ <text>{{ detail.received_at }}</text>
84
+ </view>
85
+ <rich-text
86
+ v-if="detail.content_format === MessageContentFormat.MESSAGE_CONTENT_FORMAT_RICH_TEXT"
87
+ class="detail-content-rich"
88
+ :nodes="detail.content"
89
+ />
90
+ <text v-else class="detail-content">{{ detail.content }}</text>
91
+ <button
92
+ v-if="detail.action_type === MessageActionType.MESSAGE_ACTION_TYPE_VIEW_KEY"
93
+ class="detail-action"
94
+ @tap="openAction"
95
+ >
96
+ {{ t('common.action.view') }}
97
+ </button>
98
+ </template>
99
+ <view v-else class="detail-loading">{{ t('core.status.loading') }}</view>
84
100
  </view>
85
- <view v-else class="empty">{{ t('core.status.loading') }}</view>
86
101
  </template>
87
102
 
88
103
  <style scoped lang="scss">
@@ -90,14 +105,21 @@ function openAction() {
90
105
  box-sizing: border-box;
91
106
  min-height: 100vh;
92
107
  padding: 24rpx 30rpx calc(96rpx + env(safe-area-inset-bottom));
93
- background: var(--kratos-color-background, #f7f8fa);
94
- color: var(--kratos-color-text, #1f2937);
108
+ background: var(--kratos-bg-gradient);
109
+ color: var(--kratos-color-text);
110
+ }
111
+ .detail-loading {
112
+ padding: 120rpx 0;
113
+ color: var(--kratos-color-text-dim);
114
+ font-family: var(--kratos-font-mono);
115
+ text-align: center;
95
116
  }
96
117
  .detail-title {
97
118
  display: block;
98
- color: var(--kratos-color-text, #1f2937);
119
+ padding-top: 16rpx;
120
+ color: var(--kratos-color-text);
99
121
  font-size: 42rpx;
100
- font-weight: 700;
122
+ font-weight: 800;
101
123
  line-height: 1.35;
102
124
  }
103
125
  .detail-meta {
@@ -105,8 +127,11 @@ function openAction() {
105
127
  flex-wrap: wrap;
106
128
  gap: 18rpx;
107
129
  margin: 24rpx 0 36rpx;
108
- color: var(--kratos-color-text-muted, #6b7280);
109
- font-size: 24rpx;
130
+ padding-bottom: 28rpx;
131
+ border-bottom: 1rpx solid var(--kratos-color-border);
132
+ color: var(--kratos-color-text-muted);
133
+ font-size: 22rpx;
134
+ font-family: var(--kratos-font-mono);
110
135
  }
111
136
  .detail-category {
112
137
  display: inline-flex;
@@ -116,7 +141,12 @@ function openAction() {
116
141
  margin-right: 8rpx;
117
142
  }
118
143
  .detail-content {
119
- color: var(--kratos-color-text, #1f2937);
144
+ display: block;
145
+ padding: 28rpx;
146
+ border: 1rpx solid var(--kratos-color-border);
147
+ border-radius: 20rpx;
148
+ background: var(--kratos-color-surface);
149
+ color: var(--kratos-color-text);
120
150
  font-size: 30rpx;
121
151
  line-height: 1.8;
122
152
  white-space: pre-wrap;
@@ -124,14 +154,32 @@ function openAction() {
124
154
  }
125
155
  .detail-content-rich {
126
156
  display: block;
127
- color: var(--kratos-color-text, #1f2937);
157
+ padding: 28rpx;
158
+ border: 1rpx solid var(--kratos-color-border);
159
+ border-radius: 20rpx;
160
+ background: var(--kratos-color-surface);
161
+ color: var(--kratos-color-text);
128
162
  font-size: 30rpx;
129
163
  line-height: 1.8;
130
164
  overflow-wrap: anywhere;
131
165
  }
166
+ .detail-action {
167
+ margin-top: 36rpx;
168
+ border: 1rpx solid var(--kratos-color-border-accent);
169
+ border-radius: 20rpx;
170
+ background: var(--kratos-color-primary-soft);
171
+ color: var(--kratos-color-primary);
172
+ font-size: 28rpx;
173
+ font-weight: 700;
174
+ line-height: 88rpx;
175
+ }
132
176
  .empty {
177
+ box-sizing: border-box;
178
+ min-height: 100vh;
133
179
  padding-top: 180rpx;
134
- color: var(--kratos-color-text-muted, #6b7280);
180
+ background: var(--kratos-bg-gradient);
181
+ color: var(--kratos-color-text-dim);
182
+ font-family: var(--kratos-font-mono);
135
183
  text-align: center;
136
184
  }
137
185
  </style>