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
@@ -23,17 +23,19 @@ import {
23
23
  import { useRoleStore } from '../hooks/useRoles'
24
24
  import { useConfig, usePermissionCategories } from '../hooks/useConfig'
25
25
  import { usePermissions } from '../hooks/usePermissions'
26
- import type { RoleType, CreateRoleType } from '@shared/modules/role/schemas'
26
+ import type { RoleDataType, CreateRoleType } from '@shared/modules/role/schemas'
27
27
  import { PermissionConfigEditor } from '../components/PermissionConfigEditor'
28
28
  import { PermissionTree } from '../components/PermissionTree'
29
29
  import { apiClient } from '../services/apiClient'
30
30
  import { validatePermissionDependencies } from '@shared/modules/permission/permission-dependencies'
31
+ import { useLanguage } from '../i18n/useLanguage'
31
32
 
32
33
  type RoleFormValues = Pick<CreateRoleType, 'code' | 'name' | 'label' | 'description'> & {
33
34
  isActive?: boolean | null
34
35
  }
35
36
 
36
37
  export const RolesPage: React.FC = () => {
38
+ const { t } = useLanguage()
37
39
  const { roles, loading, fetchRoles, createRole, updateRole, deleteRole, updateRolePermissions } =
38
40
  useRoleStore()
39
41
  const { permissions } = useConfig()
@@ -41,7 +43,7 @@ export const RolesPage: React.FC = () => {
41
43
  const { refreshPermissions } = usePermissions()
42
44
  const [modalVisible, setModalVisible] = useState(false)
43
45
  const [permissionModalVisible, setPermissionModalVisible] = useState(false)
44
- const [editingRole, setEditingRole] = useState<RoleType | null>(null)
46
+ const [editingRole, setEditingRole] = useState<RoleDataType | null>(null)
45
47
  const [selectedPermissions, setSelectedPermissions] = useState<string[]>([])
46
48
  const [form] = Form.useForm<RoleFormValues>()
47
49
 
@@ -55,7 +57,7 @@ export const RolesPage: React.FC = () => {
55
57
  setModalVisible(true)
56
58
  }
57
59
 
58
- const handleEdit = (role: RoleType) => {
60
+ const handleEdit = (role: RoleDataType) => {
59
61
  setEditingRole(role)
60
62
  form.setFieldsValue({
61
63
  code: role.code,
@@ -70,11 +72,11 @@ export const RolesPage: React.FC = () => {
70
72
  const handleDelete = async (id: string) => {
71
73
  const success = await deleteRole(id)
72
74
  if (success) {
73
- message.success('角色删除成功')
75
+ message.success(t('roles.deleted'))
74
76
  }
75
77
  }
76
78
 
77
- const handleManagePermissions = async (role: RoleType) => {
79
+ const handleManagePermissions = async (role: RoleDataType) => {
78
80
  setEditingRole(role)
79
81
 
80
82
  try {
@@ -103,13 +105,13 @@ export const RolesPage: React.FC = () => {
103
105
  if (editingRole) {
104
106
  const success = await updateRole(editingRole.id, values)
105
107
  if (success) {
106
- message.success('角色更新成功')
108
+ message.success(t('roles.updated'))
107
109
  setModalVisible(false)
108
110
  }
109
111
  } else {
110
112
  const success = await createRole(values)
111
113
  if (success) {
112
- message.success('角色创建成功')
114
+ message.success(t('roles.created'))
113
115
  setModalVisible(false)
114
116
  }
115
117
  }
@@ -124,10 +126,10 @@ export const RolesPage: React.FC = () => {
124
126
  const validation = validatePermissionDependencies(selectedPermissions)
125
127
  if (!validation.valid) {
126
128
  Modal.error({
127
- title: '权限依赖校验失败',
129
+ title: t('roles.permissionValidationFailed'),
128
130
  content: (
129
131
  <div>
130
- <p>以下权限配置存在问题:</p>
132
+ <p>{t('roles.permissionValidationErrors')}</p>
131
133
  <ul>
132
134
  {validation.errors.map((error, index) => (
133
135
  <li key={index}>{error}</li>
@@ -141,7 +143,7 @@ export const RolesPage: React.FC = () => {
141
143
 
142
144
  const success = await updateRolePermissions(editingRole.id, selectedPermissions)
143
145
  if (success) {
144
- message.success('权限更新成功')
146
+ message.success(t('roles.permissionUpdated'))
145
147
  setPermissionModalVisible(false)
146
148
  await refreshPermissions()
147
149
  }
@@ -149,45 +151,45 @@ export const RolesPage: React.FC = () => {
149
151
 
150
152
  const columns = [
151
153
  {
152
- title: '角色代码',
154
+ title: t('roles.roleCode'),
153
155
  dataIndex: 'code',
154
156
  key: 'code',
155
157
  },
156
158
  {
157
- title: '角色名称',
159
+ title: t('roles.roleName'),
158
160
  dataIndex: 'name',
159
161
  key: 'name',
160
162
  },
161
163
  {
162
- title: '显示名称',
164
+ title: t('roles.displayName'),
163
165
  dataIndex: 'label',
164
166
  key: 'label',
165
167
  },
166
168
  {
167
- title: '描述',
169
+ title: t('roles.description'),
168
170
  dataIndex: 'description',
169
171
  key: 'description',
170
172
  },
171
173
  {
172
- title: '系统角色',
174
+ title: t('roles.isSystem'),
173
175
  dataIndex: 'isSystem',
174
176
  key: 'isSystem',
175
177
  render: (isSystem: boolean) => (
176
- <Tag color={isSystem ? 'blue' : 'default'}>{isSystem ? '' : ''}</Tag>
178
+ <Tag color={isSystem ? 'blue' : 'default'}>{isSystem ? t('common.yes') : t('common.no')}</Tag>
177
179
  ),
178
180
  },
179
181
  {
180
- title: '状态',
182
+ title: t('common.status'),
181
183
  dataIndex: 'isActive',
182
184
  key: 'isActive',
183
185
  render: (isActive: boolean) => (
184
- <Tag color={isActive ? 'green' : 'red'}>{isActive ? '启用' : '禁用'}</Tag>
186
+ <Tag color={isActive ? 'green' : 'red'}>{isActive ? t('common.enabled') : t('common.disabled')}</Tag>
185
187
  ),
186
188
  },
187
189
  {
188
- title: '操作',
190
+ title: t('common.actions'),
189
191
  key: 'action',
190
- render: (_: unknown, record: RoleType) => (
192
+ render: (_: unknown, record: RoleDataType) => (
191
193
  <Space>
192
194
  {record.code !== 'super_admin' && (
193
195
  <Button
@@ -195,21 +197,21 @@ export const RolesPage: React.FC = () => {
195
197
  icon={<KeyOutlined />}
196
198
  onClick={() => handleManagePermissions(record)}
197
199
  >
198
- 权限
200
+ {t('roles.permissions')}
199
201
  </Button>
200
202
  )}
201
203
  <Button type="link" icon={<EditOutlined />} onClick={() => handleEdit(record)}>
202
- 编辑
204
+ {t('common.edit')}
203
205
  </Button>
204
206
  {!record.isSystem && (
205
207
  <Popconfirm
206
- title="确定要删除这个角色吗?"
208
+ title={t('roles.deleteConfirm')}
207
209
  onConfirm={() => handleDelete(record.id)}
208
- okText="确定"
209
- cancelText="取消"
210
+ okText={t('common.confirm')}
211
+ cancelText={t('common.cancel')}
210
212
  >
211
213
  <Button type="link" danger icon={<DeleteOutlined />}>
212
- 删除
214
+ {t('common.delete')}
213
215
  </Button>
214
216
  </Popconfirm>
215
217
  )}
@@ -221,67 +223,67 @@ export const RolesPage: React.FC = () => {
221
223
  return (
222
224
  <div style={{ padding: '24px' }}>
223
225
  <div style={{ marginBottom: '16px', display: 'flex', justifyContent: 'space-between' }}>
224
- <h1>角色管理</h1>
226
+ <h1>{t('roles.title')}</h1>
225
227
  <Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}>
226
- 创建角色
228
+ {t('roles.createRole')}
227
229
  </Button>
228
230
  </div>
229
231
 
230
232
  <Table columns={columns} dataSource={roles} rowKey="id" loading={loading} />
231
233
 
232
234
  <Modal
233
- title={editingRole ? '编辑角色' : '创建角色'}
235
+ title={editingRole ? t('roles.editRole') : t('roles.createRole')}
234
236
  open={modalVisible}
235
237
  onOk={handleSubmit}
236
238
  onCancel={() => setModalVisible(false)}
237
- okText="确定"
238
- cancelText="取消"
239
+ okText={t('common.confirm')}
240
+ cancelText={t('common.cancel')}
239
241
  >
240
242
  <Form form={form} layout="vertical">
241
243
  <Form.Item
242
244
  name="code"
243
- label="角色代码"
244
- rules={[{ required: true, message: '请输入角色代码' }]}
245
+ label={t('roles.roleCode')}
246
+ rules={[{ required: true, message: t('roles.roleCodeRequired') }]}
245
247
  >
246
- <Input disabled={!!editingRole} placeholder="请输入角色代码,如:manager" />
248
+ <Input disabled={!!editingRole} placeholder={t('roles.roleCodePlaceholder')} />
247
249
  </Form.Item>
248
250
 
249
251
  <Form.Item
250
252
  name="name"
251
- label="角色名称"
252
- rules={[{ required: true, message: '请输入角色名称' }]}
253
+ label={t('roles.roleName')}
254
+ rules={[{ required: true, message: t('roles.roleNameRequired') }]}
253
255
  >
254
- <Input placeholder="请输入角色名称" />
256
+ <Input placeholder={t('roles.roleNamePlaceholder')} />
255
257
  </Form.Item>
256
258
 
257
259
  <Form.Item
258
260
  name="label"
259
- label="显示名称"
260
- rules={[{ required: true, message: '请输入显示名称' }]}
261
+ label={t('roles.displayName')}
262
+ rules={[{ required: true, message: t('roles.displayNameRequired') }]}
261
263
  >
262
- <Input placeholder="请输入显示名称" />
264
+ <Input placeholder={t('roles.displayNamePlaceholder')} />
263
265
  </Form.Item>
264
266
 
265
- <Form.Item name="description" label="描述">
266
- <Input.TextArea placeholder="请输入角色描述" />
267
+ <Form.Item name="description" label={t('roles.description')}>
268
+ <Input.TextArea placeholder={t('roles.descriptionPlaceholder')} />
267
269
  </Form.Item>
268
270
 
269
271
  {editingRole && (
270
- <Form.Item name="isActive" label="状态" valuePropName="checked">
271
- <Switch checkedChildren="启用" unCheckedChildren="禁用" />
272
+ <Form.Item name="isActive" label={t('common.status')} valuePropName="checked">
273
+ <Switch checkedChildren={t('common.enabled')} unCheckedChildren={t('common.disabled')} />
272
274
  </Form.Item>
273
275
  )}
274
276
  </Form>
275
277
  </Modal>
276
278
 
277
279
  <Modal
278
- title={`管理角色权限 - ${editingRole?.label || ''}`}
280
+ title={t('roles.managePermissions', { name: editingRole?.label || '' })}
279
281
  open={permissionModalVisible}
280
282
  onOk={handlePermissionSubmit}
281
283
  onCancel={() => setPermissionModalVisible(false)}
282
284
  width={900}
283
- okText="保存"
284
- cancelText="取消"
285
+ okText={t('common.save')}
286
+ cancelText={t('common.cancel')}
285
287
  >
286
288
  <Tabs
287
289
  defaultActiveKey="tree"
@@ -291,7 +293,7 @@ export const RolesPage: React.FC = () => {
291
293
  label: (
292
294
  <span>
293
295
  <ApartmentOutlined />
294
- 树状选择
296
+ {t('roles.treeView')}
295
297
  </span>
296
298
  ),
297
299
  children: (
@@ -308,7 +310,7 @@ export const RolesPage: React.FC = () => {
308
310
  label: (
309
311
  <span>
310
312
  <CodeOutlined />
311
- JSON编辑
313
+ {t('roles.jsonView')}
312
314
  </span>
313
315
  ),
314
316
  children: (
@@ -1,4 +1,5 @@
1
1
  import { Card, Form, Input, Button, Switch, Divider, message } from 'antd'
2
+ import { useLanguage } from '../i18n/useLanguage'
2
3
 
3
4
  interface SettingsFormValues {
4
5
  siteName?: string
@@ -9,62 +10,61 @@ interface SettingsFormValues {
9
10
  }
10
11
 
11
12
  export const SettingsPage: React.FC = () => {
13
+ const { t } = useLanguage()
12
14
  const [form] = Form.useForm<SettingsFormValues>()
13
15
 
14
16
  const handleSave = () => {
15
- message.success('Settings saved successfully!')
17
+ message.success(t('settings.saved'))
16
18
  }
17
19
 
18
20
  return (
19
21
  <div>
20
- <h1 className="text-2xl font-bold text-gray-900 mb-6">Settings</h1>
22
+ <h1 className="text-2xl font-bold text-gray-900 mb-6">{t('settings.title')}</h1>
21
23
 
22
- <Card title="General Settings" className="mb-6">
24
+ <Card title={t('settings.general')} className="mb-6">
23
25
  <Form form={form} layout="vertical">
24
- <Form.Item label="Site Name" name="siteName">
25
- <Input placeholder="Enter site name" />
26
+ <Form.Item label={t('settings.siteName')} name="siteName">
27
+ <Input placeholder={t('settings.siteNamePlaceholder')} />
26
28
  </Form.Item>
27
- <Form.Item label="Site Description" name="siteDescription">
28
- <Input.TextArea rows={4} placeholder="Enter site description" />
29
+ <Form.Item label={t('settings.siteDescription')} name="siteDescription">
30
+ <Input.TextArea rows={4} placeholder={t('settings.siteDescriptionPlaceholder')} />
29
31
  </Form.Item>
30
32
  </Form>
31
33
  </Card>
32
34
 
33
- <Card title="Notification Settings" className="mb-6">
35
+ <Card title={t('settings.notification')} className="mb-6">
34
36
  <div className="space-y-4">
35
37
  <div className="flex items-center justify-between">
36
38
  <div>
37
- <p className="font-medium">Email Notifications</p>
38
- <p className="text-sm text-gray-500">
39
- Receive email notifications for important updates
40
- </p>
39
+ <p className="font-medium">{t('settings.emailNotifications')}</p>
40
+ <p className="text-sm text-gray-500">{t('settings.emailNotificationsDesc')}</p>
41
41
  </div>
42
42
  <Switch defaultChecked />
43
43
  </div>
44
44
  <Divider />
45
45
  <div className="flex items-center justify-between">
46
46
  <div>
47
- <p className="font-medium">Push Notifications</p>
48
- <p className="text-sm text-gray-500">Receive push notifications in browser</p>
47
+ <p className="font-medium">{t('settings.pushNotifications')}</p>
48
+ <p className="text-sm text-gray-500">{t('settings.pushNotificationsDesc')}</p>
49
49
  </div>
50
50
  <Switch />
51
51
  </div>
52
52
  </div>
53
53
  </Card>
54
54
 
55
- <Card title="Security Settings">
55
+ <Card title={t('settings.security')}>
56
56
  <Form form={form} layout="vertical">
57
- <Form.Item label="Current Password" name="currentPassword">
58
- <Input.Password placeholder="Enter current password" />
57
+ <Form.Item label={t('settings.currentPassword')} name="currentPassword">
58
+ <Input.Password placeholder={t('settings.currentPasswordPlaceholder')} />
59
59
  </Form.Item>
60
- <Form.Item label="New Password" name="newPassword">
61
- <Input.Password placeholder="Enter new password" />
60
+ <Form.Item label={t('settings.newPassword')} name="newPassword">
61
+ <Input.Password placeholder={t('settings.newPasswordPlaceholder')} />
62
62
  </Form.Item>
63
- <Form.Item label="Confirm New Password" name="confirmPassword">
64
- <Input.Password placeholder="Confirm new password" />
63
+ <Form.Item label={t('settings.confirmNewPassword')} name="confirmPassword">
64
+ <Input.Password placeholder={t('settings.confirmNewPasswordPlaceholder')} />
65
65
  </Form.Item>
66
66
  <Button type="primary" onClick={handleSave}>
67
- Save Changes
67
+ {t('settings.saveChanges')}
68
68
  </Button>
69
69
  </Form>
70
70
  </Card>
@@ -12,10 +12,12 @@ import {
12
12
  type ResourceType,
13
13
  type ActionType,
14
14
  } from '@shared/constants'
15
+ import { useLanguage } from '../i18n/useLanguage'
15
16
 
16
17
  const { Option } = Select
17
18
 
18
19
  export const SystemLogsPage: React.FC = () => {
20
+ const { t, formatDate } = useLanguage()
19
21
  const { logs, loading, fetchLogs } = useAuditLogStore()
20
22
  const [selectedLog, setSelectedLog] = useState<AuditLogType | null>(null)
21
23
  const [detailModalVisible, setDetailModalVisible] = useState(false)
@@ -62,20 +64,20 @@ export const SystemLogsPage: React.FC = () => {
62
64
 
63
65
  const columns = [
64
66
  {
65
- title: '时间',
67
+ title: t('systemLogs.time'),
66
68
  dataIndex: 'createdAt',
67
69
  key: 'createdAt',
68
70
  width: 180,
69
- render: (date: string) => new Date(date).toLocaleString('zh-CN'),
71
+ render: (date: string) => formatDate(date),
70
72
  },
71
73
  {
72
- title: '用户ID',
74
+ title: t('systemLogs.userId'),
73
75
  dataIndex: 'userId',
74
76
  key: 'userId',
75
77
  width: 120,
76
78
  },
77
79
  {
78
- title: '操作',
80
+ title: t('systemLogs.action'),
79
81
  dataIndex: 'action',
80
82
  key: 'action',
81
83
  width: 100,
@@ -84,33 +86,33 @@ export const SystemLogsPage: React.FC = () => {
84
86
  ),
85
87
  },
86
88
  {
87
- title: '资源类型',
89
+ title: t('systemLogs.resourceType'),
88
90
  dataIndex: 'resourceType',
89
91
  key: 'resourceType',
90
92
  width: 120,
91
93
  render: (type: ResourceType) => RESOURCE_LABELS[type] || type,
92
94
  },
93
95
  {
94
- title: '资源ID',
96
+ title: t('systemLogs.resourceId'),
95
97
  dataIndex: 'resourceId',
96
98
  key: 'resourceId',
97
99
  width: 150,
98
100
  render: (id: string | null) => id || '-',
99
101
  },
100
102
  {
101
- title: 'IP地址',
103
+ title: t('systemLogs.ipAddress'),
102
104
  dataIndex: 'ipAddress',
103
105
  key: 'ipAddress',
104
106
  width: 140,
105
107
  render: (ip: string | null) => ip || '-',
106
108
  },
107
109
  {
108
- title: '操作',
109
- key: 'action',
110
+ title: t('common.actions'),
111
+ key: 'actionCol',
110
112
  width: 100,
111
113
  render: (_: unknown, record: AuditLogType) => (
112
114
  <Button type="link" icon={<EyeOutlined />} onClick={() => handleViewDetail(record)}>
113
- 详情
115
+ {t('common.detail')}
114
116
  </Button>
115
117
  ),
116
118
  },
@@ -119,22 +121,22 @@ export const SystemLogsPage: React.FC = () => {
119
121
  return (
120
122
  <div style={{ padding: '24px' }}>
121
123
  <Card
122
- title="系统日志"
124
+ title={t('systemLogs.title')}
123
125
  extra={
124
126
  <Button icon={<ReloadOutlined />} onClick={() => fetchLogs()}>
125
- 刷新
127
+ {t('common.refresh')}
126
128
  </Button>
127
129
  }
128
130
  >
129
131
  <Space style={{ marginBottom: '16px' }} wrap>
130
132
  <Input
131
- placeholder="用户ID"
133
+ placeholder={t('systemLogs.userId')}
132
134
  value={filters.userId}
133
135
  onChange={e => setFilters({ ...filters, userId: e.target.value })}
134
136
  style={{ width: 150 }}
135
137
  />
136
138
  <Select
137
- placeholder="操作类型"
139
+ placeholder={t('systemLogs.actionType')}
138
140
  value={filters.action || undefined}
139
141
  onChange={value => setFilters({ ...filters, action: (value || '') as ActionType | '' })}
140
142
  style={{ width: 120 }}
@@ -147,7 +149,7 @@ export const SystemLogsPage: React.FC = () => {
147
149
  ))}
148
150
  </Select>
149
151
  <Select
150
- placeholder="资源类型"
152
+ placeholder={t('systemLogs.resourceType')}
151
153
  value={filters.resourceType || undefined}
152
154
  onChange={value =>
153
155
  setFilters({ ...filters, resourceType: (value || '') as ResourceType | '' })
@@ -162,9 +164,9 @@ export const SystemLogsPage: React.FC = () => {
162
164
  ))}
163
165
  </Select>
164
166
  <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
165
- 搜索
167
+ {t('common.search')}
166
168
  </Button>
167
- <Button onClick={handleReset}>重置</Button>
169
+ <Button onClick={handleReset}>{t('common.reset')}</Button>
168
170
  </Space>
169
171
 
170
172
  <Table
@@ -175,13 +177,13 @@ export const SystemLogsPage: React.FC = () => {
175
177
  pagination={{
176
178
  pageSize: 20,
177
179
  showSizeChanger: true,
178
- showTotal: total => `共 ${total} 条记录`,
180
+ showTotal: total => t('common.totalRecords', { count: total }),
179
181
  }}
180
182
  />
181
183
  </Card>
182
184
 
183
185
  <Modal
184
- title="日志详情"
186
+ title={t('systemLogs.detail')}
185
187
  open={detailModalVisible}
186
188
  onCancel={() => setDetailModalVisible(false)}
187
189
  footer={null}
@@ -189,31 +191,31 @@ export const SystemLogsPage: React.FC = () => {
189
191
  >
190
192
  {selectedLog && (
191
193
  <Descriptions column={1} bordered>
192
- <Descriptions.Item label="日志ID">{selectedLog.id}</Descriptions.Item>
193
- <Descriptions.Item label="用户ID">{selectedLog.userId}</Descriptions.Item>
194
- <Descriptions.Item label="操作">
194
+ <Descriptions.Item label={t('systemLogs.logId')}>{selectedLog.id}</Descriptions.Item>
195
+ <Descriptions.Item label={t('systemLogs.userId')}>{selectedLog.userId}</Descriptions.Item>
196
+ <Descriptions.Item label={t('systemLogs.action')}>
195
197
  <Tag color={ACTION_COLORS[selectedLog.action] || 'default'}>
196
198
  {ACTION_LABELS[selectedLog.action] || selectedLog.action}
197
199
  </Tag>
198
200
  </Descriptions.Item>
199
- <Descriptions.Item label="资源类型">
201
+ <Descriptions.Item label={t('systemLogs.resourceType')}>
200
202
  {RESOURCE_LABELS[selectedLog.resourceType] || selectedLog.resourceType}
201
203
  </Descriptions.Item>
202
- <Descriptions.Item label="资源ID">{selectedLog.resourceId || '-'}</Descriptions.Item>
203
- <Descriptions.Item label="IP地址">{selectedLog.ipAddress || '-'}</Descriptions.Item>
204
- <Descriptions.Item label="User Agent">{selectedLog.userAgent || '-'}</Descriptions.Item>
205
- <Descriptions.Item label="时间">
206
- {new Date(selectedLog.createdAt).toLocaleString('zh-CN')}
204
+ <Descriptions.Item label={t('systemLogs.resourceId')}>{selectedLog.resourceId || '-'}</Descriptions.Item>
205
+ <Descriptions.Item label={t('systemLogs.ipAddress')}>{selectedLog.ipAddress || '-'}</Descriptions.Item>
206
+ <Descriptions.Item label={t('systemLogs.userAgent')}>{selectedLog.userAgent || '-'}</Descriptions.Item>
207
+ <Descriptions.Item label={t('systemLogs.time')}>
208
+ {formatDate(selectedLog.createdAt)}
207
209
  </Descriptions.Item>
208
210
  {selectedLog.oldValue && (
209
- <Descriptions.Item label="旧值">
211
+ <Descriptions.Item label={t('systemLogs.oldValue')}>
210
212
  <pre style={{ margin: 0, maxHeight: '200px', overflow: 'auto' }}>
211
213
  {safeFormatJson(selectedLog.oldValue)}
212
214
  </pre>
213
215
  </Descriptions.Item>
214
216
  )}
215
217
  {selectedLog.newValue && (
216
- <Descriptions.Item label="新值">
218
+ <Descriptions.Item label={t('systemLogs.newValue')}>
217
219
  <pre style={{ margin: 0, maxHeight: '200px', overflow: 'auto' }}>
218
220
  {safeFormatJson(selectedLog.newValue)}
219
221
  </pre>