@liujitcn/kratos-uni-app-system 0.0.31 → 0.0.33

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 CHANGED
@@ -7,7 +7,7 @@
7
7
  ```text
8
8
  packages/modules/system
9
9
  ├── src
10
- │ ├── api/base # AI 消息、会话和工具接口
10
+ │ ├── api/base/v1 # AI 消息、会话和工具接口
11
11
  │ ├── rpc # system 相关 Buf 生成类型
12
12
  │ ├── views
13
13
  │ │ ├── pages/my # 个人中心主页
@@ -18,12 +18,12 @@ packages/modules/system
18
18
  └── package.json
19
19
  ```
20
20
 
21
- `src/rpc` 是生成产物,只能通过 workspace 根目录的 `pnpm generate:rpc` 更新;该命令委托 Frontend 的 `make ts-uni-app` 和 `backend/api/buf.uni-app.core.typescript.gen.yaml` 生成。MFA 类型和请求统一从 `@liujitcn/kratos-uni-app-core/rpc/base/v1/mfa` 与 `@liujitcn/kratos-uni-app-core/api/base/mfa` 复用。
21
+ `src/rpc` 是生成产物,只能通过 workspace 根目录的 `pnpm generate:rpc` 更新;该命令委托 Frontend 的 `make ts-uni-app` 和 `backend/api/buf.uni-app.core.typescript.gen.yaml` 生成。MFA 类型和请求统一从 `@liujitcn/kratos-uni-app-core/rpc/base/v1/mfa` 与 `@liujitcn/kratos-uni-app-core/api/base/v1/mfa` 复用。
22
22
 
23
23
  ## 功能
24
24
 
25
25
  - 注册 `PROFILE_HOME`、`PROFILE`、`SETTINGS` 和 `AI` 四个稳定 `viewKey`。
26
- - 提供个人中心、资料维护、默认应用设置包装和 AI 助手页面。
26
+ - 提供个人中心、资料维护(昵称、邮箱和证件信息)、默认应用设置包装和 AI 助手页面。
27
27
  - 默认设置页直接装配 core 的 `KratosSettingsPage`;产品模块可用同一公共设置页的顶部 `extensions` 插槽追加业务设置。
28
28
  - 通过 core 的公开 service、store 和工具复用底座能力。
29
29
  - 允许后续模块按相同物理路由或 `viewKey` 覆盖 system 静态视图。
package/dist/index.mjs CHANGED
@@ -6,26 +6,60 @@ import { ref } from "vue";
6
6
  import { setAppMenuBadge } from "@liujitcn/kratos-uni-app-core/navigation";
7
7
  import { getRequestAccessToken } from "@liujitcn/kratos-uni-app-core/utils/http";
8
8
 
9
- // src/api/base/notification.ts
9
+ // src/api/base/v1/notification.ts
10
10
  import { http } from "@liujitcn/kratos-uni-app-core/utils/http";
11
11
  var NOTIFICATION_URL = "/v1/base/notification";
12
12
  var NotificationServiceImpl = class {
13
13
  /** 分页查询收件箱。 */
14
- PageNotification(request) {
15
- return http({ url: NOTIFICATION_URL, method: "GET", authMode: "required", data: request });
14
+ async PageNotification(request) {
15
+ const data = Object.fromEntries(
16
+ Object.entries(request).filter(([, value]) => value !== void 0)
17
+ );
18
+ const response = await http({
19
+ url: NOTIFICATION_URL,
20
+ method: "GET",
21
+ authMode: "required",
22
+ data
23
+ });
24
+ return {
25
+ ...response,
26
+ notifications: Array.isArray(response.notifications) ? response.notifications : [],
27
+ total: response.total ?? 0,
28
+ next_cursor_id: response.next_cursor_id ?? 0,
29
+ has_more: response.has_more ?? false
30
+ };
31
+ }
32
+ /** 查询当前可用的消息分类。 */
33
+ async ListNotificationCategories(request) {
34
+ const response = await http({
35
+ url: `${NOTIFICATION_URL}/categories`,
36
+ method: "GET",
37
+ authMode: "required",
38
+ data: request
39
+ });
40
+ return {
41
+ ...response,
42
+ categories: Array.isArray(response.categories) ? response.categories : []
43
+ };
16
44
  }
17
45
  /** 查询消息详情。 */
18
46
  GetNotification(request) {
19
47
  return http({ url: `${NOTIFICATION_URL}/${request.id}`, method: "GET", authMode: "required" });
20
48
  }
21
49
  /** 查询未读汇总。 */
22
- GetNotificationSummary(request) {
23
- return http({
50
+ async GetNotificationSummary(request) {
51
+ const response = await http({
24
52
  url: `${NOTIFICATION_URL}/summary`,
25
53
  method: "GET",
26
54
  authMode: "required",
27
55
  data: request
28
56
  });
57
+ return {
58
+ ...response,
59
+ unread_total: response.unread_total ?? 0,
60
+ latest_delivery_id: response.latest_delivery_id ?? 0,
61
+ category_unread: Array.isArray(response.category_unread) ? response.category_unread : []
62
+ };
29
63
  }
30
64
  /** 标记消息已读。 */
31
65
  MarkNotificationRead(request) {
@@ -198,7 +232,12 @@ var systemPages = {
198
232
  navigationBarTitleText: ""
199
233
  }
200
234
  },
201
- "pagesMember/message/index": { style: { navigationBarTitleText: "" } },
235
+ "pagesMember/message/index": {
236
+ style: {
237
+ navigationStyle: "custom",
238
+ navigationBarTitleText: ""
239
+ }
240
+ },
202
241
  "pagesMember/message/detail": { style: { navigationBarTitleText: "" } }
203
242
  };
204
243
 
@@ -273,6 +312,20 @@ var zh_CN_default = {
273
312
  "system.profile.avatar_upload_failed": "\u4E0A\u4F20\u5934\u50CF\u5931\u8D25",
274
313
  "system.profile.avatar_update": "\u66F4\u65B0\u5934\u50CF\u6635\u79F0",
275
314
  "system.profile.gender": "\u6027\u522B",
315
+ "system.profile.email": "\u90AE\u7BB1",
316
+ "system.profile.email_placeholder": "\u8BF7\u8F93\u5165\u90AE\u7BB1",
317
+ "system.profile.email_invalid": "\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u90AE\u7BB1",
318
+ "system.profile.id_type": "\u8BC1\u4EF6\u7C7B\u578B",
319
+ "system.profile.id_code": "\u8BC1\u4EF6\u53F7",
320
+ "system.profile.id_code_placeholder": "\u8BF7\u8F93\u5165\u8BC1\u4EF6\u53F7",
321
+ "system.profile.id_code_invalid": "\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u8BC1\u4EF6\u53F7",
322
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
323
+ "system.profile.id_type.id_card": "\u5C45\u6C11\u8EAB\u4EFD\u8BC1",
324
+ "system.profile.id_type.passport": "\u62A4\u7167",
325
+ "system.profile.id_type.hk_macao_permit": "\u6E2F\u6FB3\u5C45\u6C11\u6765\u5F80\u5185\u5730\u901A\u884C\u8BC1",
326
+ "system.profile.id_type.taiwan_permit": "\u53F0\u6E7E\u5C45\u6C11\u6765\u5F80\u5927\u9646\u901A\u884C\u8BC1",
327
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u56FD\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u4EFD\u8BC1",
328
+ "system.profile.id_type.other": "\u5176\u4ED6\u8BC1\u4EF6",
276
329
  "system.profile.login_prompt": "\u70B9\u51FB\u767B\u5F55\u8D26\u53F7",
277
330
  "system.profile.mobile": "\u624B\u673A\u53F7",
278
331
  "system.profile.nick_name": "\u6635\u79F0",
@@ -372,6 +425,20 @@ var en_US_default = {
372
425
  "system.profile.avatar_upload_failed": "Avatar upload failed",
373
426
  "system.profile.avatar_update": "Update avatar and name",
374
427
  "system.profile.gender": "Gender",
428
+ "system.profile.email": "Email",
429
+ "system.profile.email_placeholder": "Enter email",
430
+ "system.profile.email_invalid": "Enter a valid email",
431
+ "system.profile.id_type": "ID type",
432
+ "system.profile.id_code": "ID number",
433
+ "system.profile.id_code_placeholder": "Enter ID number",
434
+ "system.profile.id_code_invalid": "Enter a valid ID number",
435
+ "system.profile.id_type.unspecified": "Unspecified",
436
+ "system.profile.id_type.id_card": "Resident ID card",
437
+ "system.profile.id_type.passport": "Passport",
438
+ "system.profile.id_type.hk_macao_permit": "Mainland travel permit for Hong Kong/Macao residents",
439
+ "system.profile.id_type.taiwan_permit": "Mainland travel permit for Taiwan residents",
440
+ "system.profile.id_type.foreign_permanent_residence": "Foreign permanent residence ID",
441
+ "system.profile.id_type.other": "Other ID",
375
442
  "system.profile.login_prompt": "Tap to log in",
376
443
  "system.profile.mobile": "Mobile",
377
444
  "system.profile.nick_name": "Nickname",
@@ -471,6 +538,20 @@ var ja_JP_default = {
471
538
  "system.profile.avatar_upload_failed": "\u30A2\u30D0\u30BF\u30FC\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
472
539
  "system.profile.avatar_update": "\u30A2\u30D0\u30BF\u30FC\u306E\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0\u3092\u66F4\u65B0",
473
540
  "system.profile.gender": "\u6027\u5225",
541
+ "system.profile.email": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9",
542
+ "system.profile.email_placeholder": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
543
+ "system.profile.email_invalid": "\u6B63\u3057\u3044\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
544
+ "system.profile.id_type": "\u8EAB\u5206\u8A3C\u660E\u66F8\u306E\u7A2E\u985E",
545
+ "system.profile.id_code": "\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7",
546
+ "system.profile.id_code_placeholder": "\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
547
+ "system.profile.id_code_invalid": "\u6B63\u3057\u3044\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
548
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
549
+ "system.profile.id_type.id_card": "\u8EAB\u5206\u8A3C\u660E\u66F8",
550
+ "system.profile.id_type.passport": "\u30D1\u30B9\u30DD\u30FC\u30C8",
551
+ "system.profile.id_type.hk_macao_permit": "\u9999\u6E2F\u30FB\u30DE\u30AB\u30AA\u4F4F\u6C11\u5185\u5730\u901A\u884C\u8A3C",
552
+ "system.profile.id_type.taiwan_permit": "\u53F0\u6E7E\u4F4F\u6C11\u5927\u9678\u901A\u884C\u8A3C",
553
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u56FD\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u5206\u8A3C\u660E\u66F8",
554
+ "system.profile.id_type.other": "\u305D\u306E\u4ED6",
474
555
  "system.profile.login_prompt": "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u304F\u3060\u3055\u3044",
475
556
  "system.profile.mobile": "\u643A\u5E2F\u96FB\u8A71\u756A\u53F7",
476
557
  "system.profile.nick_name": "\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0",
@@ -570,6 +651,20 @@ var zh_TW_default = {
570
651
  "system.profile.avatar_upload_failed": "\u4E0A\u50B3\u982D\u50CF\u5931\u6557",
571
652
  "system.profile.avatar_update": "\u66F4\u65B0\u982D\u50CF\u66B1\u7A31",
572
653
  "system.profile.gender": "\u6027\u5225",
654
+ "system.profile.email": "\u96FB\u5B50\u90F5\u4EF6",
655
+ "system.profile.email_placeholder": "\u8ACB\u8F38\u5165\u96FB\u5B50\u90F5\u4EF6",
656
+ "system.profile.email_invalid": "\u8ACB\u8F38\u5165\u6B63\u78BA\u7684\u96FB\u5B50\u90F5\u4EF6",
657
+ "system.profile.id_type": "\u8B49\u4EF6\u985E\u578B",
658
+ "system.profile.id_code": "\u8B49\u4EF6\u865F",
659
+ "system.profile.id_code_placeholder": "\u8ACB\u8F38\u5165\u8B49\u4EF6\u865F",
660
+ "system.profile.id_code_invalid": "\u8ACB\u8F38\u5165\u6B63\u78BA\u7684\u8B49\u4EF6\u865F",
661
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
662
+ "system.profile.id_type.id_card": "\u5C45\u6C11\u8EAB\u5206\u8B49",
663
+ "system.profile.id_type.passport": "\u8B77\u7167",
664
+ "system.profile.id_type.hk_macao_permit": "\u6E2F\u6FB3\u5C45\u6C11\u4F86\u5F80\u5167\u5730\u901A\u884C\u8B49",
665
+ "system.profile.id_type.taiwan_permit": "\u81FA\u7063\u5C45\u6C11\u4F86\u5F80\u5927\u9678\u901A\u884C\u8B49",
666
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u570B\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u5206\u8B49",
667
+ "system.profile.id_type.other": "\u5176\u4ED6\u8B49\u4EF6",
573
668
  "system.profile.login_prompt": "\u9EDE\u9078\u767B\u5165\u8CEC\u865F",
574
669
  "system.profile.mobile": "\u624B\u6A5F\u865F",
575
670
  "system.profile.nick_name": "\u66B1\u7A31",
package/dist/module.mjs CHANGED
@@ -24,7 +24,12 @@ var systemPages = {
24
24
  navigationBarTitleText: ""
25
25
  }
26
26
  },
27
- "pagesMember/message/index": { style: { navigationBarTitleText: "" } },
27
+ "pagesMember/message/index": {
28
+ style: {
29
+ navigationStyle: "custom",
30
+ navigationBarTitleText: ""
31
+ }
32
+ },
28
33
  "pagesMember/message/detail": { style: { navigationBarTitleText: "" } }
29
34
  };
30
35
 
@@ -99,6 +104,20 @@ var zh_CN_default = {
99
104
  "system.profile.avatar_upload_failed": "\u4E0A\u4F20\u5934\u50CF\u5931\u8D25",
100
105
  "system.profile.avatar_update": "\u66F4\u65B0\u5934\u50CF\u6635\u79F0",
101
106
  "system.profile.gender": "\u6027\u522B",
107
+ "system.profile.email": "\u90AE\u7BB1",
108
+ "system.profile.email_placeholder": "\u8BF7\u8F93\u5165\u90AE\u7BB1",
109
+ "system.profile.email_invalid": "\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u90AE\u7BB1",
110
+ "system.profile.id_type": "\u8BC1\u4EF6\u7C7B\u578B",
111
+ "system.profile.id_code": "\u8BC1\u4EF6\u53F7",
112
+ "system.profile.id_code_placeholder": "\u8BF7\u8F93\u5165\u8BC1\u4EF6\u53F7",
113
+ "system.profile.id_code_invalid": "\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u8BC1\u4EF6\u53F7",
114
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
115
+ "system.profile.id_type.id_card": "\u5C45\u6C11\u8EAB\u4EFD\u8BC1",
116
+ "system.profile.id_type.passport": "\u62A4\u7167",
117
+ "system.profile.id_type.hk_macao_permit": "\u6E2F\u6FB3\u5C45\u6C11\u6765\u5F80\u5185\u5730\u901A\u884C\u8BC1",
118
+ "system.profile.id_type.taiwan_permit": "\u53F0\u6E7E\u5C45\u6C11\u6765\u5F80\u5927\u9646\u901A\u884C\u8BC1",
119
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u56FD\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u4EFD\u8BC1",
120
+ "system.profile.id_type.other": "\u5176\u4ED6\u8BC1\u4EF6",
102
121
  "system.profile.login_prompt": "\u70B9\u51FB\u767B\u5F55\u8D26\u53F7",
103
122
  "system.profile.mobile": "\u624B\u673A\u53F7",
104
123
  "system.profile.nick_name": "\u6635\u79F0",
@@ -198,6 +217,20 @@ var en_US_default = {
198
217
  "system.profile.avatar_upload_failed": "Avatar upload failed",
199
218
  "system.profile.avatar_update": "Update avatar and name",
200
219
  "system.profile.gender": "Gender",
220
+ "system.profile.email": "Email",
221
+ "system.profile.email_placeholder": "Enter email",
222
+ "system.profile.email_invalid": "Enter a valid email",
223
+ "system.profile.id_type": "ID type",
224
+ "system.profile.id_code": "ID number",
225
+ "system.profile.id_code_placeholder": "Enter ID number",
226
+ "system.profile.id_code_invalid": "Enter a valid ID number",
227
+ "system.profile.id_type.unspecified": "Unspecified",
228
+ "system.profile.id_type.id_card": "Resident ID card",
229
+ "system.profile.id_type.passport": "Passport",
230
+ "system.profile.id_type.hk_macao_permit": "Mainland travel permit for Hong Kong/Macao residents",
231
+ "system.profile.id_type.taiwan_permit": "Mainland travel permit for Taiwan residents",
232
+ "system.profile.id_type.foreign_permanent_residence": "Foreign permanent residence ID",
233
+ "system.profile.id_type.other": "Other ID",
201
234
  "system.profile.login_prompt": "Tap to log in",
202
235
  "system.profile.mobile": "Mobile",
203
236
  "system.profile.nick_name": "Nickname",
@@ -297,6 +330,20 @@ var ja_JP_default = {
297
330
  "system.profile.avatar_upload_failed": "\u30A2\u30D0\u30BF\u30FC\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
298
331
  "system.profile.avatar_update": "\u30A2\u30D0\u30BF\u30FC\u306E\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0\u3092\u66F4\u65B0",
299
332
  "system.profile.gender": "\u6027\u5225",
333
+ "system.profile.email": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9",
334
+ "system.profile.email_placeholder": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
335
+ "system.profile.email_invalid": "\u6B63\u3057\u3044\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
336
+ "system.profile.id_type": "\u8EAB\u5206\u8A3C\u660E\u66F8\u306E\u7A2E\u985E",
337
+ "system.profile.id_code": "\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7",
338
+ "system.profile.id_code_placeholder": "\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
339
+ "system.profile.id_code_invalid": "\u6B63\u3057\u3044\u8EAB\u5206\u8A3C\u660E\u66F8\u756A\u53F7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
340
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
341
+ "system.profile.id_type.id_card": "\u8EAB\u5206\u8A3C\u660E\u66F8",
342
+ "system.profile.id_type.passport": "\u30D1\u30B9\u30DD\u30FC\u30C8",
343
+ "system.profile.id_type.hk_macao_permit": "\u9999\u6E2F\u30FB\u30DE\u30AB\u30AA\u4F4F\u6C11\u5185\u5730\u901A\u884C\u8A3C",
344
+ "system.profile.id_type.taiwan_permit": "\u53F0\u6E7E\u4F4F\u6C11\u5927\u9678\u901A\u884C\u8A3C",
345
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u56FD\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u5206\u8A3C\u660E\u66F8",
346
+ "system.profile.id_type.other": "\u305D\u306E\u4ED6",
300
347
  "system.profile.login_prompt": "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u30ED\u30B0\u30A4\u30F3\u3057\u3066\u304F\u3060\u3055\u3044",
301
348
  "system.profile.mobile": "\u643A\u5E2F\u96FB\u8A71\u756A\u53F7",
302
349
  "system.profile.nick_name": "\u30CB\u30C3\u30AF\u30CD\u30FC\u30E0",
@@ -396,6 +443,20 @@ var zh_TW_default = {
396
443
  "system.profile.avatar_upload_failed": "\u4E0A\u50B3\u982D\u50CF\u5931\u6557",
397
444
  "system.profile.avatar_update": "\u66F4\u65B0\u982D\u50CF\u66B1\u7A31",
398
445
  "system.profile.gender": "\u6027\u5225",
446
+ "system.profile.email": "\u96FB\u5B50\u90F5\u4EF6",
447
+ "system.profile.email_placeholder": "\u8ACB\u8F38\u5165\u96FB\u5B50\u90F5\u4EF6",
448
+ "system.profile.email_invalid": "\u8ACB\u8F38\u5165\u6B63\u78BA\u7684\u96FB\u5B50\u90F5\u4EF6",
449
+ "system.profile.id_type": "\u8B49\u4EF6\u985E\u578B",
450
+ "system.profile.id_code": "\u8B49\u4EF6\u865F",
451
+ "system.profile.id_code_placeholder": "\u8ACB\u8F38\u5165\u8B49\u4EF6\u865F",
452
+ "system.profile.id_code_invalid": "\u8ACB\u8F38\u5165\u6B63\u78BA\u7684\u8B49\u4EF6\u865F",
453
+ "system.profile.id_type.unspecified": "\u672A\u6307\u5B9A",
454
+ "system.profile.id_type.id_card": "\u5C45\u6C11\u8EAB\u5206\u8B49",
455
+ "system.profile.id_type.passport": "\u8B77\u7167",
456
+ "system.profile.id_type.hk_macao_permit": "\u6E2F\u6FB3\u5C45\u6C11\u4F86\u5F80\u5167\u5730\u901A\u884C\u8B49",
457
+ "system.profile.id_type.taiwan_permit": "\u81FA\u7063\u5C45\u6C11\u4F86\u5F80\u5927\u9678\u901A\u884C\u8B49",
458
+ "system.profile.id_type.foreign_permanent_residence": "\u5916\u570B\u4EBA\u6C38\u4E45\u5C45\u7559\u8EAB\u5206\u8B49",
459
+ "system.profile.id_type.other": "\u5176\u4ED6\u8B49\u4EF6",
399
460
  "system.profile.login_prompt": "\u9EDE\u9078\u767B\u5165\u8CEC\u865F",
400
461
  "system.profile.mobile": "\u624B\u6A5F\u865F",
401
462
  "system.profile.nick_name": "\u66B1\u7A31",
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.31",
10
+ "version": "0.0.33",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
@@ -35,7 +35,7 @@
35
35
  "@dcloudio/uni-app": "3.0.0-5010520260709002",
36
36
  "@dcloudio/uni-ui": "^1.4.28",
37
37
  "vue": "^3.4.21",
38
- "@liujitcn/kratos-uni-app-core": "^0.0.31"
38
+ "@liujitcn/kratos-uni-app-core": "^0.0.33"
39
39
  },
40
40
  "devDependencies": {
41
41
  "esbuild": "^0.20.2",
@@ -4,7 +4,7 @@ import {
4
4
  http,
5
5
  requestBaseURL,
6
6
  } from '@liujitcn/kratos-uni-app-core/utils/http'
7
- import type { ListAiMessageRequest, ListAiMessageResponse } from '../../rpc/base/v1/ai_session'
7
+ import type { ListAiMessageRequest, ListAiMessageResponse } from '../../../rpc/base/v1/ai_session'
8
8
  import type {
9
9
  AiMessageService,
10
10
  DeleteAiMessageRequest,
@@ -14,7 +14,7 @@ import type {
14
14
  SendAiMessageRequest,
15
15
  SendAiMessageResponse,
16
16
  UpdateAiMessageRequest,
17
- } from '../../rpc/base/v1/ai_message'
17
+ } from '../../../rpc/base/v1/ai_message'
18
18
  import { getLocaleRequestHeaders, t } from '@liujitcn/kratos-uni-app-core'
19
19
 
20
20
  const AI_SESSION_URL = '/v1/base/ai/session'
@@ -13,7 +13,7 @@ import type {
13
13
  ListAiSessionResponse,
14
14
  UpdateAiSessionRequest,
15
15
  UpdateAiSessionResponse,
16
- } from '../../rpc/base/v1/ai_session'
16
+ } from '../../../rpc/base/v1/ai_session'
17
17
 
18
18
  const AI_SESSION_URL = '/v1/base/ai/session'
19
19
 
@@ -3,7 +3,7 @@ import type {
3
3
  AiToolService,
4
4
  ListAiShortcutRequest,
5
5
  ListAiShortcutResponse,
6
- } from '../../rpc/base/v1/ai_tool'
6
+ } from '../../../rpc/base/v1/ai_tool'
7
7
 
8
8
  const AI_SHORTCUT_URL = '/v1/base/ai/shortcut'
9
9
 
@@ -4,6 +4,8 @@ import type {
4
4
  DeleteNotificationRequest,
5
5
  GetNotificationRequest,
6
6
  GetNotificationSummaryRequest,
7
+ ListNotificationCategoriesRequest,
8
+ ListNotificationCategoriesResponse,
7
9
  MarkAllNotificationReadRequest,
8
10
  MarkNotificationReadRequest,
9
11
  MarkNotificationUnreadRequest,
@@ -13,29 +15,67 @@ import type {
13
15
  PageNotificationRequest,
14
16
  PageNotificationResponse,
15
17
  RestoreNotificationRequest,
16
- } from '../../rpc/base/v1/notification'
17
- import type { Empty } from '../../rpc/google/protobuf/empty'
18
+ } from '../../../rpc/base/v1/notification'
19
+ import type { Empty } from '../../../rpc/google/protobuf/empty'
18
20
 
19
21
  const NOTIFICATION_URL = '/v1/base/notification'
20
22
 
21
23
  /** uni-app 当前用户站内信服务。 */
22
24
  export class NotificationServiceImpl implements NotificationService {
23
25
  /** 分页查询收件箱。 */
24
- PageNotification(request: PageNotificationRequest): Promise<PageNotificationResponse> {
25
- return http({ url: NOTIFICATION_URL, method: 'GET', authMode: 'required', data: request })
26
+ async PageNotification(request: PageNotificationRequest): Promise<PageNotificationResponse> {
27
+ const data = Object.fromEntries(
28
+ Object.entries(request).filter(([, value]) => value !== undefined),
29
+ )
30
+ const response = await http<Partial<PageNotificationResponse>>({
31
+ url: NOTIFICATION_URL,
32
+ method: 'GET',
33
+ authMode: 'required',
34
+ data,
35
+ })
36
+ return {
37
+ ...response,
38
+ notifications: Array.isArray(response.notifications) ? response.notifications : [],
39
+ total: response.total ?? 0,
40
+ next_cursor_id: response.next_cursor_id ?? 0,
41
+ has_more: response.has_more ?? false,
42
+ }
43
+ }
44
+ /** 查询当前可用的消息分类。 */
45
+ async ListNotificationCategories(
46
+ request: ListNotificationCategoriesRequest,
47
+ ): Promise<ListNotificationCategoriesResponse> {
48
+ const response = await http<Partial<ListNotificationCategoriesResponse>>({
49
+ url: `${NOTIFICATION_URL}/categories`,
50
+ method: 'GET',
51
+ authMode: 'required',
52
+ data: request,
53
+ })
54
+ return {
55
+ ...response,
56
+ categories: Array.isArray(response.categories) ? response.categories : [],
57
+ }
26
58
  }
27
59
  /** 查询消息详情。 */
28
60
  GetNotification(request: GetNotificationRequest): Promise<Notification> {
29
61
  return http({ url: `${NOTIFICATION_URL}/${request.id}`, method: 'GET', authMode: 'required' })
30
62
  }
31
63
  /** 查询未读汇总。 */
32
- GetNotificationSummary(request: GetNotificationSummaryRequest): Promise<NotificationSummary> {
33
- return http({
64
+ async GetNotificationSummary(
65
+ request: GetNotificationSummaryRequest,
66
+ ): Promise<NotificationSummary> {
67
+ const response = await http<Partial<NotificationSummary>>({
34
68
  url: `${NOTIFICATION_URL}/summary`,
35
69
  method: 'GET',
36
70
  authMode: 'required',
37
71
  data: request,
38
72
  })
73
+ return {
74
+ ...response,
75
+ unread_total: response.unread_total ?? 0,
76
+ latest_delivery_id: response.latest_delivery_id ?? 0,
77
+ category_unread: Array.isArray(response.category_unread) ? response.category_unread : [],
78
+ }
39
79
  }
40
80
  /** 标记消息已读。 */
41
81
  MarkNotificationRead(request: MarkNotificationReadRequest): Promise<Empty> {
@@ -68,6 +68,20 @@
68
68
  "system.profile.avatar_upload_failed": "Avatar upload failed",
69
69
  "system.profile.avatar_update": "Update avatar and name",
70
70
  "system.profile.gender": "Gender",
71
+ "system.profile.email": "Email",
72
+ "system.profile.email_placeholder": "Enter email",
73
+ "system.profile.email_invalid": "Enter a valid email",
74
+ "system.profile.id_type": "ID type",
75
+ "system.profile.id_code": "ID number",
76
+ "system.profile.id_code_placeholder": "Enter ID number",
77
+ "system.profile.id_code_invalid": "Enter a valid ID number",
78
+ "system.profile.id_type.unspecified": "Unspecified",
79
+ "system.profile.id_type.id_card": "Resident ID card",
80
+ "system.profile.id_type.passport": "Passport",
81
+ "system.profile.id_type.hk_macao_permit": "Mainland travel permit for Hong Kong/Macao residents",
82
+ "system.profile.id_type.taiwan_permit": "Mainland travel permit for Taiwan residents",
83
+ "system.profile.id_type.foreign_permanent_residence": "Foreign permanent residence ID",
84
+ "system.profile.id_type.other": "Other ID",
71
85
  "system.profile.login_prompt": "Tap to log in",
72
86
  "system.profile.mobile": "Mobile",
73
87
  "system.profile.nick_name": "Nickname",
@@ -68,6 +68,20 @@
68
68
  "system.profile.avatar_upload_failed": "アバターのアップロードに失敗しました",
69
69
  "system.profile.avatar_update": "アバターのニックネームを更新",
70
70
  "system.profile.gender": "性別",
71
+ "system.profile.email": "メールアドレス",
72
+ "system.profile.email_placeholder": "メールアドレスを入力してください",
73
+ "system.profile.email_invalid": "正しいメールアドレスを入力してください",
74
+ "system.profile.id_type": "身分証明書の種類",
75
+ "system.profile.id_code": "身分証明書番号",
76
+ "system.profile.id_code_placeholder": "身分証明書番号を入力してください",
77
+ "system.profile.id_code_invalid": "正しい身分証明書番号を入力してください",
78
+ "system.profile.id_type.unspecified": "未指定",
79
+ "system.profile.id_type.id_card": "身分証明書",
80
+ "system.profile.id_type.passport": "パスポート",
81
+ "system.profile.id_type.hk_macao_permit": "香港・マカオ住民内地通行証",
82
+ "system.profile.id_type.taiwan_permit": "台湾住民大陸通行証",
83
+ "system.profile.id_type.foreign_permanent_residence": "外国人永久居留身分証明書",
84
+ "system.profile.id_type.other": "その他",
71
85
  "system.profile.login_prompt": "クリックしてアカウントにログインしてください",
72
86
  "system.profile.mobile": "携帯電話番号",
73
87
  "system.profile.nick_name": "ニックネーム",
@@ -68,6 +68,20 @@
68
68
  "system.profile.avatar_upload_failed": "上传头像失败",
69
69
  "system.profile.avatar_update": "更新头像昵称",
70
70
  "system.profile.gender": "性别",
71
+ "system.profile.email": "邮箱",
72
+ "system.profile.email_placeholder": "请输入邮箱",
73
+ "system.profile.email_invalid": "请输入正确的邮箱",
74
+ "system.profile.id_type": "证件类型",
75
+ "system.profile.id_code": "证件号",
76
+ "system.profile.id_code_placeholder": "请输入证件号",
77
+ "system.profile.id_code_invalid": "请输入正确的证件号",
78
+ "system.profile.id_type.unspecified": "未指定",
79
+ "system.profile.id_type.id_card": "居民身份证",
80
+ "system.profile.id_type.passport": "护照",
81
+ "system.profile.id_type.hk_macao_permit": "港澳居民来往内地通行证",
82
+ "system.profile.id_type.taiwan_permit": "台湾居民来往大陆通行证",
83
+ "system.profile.id_type.foreign_permanent_residence": "外国人永久居留身份证",
84
+ "system.profile.id_type.other": "其他证件",
71
85
  "system.profile.login_prompt": "点击登录账号",
72
86
  "system.profile.mobile": "手机号",
73
87
  "system.profile.nick_name": "昵称",
@@ -68,6 +68,20 @@
68
68
  "system.profile.avatar_upload_failed": "上傳頭像失敗",
69
69
  "system.profile.avatar_update": "更新頭像暱稱",
70
70
  "system.profile.gender": "性別",
71
+ "system.profile.email": "電子郵件",
72
+ "system.profile.email_placeholder": "請輸入電子郵件",
73
+ "system.profile.email_invalid": "請輸入正確的電子郵件",
74
+ "system.profile.id_type": "證件類型",
75
+ "system.profile.id_code": "證件號",
76
+ "system.profile.id_code_placeholder": "請輸入證件號",
77
+ "system.profile.id_code_invalid": "請輸入正確的證件號",
78
+ "system.profile.id_type.unspecified": "未指定",
79
+ "system.profile.id_type.id_card": "居民身分證",
80
+ "system.profile.id_type.passport": "護照",
81
+ "system.profile.id_type.hk_macao_permit": "港澳居民來往內地通行證",
82
+ "system.profile.id_type.taiwan_permit": "臺灣居民來往大陸通行證",
83
+ "system.profile.id_type.foreign_permanent_residence": "外國人永久居留身分證",
84
+ "system.profile.id_type.other": "其他證件",
71
85
  "system.profile.login_prompt": "點選登入賬號",
72
86
  "system.profile.mobile": "手機號",
73
87
  "system.profile.nick_name": "暱稱",
@@ -1,7 +1,7 @@
1
1
  import { ref } from 'vue'
2
2
  import { setAppMenuBadge } from '@liujitcn/kratos-uni-app-core/navigation'
3
3
  import { getRequestAccessToken } from '@liujitcn/kratos-uni-app-core/utils/http'
4
- import { defNotificationService } from './api/base/notification'
4
+ import { defNotificationService } from './api/base/v1/notification'
5
5
 
6
6
  /** System 模块共享的站内信未读数。 */
7
7
  export const notificationUnreadTotal = ref(0)
package/src/pages.ts CHANGED
@@ -23,6 +23,11 @@ export const systemPages: Record<string, KratosAppPageConfig> = {
23
23
  navigationBarTitleText: '',
24
24
  },
25
25
  },
26
- 'pagesMember/message/index': { style: { navigationBarTitleText: '' } },
26
+ 'pagesMember/message/index': {
27
+ style: {
28
+ navigationStyle: 'custom',
29
+ navigationBarTitleText: '',
30
+ },
31
+ },
27
32
  'pagesMember/message/detail': { style: { navigationBarTitleText: '' } },
28
33
  }
@@ -184,6 +184,32 @@ export interface PageNotificationResponse {
184
184
  has_more: boolean;
185
185
  }
186
186
 
187
+ /** 消息分类列表查询条件。 */
188
+ export interface ListNotificationCategoriesRequest {
189
+ }
190
+
191
+ /** 消息分类列表响应。 */
192
+ export interface ListNotificationCategoriesResponse {
193
+ /** 消息分类列表 */
194
+ categories: NotificationCategory[];
195
+ }
196
+
197
+ /** 当前可用的消息分类。 */
198
+ export interface NotificationCategory {
199
+ /** 消息分类ID */
200
+ id: number;
201
+ /** 分类编码 */
202
+ code: string;
203
+ /** 分类名称 */
204
+ name: string;
205
+ /** 分类图标 */
206
+ icon: string;
207
+ /** 分类颜色 */
208
+ color: string;
209
+ /** 排序 */
210
+ sort: number;
211
+ }
212
+
187
213
  /** 消息详情查询条件。 */
188
214
  export interface GetNotificationRequest {
189
215
  /** 投递ID */
@@ -296,6 +322,8 @@ export interface Notification {
296
322
  export interface NotificationService {
297
323
  /** 分页查询当前用户收件箱。 */
298
324
  PageNotification(request: PageNotificationRequest): Promise<PageNotificationResponse>;
325
+ /** 查询消息分类列表。 */
326
+ ListNotificationCategories(request: ListNotificationCategoriesRequest): Promise<ListNotificationCategoriesResponse>;
299
327
  /** 查询当前用户未读汇总。 */
300
328
  GetNotificationSummary(request: GetNotificationSummaryRequest): Promise<NotificationSummary>;
301
329
  /** 查询当前用户消息详情。 */
@@ -16,7 +16,6 @@ const profile = computed(() => userStore.userInfo)
16
16
 
17
17
  /** 打开移动端 AI 助手静态页,未登录时先进入登录流程。 */
18
18
  const navigateToAi = () => {
19
- console.log('[DEBUG-login-click] ai')
20
19
  if (!userStore.ensureAuthenticated()) {
21
20
  navigateToLogin()
22
21
  return
@@ -26,7 +25,6 @@ const navigateToAi = () => {
26
25
 
27
26
  /** 打开设置页,未登录时先进入登录流程。 */
28
27
  const navigateToSettings = () => {
29
- console.log('[DEBUG-login-click] settings')
30
28
  if (!userStore.ensureAuthenticated()) {
31
29
  navigateToLogin()
32
30
  return
@@ -1,9 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  import { onLoad } from '@dcloudio/uni-app'
3
3
  import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
4
- import { defAiMessageService, StreamAiMessageByChunkedRequest } from '../../../api/base/ai_message'
5
- import { defAiSessionService } from '../../../api/base/ai_session'
6
- import { defAiToolService } from '../../../api/base/ai_tool'
4
+ import {
5
+ defAiMessageService,
6
+ StreamAiMessageByChunkedRequest,
7
+ } from '../../../api/base/v1/ai_message'
8
+ import { defAiSessionService } from '../../../api/base/v1/ai_session'
9
+ import { defAiToolService } from '../../../api/base/v1/ai_tool'
7
10
  import type { AiMessage } from '../../../rpc/base/v1/ai_session'
8
11
  import type { AiAttachment, AiSession } from '../../../rpc/base/v1/ai_session'
9
12
  import type { AiShortcut, AiToolCall } from '../../../rpc/base/v1/ai_tool'
@@ -2,9 +2,10 @@
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 { defNotificationService } from '../../../api/base/notification'
5
+ import { defNotificationService } from '../../../api/base/v1/notification'
6
6
  import type { Notification } from '../../../rpc/base/v1/notification'
7
7
  import { MessageActionType, MessageContentFormat } from '../../../rpc/base/v1/notification'
8
+ import { resolveMessageCategoryIcon } from './icons'
8
9
 
9
10
  const detail = ref<Notification>()
10
11
 
@@ -46,7 +47,16 @@ function openAction() {
46
47
  <view class="detail-page" v-if="detail">
47
48
  <text class="detail-title">{{ detail.title }}</text>
48
49
  <view class="detail-meta">
49
- <text>{{ detail.category_name }}</text>
50
+ <view class="detail-category" :style="{ color: detail.category_color || undefined }">
51
+ <uni-icons
52
+ class="detail-category__icon"
53
+ :type="resolveMessageCategoryIcon(detail.category_icon)"
54
+ size="18"
55
+ :color="detail.category_color || '#64748b'"
56
+ aria-hidden="true"
57
+ />
58
+ <text>{{ detail.category_name }}</text>
59
+ </view>
50
60
  <text>{{ detail.sender_name }}</text>
51
61
  <text>{{ detail.received_at }}</text>
52
62
  </view>
@@ -69,13 +79,15 @@ function openAction() {
69
79
 
70
80
  <style scoped lang="scss">
71
81
  .detail-page {
82
+ box-sizing: border-box;
72
83
  min-height: 100vh;
73
- padding: 36rpx 30rpx;
74
- background: var(--kratos-color-background);
84
+ padding: 24rpx 30rpx calc(96rpx + env(safe-area-inset-bottom));
85
+ background: var(--kratos-color-background, #f7f8fa);
86
+ color: var(--kratos-color-text, #1f2937);
75
87
  }
76
88
  .detail-title {
77
89
  display: block;
78
- color: var(--kratos-color-text);
90
+ color: var(--kratos-color-text, #1f2937);
79
91
  font-size: 42rpx;
80
92
  font-weight: 700;
81
93
  line-height: 1.35;
@@ -85,11 +97,18 @@ function openAction() {
85
97
  flex-wrap: wrap;
86
98
  gap: 18rpx;
87
99
  margin: 24rpx 0 36rpx;
88
- color: var(--kratos-color-text-muted);
100
+ color: var(--kratos-color-text-muted, #6b7280);
89
101
  font-size: 24rpx;
90
102
  }
103
+ .detail-category {
104
+ display: inline-flex;
105
+ align-items: center;
106
+ }
107
+ .detail-category__icon {
108
+ margin-right: 8rpx;
109
+ }
91
110
  .detail-content {
92
- color: var(--kratos-color-text);
111
+ color: var(--kratos-color-text, #1f2937);
93
112
  font-size: 30rpx;
94
113
  line-height: 1.8;
95
114
  white-space: pre-wrap;
@@ -97,14 +116,14 @@ function openAction() {
97
116
  }
98
117
  .detail-content-rich {
99
118
  display: block;
100
- color: var(--kratos-color-text);
119
+ color: var(--kratos-color-text, #1f2937);
101
120
  font-size: 30rpx;
102
121
  line-height: 1.8;
103
122
  overflow-wrap: anywhere;
104
123
  }
105
124
  .empty {
106
125
  padding-top: 180rpx;
107
- color: var(--kratos-color-text-muted);
126
+ color: var(--kratos-color-text-muted, #6b7280);
108
127
  text-align: center;
109
128
  }
110
129
  </style>
@@ -0,0 +1,38 @@
1
+ /** 消息分类在 uni-app 中可渲染的图标名称。 */
2
+ export type MessageCategoryIcon =
3
+ | 'notification'
4
+ | 'notification-filled'
5
+ | 'locked-filled'
6
+ | 'list'
7
+ | 'chatboxes-filled'
8
+ | 'info-filled'
9
+ | 'flag-filled'
10
+ | 'checkbox-filled'
11
+ | 'paperplane-filled'
12
+ | 'settings-filled'
13
+ | 'person-filled'
14
+ | 'calendar-filled'
15
+ | 'chatbubble-filled'
16
+ | 'bars'
17
+
18
+ const categoryIconMap: Record<string, MessageCategoryIcon> = {
19
+ Bell: 'notification-filled',
20
+ Lock: 'locked-filled',
21
+ List: 'list',
22
+ Message: 'chatboxes-filled',
23
+ InfoFilled: 'info-filled',
24
+ WarningFilled: 'flag-filled',
25
+ CircleCheckFilled: 'checkbox-filled',
26
+ Promotion: 'paperplane-filled',
27
+ Setting: 'settings-filled',
28
+ User: 'person-filled',
29
+ Calendar: 'calendar-filled',
30
+ ChatDotRound: 'chatbubble-filled',
31
+ DataAnalysis: 'bars',
32
+ CollectionTag: 'list',
33
+ }
34
+
35
+ /** 将管理端维护的 Element Plus 图标名转换为 uni-icons 图标名。 */
36
+ export function resolveMessageCategoryIcon(icon?: string): MessageCategoryIcon {
37
+ return categoryIconMap[icon ?? ''] ?? 'notification'
38
+ }
@@ -1,19 +1,20 @@
1
1
  <script setup lang="ts">
2
2
  import { onShow } from '@dcloudio/uni-app'
3
3
  import { ref } from 'vue'
4
- import { navigateAppRoute, t } from '@liujitcn/kratos-uni-app-core'
5
- import { defNotificationService } from '../../../api/base/notification'
6
- import type { Notification } from '../../../rpc/base/v1/notification'
4
+ import { navigateAppView, t } from '@liujitcn/kratos-uni-app-core'
5
+ import { defNotificationService } from '../../../api/base/v1/notification'
6
+ import type { Notification, NotificationCategory } from '../../../rpc/base/v1/notification'
7
7
  import { NotificationView } from '../../../rpc/base/v1/notification'
8
8
  import { notificationUnreadTotal, refreshNotificationSummary } from '../../../notification'
9
+ import { resolveMessageCategoryIcon } from './icons'
9
10
 
10
11
  const items = ref<Notification[]>([])
11
12
  const loading = ref(false)
12
13
  const cursorId = ref(0)
13
14
  const finished = ref(false)
14
15
  const selectedView = ref(NotificationView.NOTIFICATION_VIEW_INBOX)
15
- const categoryInput = ref('')
16
16
  const categoryId = ref<number>()
17
+ const categoryOptions = ref<NotificationCategory[]>([])
17
18
  const viewOptions = [
18
19
  { value: NotificationView.NOTIFICATION_VIEW_INBOX, key: 'system.notification.view.inbox' },
19
20
  { value: NotificationView.NOTIFICATION_VIEW_UNREAD, key: 'system.notification.view.unread' },
@@ -21,10 +22,17 @@ const viewOptions = [
21
22
  ]
22
23
 
23
24
  onShow(() => {
25
+ void loadCategories().catch(() => undefined)
24
26
  void refresh()
25
27
  void refreshNotificationSummary()
26
28
  })
27
29
 
30
+ /** 加载管理端维护的消息分类。 */
31
+ async function loadCategories() {
32
+ const result = await defNotificationService.ListNotificationCategories({})
33
+ categoryOptions.value = result.categories ?? []
34
+ }
35
+
28
36
  /** 刷新站内信列表。 */
29
37
  async function refresh() {
30
38
  cursorId.value = 0
@@ -39,10 +47,9 @@ function changeView(view: NotificationView) {
39
47
  void refresh()
40
48
  }
41
49
 
42
- /** 应用分类筛选。 */
43
- function applyCategoryFilter() {
44
- const value = Number(categoryInput.value)
45
- categoryId.value = Number.isInteger(value) && value > 0 ? value : undefined
50
+ /** 切换消息分类。 */
51
+ function changeCategory(id?: number) {
52
+ categoryId.value = id
46
53
  void refresh()
47
54
  }
48
55
 
@@ -95,19 +102,56 @@ async function loadMore() {
95
102
  /** 打开站内信详情。 */
96
103
  async function openDetail(item: Notification) {
97
104
  if (!item.read_at) await defNotificationService.MarkNotificationRead({ ids: [item.id] })
98
- navigateAppRoute(`app/message/detail?id=${item.id}`)
105
+ navigateAppView('MESSAGE_DETAIL', { id: String(item.id) })
99
106
  }
100
107
  </script>
101
108
 
102
109
  <template>
103
110
  <view class="message-page">
104
111
  <view class="message-header">
105
- <text class="message-title">{{ t('system.notification.title') }}</text>
106
- <text v-if="notificationUnreadTotal > 0" class="message-unread">{{
107
- notificationUnreadTotal > 99 ? '99+' : notificationUnreadTotal
108
- }}</text>
112
+ <view class="message-header__title">
113
+ <text class="message-title">{{ t('system.notification.title') }}</text>
114
+ <view v-if="notificationUnreadTotal > 0" class="message-unread-dot" aria-hidden="true" />
115
+ </view>
116
+ <button
117
+ v-if="
118
+ selectedView !== NotificationView.NOTIFICATION_VIEW_ARCHIVED &&
119
+ notificationUnreadTotal > 0
120
+ "
121
+ class="message-action message-action--read message-header__action"
122
+ @tap="markAllRead"
123
+ >
124
+ {{ t('system.notification.mark_all_read') }}
125
+ </button>
109
126
  </view>
110
127
  <view class="message-controls">
128
+ <scroll-view class="message-category-scroll" scroll-x :show-scrollbar="false">
129
+ <view class="message-category-list">
130
+ <button
131
+ class="message-category"
132
+ :class="{ active: categoryId === undefined }"
133
+ @tap="changeCategory()"
134
+ >
135
+ {{ t('system.notification.view.inbox') }}
136
+ </button>
137
+ <button
138
+ v-for="category in categoryOptions"
139
+ :key="category.id"
140
+ class="message-category"
141
+ :class="{ active: categoryId === category.id }"
142
+ @tap="changeCategory(category.id)"
143
+ >
144
+ <uni-icons
145
+ class="message-category__icon"
146
+ :type="resolveMessageCategoryIcon(category.icon)"
147
+ size="16"
148
+ :color="categoryId === category.id ? '#fff' : category.color || '#64748b'"
149
+ aria-hidden="true"
150
+ />
151
+ {{ category.name }}
152
+ </button>
153
+ </view>
154
+ </scroll-view>
111
155
  <view class="message-tabs">
112
156
  <button
113
157
  v-for="option in viewOptions"
@@ -118,31 +162,22 @@ async function openDetail(item: Notification) {
118
162
  {{ t(option.key) }}
119
163
  </button>
120
164
  </view>
121
- <view class="message-filter">
122
- <input
123
- v-model="categoryInput"
124
- type="number"
125
- :placeholder="t('system.notification.category_filter')"
126
- @confirm="applyCategoryFilter"
127
- />
128
- <button @tap="applyCategoryFilter">{{ t('common.action.confirm') }}</button>
129
- <button
130
- v-if="
131
- selectedView !== NotificationView.NOTIFICATION_VIEW_ARCHIVED &&
132
- notificationUnreadTotal > 0
133
- "
134
- @tap="markAllRead"
135
- >
136
- {{ t('system.notification.mark_all_read') }}
137
- </button>
138
- </view>
139
165
  </view>
140
166
  <view v-for="item in items" :key="item.id" class="message-item" @tap="openDetail(item)">
141
167
  <view class="message-item__main">
142
168
  <text :class="['message-item__title', { unread: !item.read_at }]">{{ item.title }}</text>
143
169
  <text class="message-item__time">{{ item.received_at }}</text>
144
170
  </view>
145
- <text class="message-item__category">{{ item.category_name }}</text>
171
+ <view class="message-item__category">
172
+ <uni-icons
173
+ class="message-item__category-icon"
174
+ :type="resolveMessageCategoryIcon(item.category_icon)"
175
+ size="16"
176
+ :color="item.category_color || '#64748b'"
177
+ aria-hidden="true"
178
+ />
179
+ <text>{{ item.category_name }}</text>
180
+ </view>
146
181
  <view class="message-item__actions">
147
182
  <button
148
183
  v-if="selectedView !== NotificationView.NOTIFICATION_VIEW_ARCHIVED && item.allow_archive"
@@ -172,75 +207,150 @@ async function openDetail(item: Notification) {
172
207
 
173
208
  <style scoped lang="scss">
174
209
  .message-page {
210
+ box-sizing: border-box;
175
211
  min-height: 100vh;
176
- padding: 24rpx;
177
- background: var(--kratos-color-background);
212
+ padding: 16rpx 24rpx calc(120rpx + env(safe-area-inset-bottom));
213
+ background: var(--kratos-color-background, #f7f8fa);
214
+ color: var(--kratos-color-text, #1f2937);
178
215
  }
179
216
  .message-header {
180
- padding: 20rpx 4rpx 28rpx;
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: space-between;
220
+ gap: 20rpx;
221
+ padding: 20rpx 4rpx 24rpx;
222
+ }
223
+ .message-header__title {
224
+ display: flex;
225
+ align-items: center;
226
+ min-width: 0;
181
227
  }
182
228
  .message-title {
229
+ display: block;
183
230
  font-size: 40rpx;
184
231
  font-weight: 700;
185
- color: var(--kratos-color-text);
232
+ line-height: 1.35;
233
+ color: var(--kratos-color-text, #1f2937);
186
234
  }
187
- .message-unread {
188
- min-width: 36rpx;
189
- padding: 4rpx 10rpx;
190
- border-radius: 18rpx;
235
+ .message-unread-dot {
236
+ width: 12rpx;
237
+ height: 12rpx;
238
+ margin-left: 8rpx;
239
+ border-radius: 50%;
191
240
  background: #e5484d;
192
- color: #fff;
193
- font-size: 22rpx;
194
- line-height: 28rpx;
195
- text-align: center;
196
241
  }
197
242
  .message-controls {
198
- margin-bottom: 18rpx;
243
+ margin-bottom: 24rpx;
244
+ }
245
+ .message-category-scroll {
246
+ width: 100%;
247
+ white-space: nowrap;
248
+ }
249
+ .message-category-list {
250
+ display: flex;
251
+ gap: 12rpx;
252
+ width: max-content;
253
+ padding-bottom: 4rpx;
199
254
  }
255
+ .message-category,
200
256
  .message-tabs,
201
- .message-filter,
202
257
  .message-item__actions {
203
258
  display: flex;
204
259
  align-items: center;
260
+ }
261
+ .message-category {
262
+ flex-shrink: 0;
263
+ height: 64rpx;
264
+ margin: 0;
265
+ padding: 0 22rpx;
266
+ border: 1rpx solid var(--kratos-color-border, #e2e8f0);
267
+ border-radius: 32rpx;
268
+ background: #fff;
269
+ color: var(--kratos-color-text-muted, #6b7280);
270
+ font-size: 24rpx;
271
+ line-height: 64rpx;
272
+ white-space: nowrap;
273
+ }
274
+ .message-category.active {
275
+ border-color: var(--kratos-color-primary, #27ba9b);
276
+ background: var(--kratos-color-primary, #27ba9b);
277
+ color: #fff;
278
+ }
279
+ .message-tabs {
280
+ gap: 12rpx;
281
+ margin-top: 18rpx;
282
+ padding: 6rpx;
283
+ border-radius: 12rpx;
284
+ background: #eef1f3;
285
+ }
286
+ .message-item__actions {
205
287
  gap: 12rpx;
288
+ flex-wrap: wrap;
289
+ justify-content: flex-end;
206
290
  }
207
291
  .message-tab {
292
+ box-sizing: border-box;
208
293
  flex: 1;
294
+ min-width: 0;
295
+ height: 68rpx;
209
296
  margin: 0;
210
297
  padding: 0 12rpx;
298
+ border: 0;
299
+ border-radius: 8rpx;
211
300
  background: transparent;
212
- color: var(--kratos-color-text-muted);
301
+ color: var(--kratos-color-text-muted, #6b7280);
213
302
  font-size: 26rpx;
303
+ line-height: 68rpx;
304
+ white-space: nowrap;
305
+ }
306
+ .message-tab::after,
307
+ .message-category::after,
308
+ .message-action::after,
309
+ .message-item__actions button::after,
310
+ .load-more::after {
311
+ border: 0;
214
312
  }
215
313
  .message-tab.active {
216
- background: var(--kratos-color-primary);
314
+ background: var(--kratos-color-primary, #27ba9b);
217
315
  color: #fff;
218
316
  }
219
- .message-filter {
220
- margin-top: 14rpx;
221
- }
222
- .message-filter input {
223
- min-width: 0;
224
- flex: 1;
225
- padding: 12rpx 16rpx;
226
- border: 1rpx solid var(--kratos-color-border);
227
- border-radius: 8rpx;
228
- }
229
- .message-filter button,
317
+ .message-action,
230
318
  .message-item__actions button {
319
+ box-sizing: border-box;
320
+ flex-shrink: 0;
321
+ width: auto;
322
+ height: 68rpx;
231
323
  margin: 0;
232
- padding: 0 14rpx;
324
+ padding: 0 20rpx;
325
+ border: 0;
326
+ border-radius: 8rpx;
327
+ background: #eef1f3;
328
+ color: var(--kratos-color-text, #1f2937);
233
329
  font-size: 24rpx;
330
+ line-height: 68rpx;
331
+ white-space: nowrap;
234
332
  }
235
- .message-item__actions {
236
- justify-content: flex-end;
237
- margin-top: 14rpx;
333
+ .message-action--read {
334
+ background: #e8f8f4;
335
+ color: var(--kratos-color-primary, #16806d);
336
+ }
337
+ .message-header__action {
338
+ height: 56rpx;
339
+ padding: 0 18rpx;
340
+ line-height: 56rpx;
341
+ }
342
+ .message-item__actions button {
343
+ height: 56rpx;
344
+ padding: 0 16rpx;
345
+ font-size: 22rpx;
346
+ line-height: 56rpx;
238
347
  }
239
348
  .message-item {
240
349
  margin-bottom: 16rpx;
241
350
  padding: 28rpx;
242
351
  border-radius: 12rpx;
243
352
  background: #fff;
353
+ box-shadow: 0 4rpx 16rpx rgba(31, 41, 55, 0.05);
244
354
  }
245
355
  .message-item__main {
246
356
  display: flex;
@@ -249,33 +359,47 @@ async function openDetail(item: Notification) {
249
359
  gap: 20rpx;
250
360
  }
251
361
  .message-item__title {
362
+ display: block;
252
363
  min-width: 0;
253
364
  flex: 1;
254
365
  overflow: hidden;
255
- color: var(--kratos-color-text);
366
+ color: var(--kratos-color-text, #1f2937);
256
367
  text-overflow: ellipsis;
257
368
  white-space: nowrap;
258
369
  }
259
370
  .message-item__title.unread {
260
- color: var(--kratos-color-text);
371
+ color: var(--kratos-color-text, #1f2937);
261
372
  font-weight: 700;
262
373
  }
263
374
  .message-item__time,
264
375
  .message-item__category {
265
- color: var(--kratos-color-text-muted);
376
+ color: var(--kratos-color-text-muted, #6b7280);
266
377
  font-size: 24rpx;
267
378
  }
379
+ .message-item__time {
380
+ flex-shrink: 0;
381
+ }
268
382
  .message-item__category {
269
- display: block;
383
+ display: flex;
384
+ align-items: center;
270
385
  margin-top: 14rpx;
271
386
  }
272
387
  .load-more {
388
+ box-sizing: border-box;
389
+ width: 100%;
390
+ height: 72rpx;
273
391
  margin-top: 24rpx;
392
+ padding: 0 24rpx;
393
+ border: 0;
394
+ border-radius: 8rpx;
395
+ background: #eef1f3;
396
+ color: var(--kratos-color-text, #1f2937);
274
397
  font-size: 28rpx;
398
+ line-height: 72rpx;
275
399
  }
276
400
  .empty {
277
401
  padding: 120rpx 0;
278
- color: var(--kratos-color-text-muted);
402
+ color: var(--kratos-color-text-muted, #6b7280);
279
403
  text-align: center;
280
404
  }
281
405
  </style>
@@ -1,11 +1,12 @@
1
1
  <script setup lang="ts">
2
- import { defAuthService } from '@liujitcn/kratos-uni-app-core/api/system/auth'
2
+ import { defAuthService } from '@liujitcn/kratos-uni-app-core/api/system/app/v1/auth'
3
3
  import { useUserStore } from '@liujitcn/kratos-uni-app-core/stores'
4
4
  import type { UserProfileForm } from '@liujitcn/kratos-uni-app-core/rpc/system/app/v1/auth'
5
5
  import { onLoad } from '@dcloudio/uni-app'
6
- import { ref } from 'vue'
6
+ import { computed, ref } from 'vue'
7
7
  import type { BaseDictForm_DictItem } from '@liujitcn/kratos-uni-app-core/rpc/system/app/v1/base_dict'
8
- import { defBaseDictService } from '@liujitcn/kratos-uni-app-core/api/system/base_dict'
8
+ import { BaseUserIDType } from '@liujitcn/kratos-uni-app-core/rpc/system/common/v1/common'
9
+ import { defBaseDictService } from '@liujitcn/kratos-uni-app-core/api/system/app/v1/base_dict'
9
10
  import { formatSrc } from '@liujitcn/kratos-uni-app-core/utils/index'
10
11
  import { uploadFile } from '@liujitcn/kratos-uni-app-core/utils/file'
11
12
  import { navigateToLogin } from '@liujitcn/kratos-uni-app-core/utils/navigation'
@@ -23,6 +24,36 @@ const imgMaxSize = ref(1024 * 1024)
23
24
 
24
25
  // 获取个人信息,修改个人信息需提供初始值
25
26
  const userInfo = ref({} as UserProfileForm)
27
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
28
+ const idCodePattern = /^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/
29
+ const idTypeOptions = computed(() => [
30
+ {
31
+ label: t('system.profile.id_type.unspecified'),
32
+ value: BaseUserIDType.BASE_USER_ID_TYPE_UNSPECIFIED,
33
+ },
34
+ { label: t('system.profile.id_type.id_card'), value: BaseUserIDType.BASE_USER_ID_TYPE_ID_CARD },
35
+ { label: t('system.profile.id_type.passport'), value: BaseUserIDType.BASE_USER_ID_TYPE_PASSPORT },
36
+ {
37
+ label: t('system.profile.id_type.hk_macao_permit'),
38
+ value: BaseUserIDType.BASE_USER_ID_TYPE_HK_MACAO_PERMIT,
39
+ },
40
+ {
41
+ label: t('system.profile.id_type.taiwan_permit'),
42
+ value: BaseUserIDType.BASE_USER_ID_TYPE_TAIWAN_PERMIT,
43
+ },
44
+ {
45
+ label: t('system.profile.id_type.foreign_permanent_residence'),
46
+ value: BaseUserIDType.BASE_USER_ID_TYPE_FOREIGN_PERMANENT_RESIDENCE,
47
+ },
48
+ { label: t('system.profile.id_type.other'), value: BaseUserIDType.BASE_USER_ID_TYPE_OTHER },
49
+ ])
50
+ const idTypeIndex = computed(() => {
51
+ const index = idTypeOptions.value.findIndex((item) => item.value === userInfo.value.id_type)
52
+ return index >= 0 ? index : 0
53
+ })
54
+ const idTypeLabel = computed(
55
+ () => idTypeOptions.value[idTypeIndex.value]?.label || idTypeOptions.value[0].label,
56
+ )
26
57
  const syncUserStoreProfile = (profile: UserProfileForm) => {
27
58
  if (!userStore.userInfo) {
28
59
  return
@@ -33,6 +64,9 @@ const syncUserStoreProfile = (profile: UserProfileForm) => {
33
64
  userStore.userInfo.gender = profile.gender
34
65
  userStore.userInfo.phone = profile.phone
35
66
  userStore.userInfo.avatar = profile.avatar
67
+ userStore.userInfo.email = profile.email
68
+ userStore.userInfo.id_type = profile.id_type
69
+ userStore.userInfo.id_code = profile.id_code
36
70
  }
37
71
 
38
72
  const getUserData = async () => {
@@ -47,7 +81,7 @@ const genderList = ref<BaseDictForm_DictItem[]>([])
47
81
  const getDictData = async () => {
48
82
  const genderCode = 'base_user_gender'
49
83
  const res = await defBaseDictService.GetBaseDict({
50
- value: genderCode,
84
+ code: genderCode,
51
85
  })
52
86
  genderList.value = res.items || []
53
87
  }
@@ -123,7 +157,7 @@ const uploadAvatar = async (file: string) => {
123
157
  try {
124
158
  const fileInfo = await uploadFile('avatar', file)
125
159
  userInfo.value.avatar = fileInfo.url
126
- await defAuthService.UpdateUserProfile(userInfo.value)
160
+ await defAuthService.UpdateUserProfile({ user_profile: userInfo.value })
127
161
  syncUserStoreProfile(userInfo.value)
128
162
  await uni.showToast({ icon: 'success', title: t('system.profile.update_success') })
129
163
  } catch {
@@ -136,6 +170,13 @@ const onGenderChange: UniHelper.RadioGroupOnChange = (ev) => {
136
170
  userInfo.value.gender = Number(ev.detail.value)
137
171
  }
138
172
 
173
+ // 选择证件类型。
174
+ const onIdTypeChange: UniHelper.SelectorPickerOnChange = (ev) => {
175
+ userInfo.value.id_type =
176
+ idTypeOptions.value[Number(ev.detail.value)]?.value ??
177
+ BaseUserIDType.BASE_USER_ID_TYPE_UNSPECIFIED
178
+ }
179
+
139
180
  // #ifdef MP-WEIXIN
140
181
  // 新增授权手机号处理
141
182
  const onGetPhoneNumber: UniHelper.ButtonOnGetphonenumber = async (e) => {
@@ -160,13 +201,27 @@ const onSubmit = async () => {
160
201
  return
161
202
  }
162
203
 
163
- const { nick_name, gender } = userInfo.value
204
+ if (userInfo.value.email && !emailPattern.test(userInfo.value.email)) {
205
+ await uni.showToast({ icon: 'none', title: t('system.profile.email_invalid') })
206
+ return
207
+ }
208
+ if (userInfo.value.id_code && !idCodePattern.test(userInfo.value.id_code)) {
209
+ await uni.showToast({ icon: 'none', title: t('system.profile.id_code_invalid') })
210
+ return
211
+ }
212
+
213
+ const { nick_name, gender, email, id_type, id_code } = userInfo.value
164
214
  await defAuthService.UpdateUserProfile({
165
- nick_name: nick_name,
166
- gender: gender,
167
- avatar: userInfo.value.avatar,
168
- phone: userInfo.value.phone,
169
- user_name: userInfo.value.user_name,
215
+ user_profile: {
216
+ nick_name: nick_name,
217
+ gender: gender,
218
+ avatar: userInfo.value.avatar,
219
+ phone: userInfo.value.phone,
220
+ user_name: userInfo.value.user_name,
221
+ email,
222
+ id_type,
223
+ id_code,
224
+ },
170
225
  })
171
226
  // 更新Store昵称
172
227
  syncUserStoreProfile(userInfo.value)
@@ -230,6 +285,37 @@ const onSubmit = async () => {
230
285
  v-model="userInfo.nick_name"
231
286
  />
232
287
  </view>
288
+ <view class="form-item">
289
+ <text class="label">{{ t('system.profile.email') }}</text>
290
+ <input
291
+ class="input"
292
+ type="text"
293
+ :placeholder="t('system.profile.email_placeholder')"
294
+ v-model="userInfo.email"
295
+ />
296
+ </view>
297
+ <view class="form-item">
298
+ <text class="label">{{ t('system.profile.id_type') }}</text>
299
+ <picker
300
+ class="picker"
301
+ mode="selector"
302
+ :range="idTypeOptions"
303
+ range-key="label"
304
+ :value="idTypeIndex"
305
+ @change="onIdTypeChange"
306
+ >
307
+ <view class="input">{{ idTypeLabel }}</view>
308
+ </picker>
309
+ </view>
310
+ <view class="form-item">
311
+ <text class="label">{{ t('system.profile.id_code') }}</text>
312
+ <input
313
+ class="input"
314
+ type="text"
315
+ :placeholder="t('system.profile.id_code_placeholder')"
316
+ v-model="userInfo.id_code"
317
+ />
318
+ </view>
233
319
  <view class="form-item">
234
320
  <text class="label">{{ t('system.profile.gender') }}</text>
235
321
  <radio-group @change="onGenderChange">