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.
- package/dist/cli/index.js +499 -100
- package/dist/cli/index.js.map +1 -1
- package/package.json +2 -2
- package/template/CLAUDE.md +18 -6
- package/template/drizzle/0004_add_merchants_products.sql +30 -0
- package/template/drizzle/meta/_journal.json +7 -0
- package/template/eslint-rules/module-boundary.js +6 -2
- package/template/eslint-rules/no-cross-module-service-import.js +105 -0
- package/template/eslint-rules/no-disable-type-safe-client.js +5 -0
- package/template/eslint.config.js +9 -0
- package/template/lint-scripts/config/project.config.ts +21 -0
- package/template/lint-scripts/validate-all.ts +48 -12
- package/template/lint-scripts/validators/index.ts +29 -0
- package/template/lint-scripts/validators/module-public-api.validator.ts +103 -0
- package/template/lint-scripts/validators/schema-uniqueness.validator.ts +147 -0
- package/template/modules.config.ts +2 -0
- package/template/package.json +2 -0
- package/template/playwright.config.ts +14 -0
- package/template/src/admin/App.tsx +40 -3
- package/template/src/admin/components/LanguageSwitcher.tsx +22 -0
- package/template/src/admin/components/NotificationDrawer.tsx +108 -38
- package/template/src/admin/components/StatsCard.tsx +3 -1
- package/template/src/admin/components/ThemeToggle.tsx +28 -0
- package/template/src/admin/hooks/useRoles.ts +2 -2
- package/template/src/admin/i18n/index.ts +18 -0
- package/template/src/admin/i18n/locales/en-US.json +381 -0
- package/template/src/admin/i18n/locales/zh-CN.json +381 -0
- package/template/src/admin/i18n/useLanguage.ts +21 -0
- package/template/src/admin/layouts/Header.tsx +37 -16
- package/template/src/admin/layouts/Layout.tsx +7 -3
- package/template/src/admin/layouts/Sidebar.tsx +70 -68
- package/template/src/admin/main.tsx +1 -0
- package/template/src/admin/pages/ContentPage.tsx +68 -56
- package/template/src/admin/pages/DashboardPage.tsx +82 -76
- package/template/src/admin/pages/DisputesPage.tsx +61 -53
- package/template/src/admin/pages/LoginPage.tsx +41 -33
- package/template/src/admin/pages/OrdersPage.tsx +69 -64
- package/template/src/admin/pages/PermissionsPage.tsx +10 -8
- package/template/src/admin/pages/PluginDashboardPage.tsx +3 -1
- package/template/src/admin/pages/PluginManagementPage.tsx +3 -1
- package/template/src/admin/pages/RegisterPage.tsx +18 -16
- package/template/src/admin/pages/RolesPage.tsx +51 -49
- package/template/src/admin/pages/SettingsPage.tsx +22 -22
- package/template/src/admin/pages/SystemLogsPage.tsx +32 -30
- package/template/src/admin/pages/TicketsPage.tsx +67 -59
- package/template/src/admin/pages/UsersPage.tsx +63 -53
- package/template/src/admin/pages/__tests__/RolesPage.test.tsx +2 -2
- package/template/src/admin/stores/themeStore.ts +32 -0
- package/template/src/client/index.css +73 -0
- package/template/src/merchant/App.tsx +2 -0
- package/template/src/merchant/components/MerchantGuard.tsx +2 -6
- package/template/src/merchant/components/__tests__/MerchantGuard.test.tsx +117 -0
- package/template/src/merchant/layouts/Header.tsx +2 -2
- package/template/src/merchant/pages/DashboardPage.tsx +2 -2
- package/template/src/merchant/pages/DisputesPage.tsx +14 -10
- package/template/src/merchant/pages/LoginPage.tsx +49 -0
- package/template/src/merchant/pages/OrdersPage.tsx +17 -10
- package/template/src/merchant/pages/ProductsPage.tsx +31 -8
- package/template/src/merchant/pages/SettingsPage.tsx +3 -7
- package/template/src/merchant/stores/__tests__/merchantStore.test.ts +317 -0
- package/template/src/merchant/stores/merchantStore.ts +24 -27
- package/template/src/server/db/schema/index.ts +2 -0
- package/template/src/server/db/schema/merchants.ts +26 -0
- package/template/src/server/db/schema/products.ts +25 -0
- package/template/src/server/db/test-setup.ts +237 -0
- package/template/src/server/middleware/__tests__/audit-log.test.ts +144 -0
- package/template/src/server/middleware/__tests__/cors.test.ts +84 -0
- package/template/src/server/middleware/__tests__/logger.test.ts +117 -0
- package/template/src/server/middleware/__tests__/rate-limit.test.ts +73 -0
- package/template/src/server/middleware/__tests__/realtime-env.test.ts +56 -0
- package/template/src/server/middleware/__tests__/tenant-isolation.test.ts +150 -0
- package/template/src/server/module-admin/__tests__/admin-routes.test.ts +1 -1
- package/template/src/server/module-admin/module.ts +4 -0
- package/template/src/server/module-admin/routes/admin-notification-routes.ts +3 -3
- package/template/src/server/module-admin/routes/user-management-routes.ts +2 -2
- package/template/src/server/module-auth/__tests__/auth-routes.test.ts +315 -0
- package/template/src/server/module-auth/__tests__/profile-routes.test.ts +83 -0
- package/template/src/server/module-auth/module.ts +2 -0
- package/template/src/server/module-content/module.ts +1 -0
- package/template/src/server/module-content/routes/content-routes.ts +2 -2
- package/template/src/server/module-dispute/module.ts +1 -0
- package/template/src/server/module-dispute/routes/dispute-routes.ts +2 -2
- package/template/src/server/module-merchant/__tests__/merchant-routes.test.ts +218 -0
- package/template/src/server/module-merchant/__tests__/merchant-service.test.ts +183 -0
- package/template/src/server/module-merchant/index.ts +10 -0
- package/template/src/server/module-merchant/module.ts +33 -0
- package/template/src/server/module-merchant/routes/merchant-routes.ts +138 -0
- package/template/src/server/module-merchant/services/merchant-service.ts +292 -0
- package/template/src/server/module-notifications/index.ts +18 -0
- package/template/src/server/module-notifications/module.ts +2 -0
- package/template/src/server/module-order/module.ts +1 -0
- package/template/src/server/module-order/routes/order-routes.ts +2 -2
- package/template/src/server/module-permission/routes/role-routes.ts +3 -3
- package/template/src/server/module-plugin/__tests__/admin-category-service.test.ts +181 -0
- package/template/src/server/module-plugin/__tests__/admin-plugin-service.test.ts +277 -0
- package/template/src/server/module-plugin/__tests__/admin-stats-service.test.ts +170 -0
- package/template/src/server/module-plugin/__tests__/plugin-review-service.test.ts +260 -0
- package/template/src/server/module-plugin/__tests__/plugin-seed-service.test.ts +170 -0
- package/template/src/server/module-plugin/module.ts +3 -0
- package/template/src/server/module-tenant/__tests__/tenant-routes.test.ts +142 -0
- package/template/src/server/module-tenant/__tests__/tenant-service.test.ts +152 -0
- package/template/src/server/module-tenant/module.ts +1 -0
- package/template/src/server/module-tenant/services/tenant-service.ts +1 -37
- package/template/src/server/module-ticket/module.ts +1 -0
- package/template/src/server/module-ticket/routes/ticket-routes.ts +2 -2
- package/template/src/server/module-todos/module.ts +3 -0
- package/template/src/server/route-registry.ts +4 -0
- package/template/src/server/utils/__tests__/date.test.ts +130 -0
- package/template/src/server/utils/__tests__/env.test.ts +25 -0
- package/template/src/server/utils/__tests__/generate.test.ts +82 -0
- package/template/src/server/utils/__tests__/id-helpers.test.ts +29 -0
- package/template/src/server/utils/__tests__/json.test.ts +49 -0
- package/template/src/server/utils/__tests__/permission-utils.test.ts +26 -0
- package/template/src/server/utils/__tests__/uuid.test.ts +25 -0
- package/template/src/shared/core/module-manifest.ts +15 -0
- package/template/src/shared/modules/admin/schemas.ts +1 -1
- package/template/src/shared/modules/content/schemas.ts +2 -2
- package/template/src/shared/modules/dispute/schemas.ts +2 -2
- package/template/src/shared/modules/index.ts +181 -1
- package/template/src/shared/modules/merchant/index.ts +12 -0
- package/template/src/shared/modules/merchant/schemas.ts +63 -0
- package/template/src/shared/modules/order/schemas.ts +2 -2
- package/template/src/shared/modules/permission/index.ts +1 -1
- package/template/src/shared/modules/permission/schemas.ts +1 -1
- package/template/src/shared/modules/role/schemas.ts +2 -2
- package/template/src/shared/modules/ticket/schemas.ts +2 -2
- 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 {
|
|
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: '
|
|
49
|
+
label: t('header.profile'),
|
|
45
50
|
icon: <User className="w-4 h-4" />,
|
|
46
51
|
},
|
|
47
52
|
{
|
|
48
53
|
key: 'settings',
|
|
49
|
-
label: '
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
20
|
-
style={{
|
|
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}
|