create-fullstack-scaffold 0.4.9 → 0.4.10

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.
Files changed (127) hide show
  1. package/dist/cli/index.js +499 -100
  2. package/dist/cli/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/template/CLAUDE.md +18 -6
  5. package/template/drizzle/0004_add_merchants_products.sql +30 -0
  6. package/template/drizzle/meta/_journal.json +7 -0
  7. package/template/eslint-rules/module-boundary.js +6 -2
  8. package/template/eslint-rules/no-cross-module-service-import.js +105 -0
  9. package/template/eslint-rules/no-disable-type-safe-client.js +5 -0
  10. package/template/eslint.config.js +9 -0
  11. package/template/lint-scripts/config/project.config.ts +21 -0
  12. package/template/lint-scripts/validate-all.ts +48 -12
  13. package/template/lint-scripts/validators/index.ts +29 -0
  14. package/template/lint-scripts/validators/module-public-api.validator.ts +103 -0
  15. package/template/lint-scripts/validators/schema-uniqueness.validator.ts +147 -0
  16. package/template/modules.config.ts +2 -0
  17. package/template/package.json +2 -0
  18. package/template/playwright.config.ts +14 -0
  19. package/template/src/admin/App.tsx +40 -3
  20. package/template/src/admin/components/LanguageSwitcher.tsx +22 -0
  21. package/template/src/admin/components/NotificationDrawer.tsx +108 -38
  22. package/template/src/admin/components/StatsCard.tsx +3 -1
  23. package/template/src/admin/components/ThemeToggle.tsx +28 -0
  24. package/template/src/admin/hooks/useRoles.ts +2 -2
  25. package/template/src/admin/i18n/index.ts +18 -0
  26. package/template/src/admin/i18n/locales/en-US.json +381 -0
  27. package/template/src/admin/i18n/locales/zh-CN.json +381 -0
  28. package/template/src/admin/i18n/useLanguage.ts +21 -0
  29. package/template/src/admin/layouts/Header.tsx +37 -16
  30. package/template/src/admin/layouts/Layout.tsx +7 -3
  31. package/template/src/admin/layouts/Sidebar.tsx +70 -68
  32. package/template/src/admin/main.tsx +1 -0
  33. package/template/src/admin/pages/ContentPage.tsx +68 -56
  34. package/template/src/admin/pages/DashboardPage.tsx +82 -76
  35. package/template/src/admin/pages/DisputesPage.tsx +61 -53
  36. package/template/src/admin/pages/LoginPage.tsx +41 -33
  37. package/template/src/admin/pages/OrdersPage.tsx +69 -64
  38. package/template/src/admin/pages/PermissionsPage.tsx +10 -8
  39. package/template/src/admin/pages/PluginDashboardPage.tsx +3 -1
  40. package/template/src/admin/pages/PluginManagementPage.tsx +3 -1
  41. package/template/src/admin/pages/RegisterPage.tsx +18 -16
  42. package/template/src/admin/pages/RolesPage.tsx +51 -49
  43. package/template/src/admin/pages/SettingsPage.tsx +22 -22
  44. package/template/src/admin/pages/SystemLogsPage.tsx +32 -30
  45. package/template/src/admin/pages/TicketsPage.tsx +67 -59
  46. package/template/src/admin/pages/UsersPage.tsx +63 -53
  47. package/template/src/admin/pages/__tests__/RolesPage.test.tsx +2 -2
  48. package/template/src/admin/stores/themeStore.ts +32 -0
  49. package/template/src/client/index.css +73 -0
  50. package/template/src/merchant/App.tsx +2 -0
  51. package/template/src/merchant/components/MerchantGuard.tsx +2 -6
  52. package/template/src/merchant/components/__tests__/MerchantGuard.test.tsx +117 -0
  53. package/template/src/merchant/layouts/Header.tsx +2 -2
  54. package/template/src/merchant/pages/DashboardPage.tsx +2 -2
  55. package/template/src/merchant/pages/DisputesPage.tsx +14 -10
  56. package/template/src/merchant/pages/LoginPage.tsx +49 -0
  57. package/template/src/merchant/pages/OrdersPage.tsx +17 -10
  58. package/template/src/merchant/pages/ProductsPage.tsx +31 -8
  59. package/template/src/merchant/pages/SettingsPage.tsx +3 -7
  60. package/template/src/merchant/stores/__tests__/merchantStore.test.ts +317 -0
  61. package/template/src/merchant/stores/merchantStore.ts +24 -27
  62. package/template/src/server/db/schema/index.ts +2 -0
  63. package/template/src/server/db/schema/merchants.ts +26 -0
  64. package/template/src/server/db/schema/products.ts +25 -0
  65. package/template/src/server/db/test-setup.ts +237 -0
  66. package/template/src/server/middleware/__tests__/audit-log.test.ts +144 -0
  67. package/template/src/server/middleware/__tests__/cors.test.ts +84 -0
  68. package/template/src/server/middleware/__tests__/logger.test.ts +117 -0
  69. package/template/src/server/middleware/__tests__/rate-limit.test.ts +73 -0
  70. package/template/src/server/middleware/__tests__/realtime-env.test.ts +56 -0
  71. package/template/src/server/middleware/__tests__/tenant-isolation.test.ts +150 -0
  72. package/template/src/server/module-admin/__tests__/admin-routes.test.ts +1 -1
  73. package/template/src/server/module-admin/module.ts +4 -0
  74. package/template/src/server/module-admin/routes/admin-notification-routes.ts +3 -3
  75. package/template/src/server/module-admin/routes/user-management-routes.ts +2 -2
  76. package/template/src/server/module-auth/__tests__/auth-routes.test.ts +315 -0
  77. package/template/src/server/module-auth/__tests__/profile-routes.test.ts +83 -0
  78. package/template/src/server/module-auth/module.ts +2 -0
  79. package/template/src/server/module-content/module.ts +1 -0
  80. package/template/src/server/module-content/routes/content-routes.ts +2 -2
  81. package/template/src/server/module-dispute/module.ts +1 -0
  82. package/template/src/server/module-dispute/routes/dispute-routes.ts +2 -2
  83. package/template/src/server/module-merchant/__tests__/merchant-routes.test.ts +218 -0
  84. package/template/src/server/module-merchant/__tests__/merchant-service.test.ts +183 -0
  85. package/template/src/server/module-merchant/index.ts +10 -0
  86. package/template/src/server/module-merchant/module.ts +33 -0
  87. package/template/src/server/module-merchant/routes/merchant-routes.ts +138 -0
  88. package/template/src/server/module-merchant/services/merchant-service.ts +292 -0
  89. package/template/src/server/module-notifications/index.ts +18 -0
  90. package/template/src/server/module-notifications/module.ts +2 -0
  91. package/template/src/server/module-order/module.ts +1 -0
  92. package/template/src/server/module-order/routes/order-routes.ts +2 -2
  93. package/template/src/server/module-permission/routes/role-routes.ts +3 -3
  94. package/template/src/server/module-plugin/__tests__/admin-category-service.test.ts +181 -0
  95. package/template/src/server/module-plugin/__tests__/admin-plugin-service.test.ts +277 -0
  96. package/template/src/server/module-plugin/__tests__/admin-stats-service.test.ts +170 -0
  97. package/template/src/server/module-plugin/__tests__/plugin-review-service.test.ts +260 -0
  98. package/template/src/server/module-plugin/__tests__/plugin-seed-service.test.ts +170 -0
  99. package/template/src/server/module-plugin/module.ts +3 -0
  100. package/template/src/server/module-tenant/__tests__/tenant-routes.test.ts +142 -0
  101. package/template/src/server/module-tenant/__tests__/tenant-service.test.ts +152 -0
  102. package/template/src/server/module-tenant/module.ts +1 -0
  103. package/template/src/server/module-tenant/services/tenant-service.ts +1 -37
  104. package/template/src/server/module-ticket/module.ts +1 -0
  105. package/template/src/server/module-ticket/routes/ticket-routes.ts +2 -2
  106. package/template/src/server/module-todos/module.ts +3 -0
  107. package/template/src/server/route-registry.ts +4 -0
  108. package/template/src/server/utils/__tests__/date.test.ts +130 -0
  109. package/template/src/server/utils/__tests__/env.test.ts +25 -0
  110. package/template/src/server/utils/__tests__/generate.test.ts +82 -0
  111. package/template/src/server/utils/__tests__/id-helpers.test.ts +29 -0
  112. package/template/src/server/utils/__tests__/json.test.ts +49 -0
  113. package/template/src/server/utils/__tests__/permission-utils.test.ts +26 -0
  114. package/template/src/server/utils/__tests__/uuid.test.ts +25 -0
  115. package/template/src/shared/core/module-manifest.ts +15 -0
  116. package/template/src/shared/modules/admin/schemas.ts +1 -1
  117. package/template/src/shared/modules/content/schemas.ts +2 -2
  118. package/template/src/shared/modules/dispute/schemas.ts +2 -2
  119. package/template/src/shared/modules/index.ts +181 -1
  120. package/template/src/shared/modules/merchant/index.ts +12 -0
  121. package/template/src/shared/modules/merchant/schemas.ts +63 -0
  122. package/template/src/shared/modules/order/schemas.ts +2 -2
  123. package/template/src/shared/modules/permission/index.ts +1 -1
  124. package/template/src/shared/modules/permission/schemas.ts +1 -1
  125. package/template/src/shared/modules/role/schemas.ts +2 -2
  126. package/template/src/shared/modules/ticket/schemas.ts +2 -2
  127. package/template/src/shared/schemas/index.ts +74 -2
@@ -0,0 +1,381 @@
1
+ {
2
+ "common": {
3
+ "confirm": "确定",
4
+ "cancel": "取消",
5
+ "save": "保存",
6
+ "delete": "删除",
7
+ "edit": "编辑",
8
+ "create": "创建",
9
+ "search": "搜索",
10
+ "reset": "重置",
11
+ "refresh": "刷新",
12
+ "loading": "加载中...",
13
+ "success": "操作成功",
14
+ "error": "操作失败",
15
+ "noData": "暂无数据",
16
+ "view": "查看",
17
+ "detail": "详情",
18
+ "status": "状态",
19
+ "actions": "操作",
20
+ "createdAt": "创建时间",
21
+ "close": "关闭",
22
+ "back": "返回",
23
+ "submit": "提交",
24
+ "yes": "是",
25
+ "no": "否",
26
+ "enabled": "启用",
27
+ "disabled": "禁用",
28
+ "total": "共 {{count}} 条",
29
+ "totalRecords": "共 {{count}} 条记录",
30
+ "vsLastMonth": "较上月"
31
+ },
32
+ "sidebar": {
33
+ "dashboard": "仪表盘",
34
+ "content": "内容管理",
35
+ "contentList": "内容列表",
36
+ "categories": "分类管理",
37
+ "usersOrders": "用户与订单",
38
+ "users": "用户管理",
39
+ "orders": "订单管理",
40
+ "tickets": "工单管理",
41
+ "disputes": "纠纷管理",
42
+ "system": "系统管理",
43
+ "roles": "角色权限",
44
+ "settings": "系统设置",
45
+ "logs": "系统日志",
46
+ "permissions": "权限管理"
47
+ },
48
+ "header": {
49
+ "home": "首页",
50
+ "dashboard": "仪表盘",
51
+ "profile": "个人资料",
52
+ "settings": "设置",
53
+ "logout": "退出登录",
54
+ "superAdmin": "超级管理员",
55
+ "moderator": "审核员",
56
+ "operator": "操作员"
57
+ },
58
+ "login": {
59
+ "title": "管理后台登录",
60
+ "username": "用户名",
61
+ "password": "密码",
62
+ "submit": "登录",
63
+ "noAccount": "还没有账号?",
64
+ "register": "注册",
65
+ "success": "登录成功!",
66
+ "failed": "登录失败!",
67
+ "quickLogin": "快速登录",
68
+ "quickLoginAs": "已以{{role}}身份登录!",
69
+ "usernameRequired": "请输入用户名!",
70
+ "passwordRequired": "请输入密码!",
71
+ "superAdmin": "超级管理员",
72
+ "customerService": "客服人员",
73
+ "normalUser": "普通用户"
74
+ },
75
+ "register": {
76
+ "title": "管理员注册",
77
+ "username": "用户名",
78
+ "email": "邮箱",
79
+ "password": "密码",
80
+ "confirmPassword": "确认密码",
81
+ "submit": "注册",
82
+ "hasAccount": "已有账号?",
83
+ "loginLink": "登录",
84
+ "success": "注册成功!请登录。",
85
+ "failed": "注册失败!",
86
+ "usernameRequired": "请输入用户名!",
87
+ "emailRequired": "请输入有效的邮箱!",
88
+ "passwordMin": "密码至少6个字符!",
89
+ "confirmRequired": "请确认密码!",
90
+ "passwordMismatch": "两次密码不一致!"
91
+ },
92
+ "dashboard": {
93
+ "title": "仪表盘",
94
+ "totalTodos": "总待办",
95
+ "pending": "待处理",
96
+ "completed": "已完成",
97
+ "lastUpdated": "最后更新",
98
+ "loading": "加载中...",
99
+ "loadFailed": "加载统计数据失败",
100
+ "testNotification": "测试通知功能",
101
+ "testNotificationDesc": "发送测试通知到所有已连接的客户端。error 和 warning 类型的通知会弹出 antd notification,info 和 success 类型只会出现在铃铛列表中。",
102
+ "sendTest": "发送测试通知",
103
+ "sent": "测试通知已发送 ({{type}})",
104
+ "sendFailed": "发送失败",
105
+ "notificationInfo": "信息",
106
+ "notificationSuccess": "成功",
107
+ "notificationWarning": "警告",
108
+ "notificationError": "错误",
109
+ "info": "信息",
110
+ "success": "成功",
111
+ "warning": "警告",
112
+ "error": "错误"
113
+ },
114
+ "settings": {
115
+ "title": "系统设置",
116
+ "general": "通用设置",
117
+ "siteName": "站点名称",
118
+ "siteNamePlaceholder": "请输入站点名称",
119
+ "siteDescription": "站点描述",
120
+ "siteDescriptionPlaceholder": "请输入站点描述",
121
+ "notification": "通知设置",
122
+ "emailNotifications": "邮件通知",
123
+ "emailNotificationsDesc": "接收重要更新的邮件通知",
124
+ "pushNotifications": "推送通知",
125
+ "pushNotificationsDesc": "在浏览器中接收推送通知",
126
+ "security": "安全设置",
127
+ "currentPassword": "当前密码",
128
+ "currentPasswordPlaceholder": "请输入当前密码",
129
+ "newPassword": "新密码",
130
+ "newPasswordPlaceholder": "请输入新密码",
131
+ "confirmNewPassword": "确认新密码",
132
+ "confirmNewPasswordPlaceholder": "请确认新密码",
133
+ "saveChanges": "保存更改",
134
+ "saved": "设置保存成功!"
135
+ },
136
+ "roles": {
137
+ "title": "角色管理",
138
+ "createRole": "创建角色",
139
+ "editRole": "编辑角色",
140
+ "roleCode": "角色代码",
141
+ "roleName": "角色名称",
142
+ "displayName": "显示名称",
143
+ "description": "描述",
144
+ "isSystem": "系统角色",
145
+ "roleCodePlaceholder": "请输入角色代码,如:manager",
146
+ "roleNamePlaceholder": "请输入角色名称",
147
+ "displayNamePlaceholder": "请输入显示名称",
148
+ "descriptionPlaceholder": "请输入角色描述",
149
+ "roleCodeRequired": "请输入角色代码",
150
+ "roleNameRequired": "请输入角色名称",
151
+ "displayNameRequired": "请输入显示名称",
152
+ "permissions": "权限",
153
+ "managePermissions": "管理角色权限 - {{name}}",
154
+ "deleteConfirm": "确定要删除这个角色吗?",
155
+ "created": "角色创建成功",
156
+ "updated": "角色更新成功",
157
+ "deleted": "角色删除成功",
158
+ "permissionUpdated": "权限更新成功",
159
+ "permissionValidationFailed": "权限依赖校验失败",
160
+ "permissionValidationErrors": "以下权限配置存在问题:",
161
+ "treeView": "树状选择",
162
+ "jsonView": "JSON编辑"
163
+ },
164
+ "users": {
165
+ "title": "用户管理",
166
+ "createUser": "创建用户",
167
+ "editUser": "编辑用户",
168
+ "username": "用户名",
169
+ "email": "邮箱",
170
+ "password": "密码",
171
+ "role": "角色",
172
+ "status": "状态",
173
+ "active": "正常",
174
+ "inactive": "未激活",
175
+ "locked": "已锁定",
176
+ "usernamePlaceholder": "请输入用户名",
177
+ "emailPlaceholder": "请输入邮箱",
178
+ "passwordPlaceholder": "请输入密码",
179
+ "rolePlaceholder": "请选择角色",
180
+ "statusPlaceholder": "请选择状态",
181
+ "usernameRequired": "请输入用户名",
182
+ "emailRequired": "请输入邮箱",
183
+ "emailInvalid": "请输入有效的邮箱地址",
184
+ "passwordRequired": "请输入密码",
185
+ "roleRequired": "请选择角色",
186
+ "statusRequired": "请选择状态",
187
+ "superAdmin": "超级管理员",
188
+ "customerService": "客服人员",
189
+ "normalUser": "普通用户",
190
+ "lock": "锁定",
191
+ "unlock": "解锁",
192
+ "deleteConfirm": "确定要删除此用户吗?",
193
+ "loading": "加载用户列表...",
194
+ "updating": "更新中...",
195
+ "creating": "创建中...",
196
+ "deleting": "删除中...",
197
+ "created": "用户创建成功",
198
+ "updated": "用户更新成功",
199
+ "deleted": "用户已删除",
200
+ "userLocked": "用户已锁定",
201
+ "userUnlocked": "用户已解锁"
202
+ },
203
+ "orders": {
204
+ "title": "订单管理",
205
+ "subtitle": "管理和处理所有订单",
206
+ "orderNo": "订单号",
207
+ "customer": "客户信息",
208
+ "product": "商品",
209
+ "amount": "金额",
210
+ "status": "状态",
211
+ "filterStatus": "筛选状态",
212
+ "totalOrders": "总订单",
213
+ "pending": "待处理",
214
+ "processing": "处理中",
215
+ "completed": "已完成",
216
+ "cancelled": "已取消",
217
+ "disputed": "争议中",
218
+ "process": "处理",
219
+ "cancelOrder": "取消",
220
+ "confirmCancel": "确认取消",
221
+ "confirmCancelContent": "确定要取消这个订单吗?",
222
+ "orderProcessed": "订单已开始处理",
223
+ "orderCancelled": "订单已取消",
224
+ "processFailed": "处理订单失败",
225
+ "cancelFailed": "取消订单失败",
226
+ "loadFailed": "获取订单列表失败",
227
+ "detail": "订单详情",
228
+ "customerName": "客户姓名",
229
+ "customerEmail": "客户邮箱",
230
+ "productName": "商品名称",
231
+ "orderAmount": "订单金额",
232
+ "orderStatus": "订单状态",
233
+ "updatedAt": "更新时间"
234
+ },
235
+ "tickets": {
236
+ "title": "客服中心",
237
+ "subtitle": "处理客户工单和咨询",
238
+ "ticketNo": "工单号",
239
+ "subject": "主题",
240
+ "customer": "客户",
241
+ "priority": "优先级",
242
+ "customerName": "客户姓名",
243
+ "customerEmail": "客户邮箱",
244
+ "assignedTo": "负责人",
245
+ "unassigned": "未分配",
246
+ "description": "描述",
247
+ "replies": "回复记录",
248
+ "totalTickets": "总工单",
249
+ "pending": "待处理",
250
+ "urgent": "紧急",
251
+ "resolved": "已解决",
252
+ "priorityLow": "低",
253
+ "priorityMedium": "中",
254
+ "priorityHigh": "高",
255
+ "priorityUrgent": "紧急",
256
+ "statusOpen": "待处理",
257
+ "statusInProgress": "处理中",
258
+ "statusWaitingCustomer": "等待客户",
259
+ "statusResolved": "已解决",
260
+ "statusClosed": "已关闭",
261
+ "confirmClose": "确认关闭",
262
+ "confirmCloseContent": "确定要关闭这个工单吗?",
263
+ "ticketClosed": "工单已关闭",
264
+ "closeFailed": "关闭工单失败",
265
+ "loadFailed": "获取工单列表失败",
266
+ "detail": "工单详情"
267
+ },
268
+ "disputes": {
269
+ "title": "争议处理",
270
+ "subtitle": "处理订单争议和客户投诉",
271
+ "disputeNo": "争议编号",
272
+ "orderNo": "订单号",
273
+ "customer": "客户",
274
+ "type": "类型",
275
+ "amount": "金额",
276
+ "totalDisputes": "总争议",
277
+ "pending": "待处理",
278
+ "resolved": "已解决",
279
+ "typeRefund": "退款争议",
280
+ "typeProductQuality": "商品质量",
281
+ "typeServiceQuality": "服务质量",
282
+ "typeDelivery": "配送问题",
283
+ "typeOther": "其他",
284
+ "statusPending": "待处理",
285
+ "statusInvestigating": "调查中",
286
+ "statusResolved": "已解决",
287
+ "statusRejected": "已驳回",
288
+ "resolve": "解决",
289
+ "resolveDispute": "解决争议",
290
+ "confirmResolve": "确定要解决这个争议吗?",
291
+ "amountLabel": "金额:¥{{amount}}",
292
+ "disputeResolved": "争议已解决",
293
+ "resolveFailed": "解决争议失败",
294
+ "loadFailed": "获取争议列表失败",
295
+ "detail": "争议详情",
296
+ "resolution": "解决方案"
297
+ },
298
+ "content": {
299
+ "title": "内容管理",
300
+ "subtitle": "管理文章、公告和教程",
301
+ "createContent": "创建内容",
302
+ "editContent": "编辑内容",
303
+ "titleCol": "标题",
304
+ "category": "分类",
305
+ "author": "作者",
306
+ "viewsLikes": "浏览/点赞",
307
+ "titlePlaceholder": "请输入标题",
308
+ "categoryPlaceholder": "请选择分类",
309
+ "contentPlaceholder": "请输入内容",
310
+ "tagsPlaceholder": "输入标签后按回车",
311
+ "titleRequired": "请输入标题",
312
+ "categoryRequired": "请选择分类",
313
+ "contentRequired": "请输入内容",
314
+ "catArticle": "文章",
315
+ "catAnnouncement": "公告",
316
+ "catTutorial": "教程",
317
+ "catNews": "新闻",
318
+ "catPolicy": "政策",
319
+ "statusDraft": "草稿",
320
+ "statusPublished": "已发布",
321
+ "statusArchived": "已归档",
322
+ "confirmDelete": "确认删除",
323
+ "confirmDeleteContent": "确定要删除这个内容吗?",
324
+ "deleted": "内容已删除",
325
+ "deleteFailed": "删除失败",
326
+ "updated": "内容已更新",
327
+ "created": "内容已创建",
328
+ "loadFailed": "获取内容列表失败",
329
+ "contentLabel": "内容",
330
+ "tags": "标签"
331
+ },
332
+ "permissions": {
333
+ "title": "权限管理",
334
+ "roleList": "角色列表",
335
+ "permissionCount": "权限数量: {{count}}",
336
+ "permissionMatrix": "权限矩阵",
337
+ "permission": "权限",
338
+ "superAdmin": "超级管理员",
339
+ "customerService": "客服人员",
340
+ "normalUser": "普通用户"
341
+ },
342
+ "systemLogs": {
343
+ "title": "系统日志",
344
+ "time": "时间",
345
+ "userId": "用户ID",
346
+ "action": "操作",
347
+ "resourceType": "资源类型",
348
+ "resourceId": "资源ID",
349
+ "ipAddress": "IP地址",
350
+ "actionType": "操作类型",
351
+ "resourceTypePlaceholder": "资源类型",
352
+ "search": "搜索",
353
+ "reset": "重置",
354
+ "detail": "日志详情",
355
+ "logId": "日志ID",
356
+ "userAgent": "User Agent",
357
+ "oldValue": "旧值",
358
+ "newValue": "新值"
359
+ },
360
+ "notification": {
361
+ "justNow": "刚刚",
362
+ "minutesAgo": "{{count}}分钟前",
363
+ "hoursAgo": "{{count}}小时前",
364
+ "daysAgo": "{{count}}天前",
365
+ "all": "全部",
366
+ "unread": "未读",
367
+ "read": "已读",
368
+ "noNotifications": "暂无通知",
369
+ "noUnread": "暂无未读通知",
370
+ "noRead": "暂无已读通知",
371
+ "center": "通知中心",
372
+ "markAllRead": "全部已读"
373
+ },
374
+ "theme": {
375
+ "toggleDark": "切换暗色模式",
376
+ "toggleLight": "切换亮色模式"
377
+ },
378
+ "monitor": {
379
+ "comingSoon": "即将推出"
380
+ }
381
+ }
@@ -0,0 +1,21 @@
1
+ import { useTranslation } from 'react-i18next'
2
+
3
+ export type Language = 'zh-CN' | 'en-US'
4
+
5
+ export function useLanguage() {
6
+ const { i18n, t } = useTranslation()
7
+
8
+ const currentLanguage = i18n.language as Language
9
+
10
+ const changeLanguage = (lang: Language) => {
11
+ i18n.changeLanguage(lang)
12
+ document.documentElement.setAttribute('lang', lang)
13
+ }
14
+
15
+ const formatDate = (date: string | Date, options?: Intl.DateTimeFormatOptions): string => {
16
+ const d = typeof date === 'string' ? new Date(date) : date
17
+ return d.toLocaleString(currentLanguage === 'zh-CN' ? 'zh-CN' : 'en-US', options)
18
+ }
19
+
20
+ return { t, currentLanguage, changeLanguage, formatDate }
21
+ }
@@ -7,22 +7,21 @@ import { useAdminStore } from '../stores/adminStore'
7
7
  import { useAdminNotifications } from '../hooks/useAdminNotifications'
8
8
  import { NotificationDrawer, NotificationBell } from '../components/NotificationDrawer'
9
9
  import { AccountSwitcher } from '../components/AccountSwitcher'
10
- import { useEffect, useState } from 'react'
10
+ import { ThemeToggle } from '../components/ThemeToggle'
11
+ import { LanguageSwitcher } from '../components/LanguageSwitcher'
12
+ import { useLanguage } from '../i18n/useLanguage'
13
+ import { useEffect, useState, useMemo } from 'react'
11
14
 
12
15
  interface HeaderProps {
13
16
  collapsed: boolean
14
17
  onToggle: () => void
15
18
  }
16
19
 
17
- const ROLE_OPTIONS = [
18
- { label: 'Super Admin', value: 'super_admin' },
19
- { label: 'Moderator', value: 'moderator' },
20
- { label: 'Operator', value: 'operator' },
21
- ]
22
-
23
20
  export const Header: React.FC<HeaderProps> = ({ collapsed, onToggle }) => {
24
21
  const navigate = useNavigate()
25
22
  const { user, logout } = useAdminStore()
23
+ const { t } = useLanguage()
24
+ const { token } = theme.useToken()
26
25
  const [drawerOpen, setDrawerOpen] = useState(false)
27
26
  const { status, notifications, unreadCount, connect, markAsRead, markAllAsRead } =
28
27
  useAdminNotifications()
@@ -38,34 +37,38 @@ export const Header: React.FC<HeaderProps> = ({ collapsed, onToggle }) => {
38
37
  navigate('/login')
39
38
  }
40
39
 
40
+ const ROLE_OPTIONS = useMemo(() => [
41
+ { label: t('header.superAdmin'), value: 'super_admin' },
42
+ { label: t('header.moderator'), value: 'moderator' },
43
+ { label: t('header.operator'), value: 'operator' },
44
+ ], [t])
45
+
41
46
  const userMenuItems: MenuProps['items'] = [
42
47
  {
43
48
  key: 'profile',
44
- label: 'Profile',
49
+ label: t('header.profile'),
45
50
  icon: <User className="w-4 h-4" />,
46
51
  },
47
52
  {
48
53
  key: 'settings',
49
- label: 'Settings',
54
+ label: t('header.settings'),
50
55
  icon: <Settings className="w-4 h-4" />,
51
56
  onClick: () => navigate('/settings'),
52
57
  },
53
58
  { type: 'divider' },
54
59
  {
55
60
  key: 'logout',
56
- label: 'Logout',
61
+ label: t('header.logout'),
57
62
  icon: <LogOut className="w-4 h-4" />,
58
63
  danger: true,
59
64
  onClick: handleLogout,
60
65
  },
61
66
  ]
62
67
 
63
- const { token } = theme.useToken()
64
-
65
68
  return (
66
69
  <>
67
70
  <AntLayout.Header
68
- className="flex items-center justify-between px-6 bg-white border-b border-gray-200"
71
+ className="flex items-center justify-between px-6"
69
72
  style={{
70
73
  paddingInline: 24,
71
74
  height: 64,
@@ -78,14 +81,21 @@ export const Header: React.FC<HeaderProps> = ({ collapsed, onToggle }) => {
78
81
  <div className="flex items-center gap-4">
79
82
  <button
80
83
  onClick={onToggle}
81
- className="p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer border-none bg-transparent"
84
+ className="p-2 rounded-lg transition-colors cursor-pointer border-none bg-transparent"
85
+ style={{ color: token.colorText }}
86
+ onMouseEnter={(e) => {
87
+ e.currentTarget.style.backgroundColor = token.colorFillQuaternary
88
+ }}
89
+ onMouseLeave={(e) => {
90
+ e.currentTarget.style.backgroundColor = 'transparent'
91
+ }}
82
92
  data-testid="toggle-sidebar-button"
83
93
  >
84
94
  {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
85
95
  </button>
86
96
 
87
97
  <Breadcrumb
88
- items={[{ title: 'Home' }, { title: 'Dashboard' }]}
98
+ items={[{ title: t('header.home') }, { title: t('header.dashboard') }]}
89
99
  style={{ fontSize: 14 }}
90
100
  />
91
101
  </div>
@@ -99,10 +109,21 @@ export const Header: React.FC<HeaderProps> = ({ collapsed, onToggle }) => {
99
109
  data-testid="role-switcher"
100
110
  />
101
111
 
112
+ <LanguageSwitcher />
113
+ <ThemeToggle />
102
114
  <NotificationBell unreadCount={unreadCount} onClick={() => setDrawerOpen(true)} />
103
115
  <AccountSwitcher />
104
116
  <Dropdown menu={{ items: userMenuItems }} placement="bottomRight" trigger={['click']}>
105
- <button className="flex items-center gap-2 p-1.5 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer border-none bg-transparent">
117
+ <button
118
+ className="flex items-center gap-2 p-1.5 rounded-lg transition-colors cursor-pointer border-none bg-transparent"
119
+ style={{ color: token.colorText }}
120
+ onMouseEnter={(e) => {
121
+ e.currentTarget.style.backgroundColor = token.colorFillQuaternary
122
+ }}
123
+ onMouseLeave={(e) => {
124
+ e.currentTarget.style.backgroundColor = 'transparent'
125
+ }}
126
+ >
106
127
  <Avatar
107
128
  size={32}
108
129
  src={user?.avatar}
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, useState } from 'react'
2
- import { Layout as AntLayout } from 'antd'
2
+ import { Layout as AntLayout, theme } from 'antd'
3
3
  import { Sidebar } from './Sidebar'
4
4
  import { Header } from './Header'
5
5
 
@@ -9,6 +9,7 @@ interface LayoutProps {
9
9
 
10
10
  export const Layout: React.FC<LayoutProps> = ({ children }) => {
11
11
  const [collapsed, setCollapsed] = useState(false)
12
+ const { token } = theme.useToken()
12
13
 
13
14
  return (
14
15
  <AntLayout className="min-h-screen" data-testid="admin-layout">
@@ -16,8 +17,11 @@ export const Layout: React.FC<LayoutProps> = ({ children }) => {
16
17
  <AntLayout>
17
18
  <Header collapsed={collapsed} onToggle={() => setCollapsed(!collapsed)} />
18
19
  <AntLayout.Content
19
- className="m-6 p-6 bg-white rounded-lg shadow-sm"
20
- style={{ minHeight: 'calc(100vh - 64px - 48px)' }}
20
+ className="m-6 p-6 rounded-lg shadow-sm"
21
+ style={{
22
+ minHeight: 'calc(100vh - 64px - 48px)',
23
+ backgroundColor: token.colorBgContainer,
24
+ }}
21
25
  data-testid="admin-main"
22
26
  >
23
27
  {children}