zant-admin 2.0.2 → 2.0.4

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 (69) hide show
  1. package/.editorconfig +6 -0
  2. package/.env.development +3 -0
  3. package/.env.production +1 -0
  4. package/.env.test +1 -0
  5. package/.gitignore +36 -0
  6. package/.prettierrc.json +9 -0
  7. package/README.en.md +461 -272
  8. package/README.md +4 -3
  9. package/bin/cli.js +1 -1
  10. package/eslint.config.js +30 -0
  11. package/index.html +13 -0
  12. package/jsconfig.json +8 -0
  13. package/package.json +11 -3
  14. package/src/App.vue +16 -16
  15. package/src/api/methods/logError.js +8 -8
  16. package/src/api/methods/logOperation.js +8 -8
  17. package/src/api/methods/login.js +6 -6
  18. package/src/api/methods/quartz.js +36 -36
  19. package/src/api/methods/region.js +16 -16
  20. package/src/api/methods/sysAccount.js +29 -29
  21. package/src/api/methods/sysDict.js +29 -29
  22. package/src/api/methods/sysDictItem.js +26 -26
  23. package/src/api/methods/sysMenu.js +42 -42
  24. package/src/api/methods/sysRole.js +35 -35
  25. package/src/api/methods/sysUser.js +25 -25
  26. package/src/api/methods/system.js +15 -15
  27. package/src/api/request.js +225 -225
  28. package/src/assets/css/zcui.css +1023 -1023
  29. package/src/components/IconPicker.vue +351 -351
  30. package/src/components/MainPage.vue +838 -838
  31. package/src/components/details/logErrorDetails.vue +58 -58
  32. package/src/components/details/logOperationDetails.vue +76 -76
  33. package/src/components/edit/QuartzEdit.vue +221 -221
  34. package/src/components/edit/SysAccountEdit.vue +185 -185
  35. package/src/components/edit/SysDictEdit.vue +116 -116
  36. package/src/components/edit/SysDictItemEdit.vue +136 -136
  37. package/src/components/edit/SysRoleEdit.vue +111 -111
  38. package/src/config/index.js +74 -74
  39. package/src/directives/permission.js +49 -49
  40. package/src/main.js +37 -37
  41. package/src/stores/config.js +43 -43
  42. package/src/stores/dict.js +33 -33
  43. package/src/stores/menu.js +81 -81
  44. package/src/stores/user.js +21 -21
  45. package/src/utils/baseEcharts.js +661 -661
  46. package/src/utils/dictTemplate.js +26 -26
  47. package/src/utils/regionUtils.js +173 -173
  48. package/src/utils/useFormCRUD.js +59 -59
  49. package/src/views/baiscstatis/center.vue +474 -474
  50. package/src/views/baiscstatis/iframePage.vue +29 -29
  51. package/src/views/baiscstatis/notFound.vue +192 -192
  52. package/src/views/console.vue +821 -821
  53. package/src/views/demo/button.vue +269 -269
  54. package/src/views/demo/importexport.vue +119 -119
  55. package/src/views/demo/region.vue +322 -322
  56. package/src/views/demo/statistics.vue +214 -214
  57. package/src/views/home.vue +6 -6
  58. package/src/views/operations/log/logError.vue +78 -78
  59. package/src/views/operations/log/logLogin.vue +66 -66
  60. package/src/views/operations/log/logOperation.vue +103 -103
  61. package/src/views/operations/log/logQuartz.vue +56 -56
  62. package/src/views/operations/quartz.vue +179 -179
  63. package/src/views/operations/serviceMonitoring.vue +134 -134
  64. package/src/views/system/sysAccount.vue +128 -128
  65. package/src/views/system/sysDict.vue +159 -159
  66. package/src/views/system/sysDictItem.vue +118 -118
  67. package/src/views/system/sysMenu.vue +225 -225
  68. package/src/views/system/sysRole.vue +207 -207
  69. package/vite.config.js +33 -0
@@ -1,474 +1,474 @@
1
- <template>
2
- <div class="user-center">
3
- <a-card title="个人中心" :bordered="false" class="user-center-card">
4
- <a-row :gutter="32">
5
- <!-- 左侧用户信息 -->
6
- <a-col :span="6">
7
- <div class="user-info">
8
- <div class="avatar-section">
9
- <a-avatar :size="80" :src="userInfo.avatar" class="user-avatar">
10
- {{ userInfo.username?.charAt(0) || 'U' }}
11
- </a-avatar>
12
- <div class="user-basic-info">
13
- <h3>{{ userInfo.username || '用户' }}</h3>
14
- <p class="user-role">{{ userInfo.roleName || '普通用户' }}</p>
15
- </div>
16
- </div>
17
- </div>
18
- </a-col>
19
-
20
- <!-- 右侧功能区域 -->
21
- <a-col :span="18">
22
- <a-tabs v-model:activeKey="activeTab" type="card">
23
- <!-- 基本信息 -->
24
- <a-tab-pane key="basic" tab="基本信息">
25
- <div class="basic-info-container">
26
- <a-descriptions title="用户信息" bordered :column="2">
27
- <a-descriptions-item label="账号名称">
28
- {{ userInfo.username || '未设置' }}
29
- </a-descriptions-item>
30
- <a-descriptions-item label="手机号">
31
- {{ userInfo.phone || '未设置' }}
32
- </a-descriptions-item>
33
- <a-descriptions-item label="邮箱">
34
- {{ userInfo.email || '未设置' }}
35
- </a-descriptions-item>
36
- <a-descriptions-item label="角色">
37
- {{ userInfo.roleName || '普通用户' }}
38
- </a-descriptions-item>
39
- <a-descriptions-item label="最后登录时间" :span="2">
40
- {{
41
- userInfo.lastLoginTime
42
- ? formatDate(userInfo.lastLoginTime)
43
- : '从未登录'
44
- }}
45
- </a-descriptions-item>
46
- <a-descriptions-item label="注册时间" :span="2">
47
- {{
48
- userInfo.createTime
49
- ? formatDate(userInfo.createTime)
50
- : '未知'
51
- }}
52
- </a-descriptions-item>
53
- </a-descriptions>
54
- </div>
55
- </a-tab-pane>
56
-
57
- <!-- 修改密码 -->
58
- <a-tab-pane key="password" tab="修改密码">
59
- <div class="password-form-container">
60
- <a-form
61
- :model="passwordForm"
62
- :label-col="{ span: 8 }"
63
- :wrapper-col="{ span: 12 }"
64
- layout="horizontal"
65
- :rules="passwordRules"
66
- ref="formRef"
67
- >
68
- <a-form-item label="原密码" name="oldPassword">
69
- <a-input-password
70
- v-model:value="passwordForm.oldPassword"
71
- placeholder="请输入原密码"
72
- />
73
- </a-form-item>
74
- <a-form-item label="新密码" name="newPassword">
75
- <a-input-password
76
- v-model:value="passwordForm.newPassword"
77
- placeholder="请输入新密码"
78
- />
79
- </a-form-item>
80
- <a-form-item label="确认密码" name="confirmPassword">
81
- <a-input-password
82
- v-model:value="passwordForm.confirmPassword"
83
- placeholder="请再次输入新密码"
84
- />
85
- </a-form-item>
86
- <a-form-item :wrapper-col="{ offset: 8, span: 12 }">
87
- <a-button type="primary" @click="changePassword"
88
- >修改密码</a-button
89
- >
90
- <a-button
91
- style="margin-left: 10px"
92
- @click="resetPasswordForm"
93
- >重置</a-button
94
- >
95
- </a-form-item>
96
- </a-form>
97
- </div>
98
- </a-tab-pane>
99
-
100
- <!-- 安全设置 -->
101
- <!-- <a-tab-pane key="security" tab="安全设置">
102
- <a-list item-layout="horizontal">
103
- <a-list-item>
104
- <a-list-item-meta
105
- title="登录提醒"
106
- description="开启后,当有新设备登录时会发送提醒"
107
- />
108
- <template #actions>
109
- <a-switch v-model:checked="securitySettings.loginAlert" />
110
- </template>
111
- </a-list-item>
112
- <a-list-item>
113
- <a-list-item-meta
114
- title="双重验证"
115
- description="开启双重验证,提高账户安全性"
116
- />
117
- <template #actions>
118
- <a-switch v-model:checked="securitySettings.twoFactorAuth" />
119
- </template>
120
- </a-list-item>
121
- <a-list-item>
122
- <a-list-item-meta
123
- title="会话管理"
124
- description="查看和管理当前登录的设备"
125
- />
126
- <template #actions>
127
- <a-button type="link" @click="showSessionManager">管理</a-button>
128
- </template>
129
- </a-list-item>
130
- </a-list>
131
- <div style="margin-top: 20px; text-align: center;">
132
- <a-button type="primary" @click="saveSecuritySettings">保存设置</a-button>
133
- </div>
134
- </a-tab-pane> -->
135
- </a-tabs>
136
- </a-col>
137
- </a-row>
138
- </a-card>
139
-
140
- <!-- 会话管理模态框 -->
141
- <a-modal
142
- v-model:visible="sessionModalVisible"
143
- title="会话管理"
144
- width="600px"
145
- :footer="null"
146
- >
147
- <a-list item-layout="horizontal" :data-source="sessions">
148
- <template #renderItem="{ item }">
149
- <a-list-item>
150
- <a-list-item-meta
151
- :title="item.device"
152
- :description="`最后活动: ${formatDate(item.lastActivity)}`"
153
- />
154
- <template #actions>
155
- <a-button type="link" danger @click="logoutSession(item.id)"
156
- >强制下线</a-button
157
- >
158
- </template>
159
- </a-list-item>
160
- </template>
161
- </a-list>
162
- </a-modal>
163
- </div>
164
- </template>
165
-
166
- <script setup>
167
- import { ref, reactive, onMounted } from 'vue'
168
- import { message } from 'ant-design-vue'
169
- import { useUserStore } from '@/stores/user'
170
- import sysAccount from '@/api/methods/sysAccount'
171
-
172
- // 用户存储
173
- const userStore = useUserStore()
174
-
175
- // 响应式数据
176
- const activeTab = ref('basic')
177
- const sessionModalVisible = ref(false)
178
- const formRef = ref(null)
179
- // 用户信息
180
- const userInfo = reactive({
181
- username: '',
182
- email: '',
183
- phone: '',
184
- position: '',
185
- roleName: '',
186
- avatar: '',
187
- lastLoginTime: null,
188
- createTime: null,
189
- })
190
-
191
- // 密码表单
192
- const passwordForm = reactive({
193
- oldPassword: '',
194
- newPassword: '',
195
- confirmPassword: '',
196
- })
197
-
198
- // 安全设置
199
- const securitySettings = reactive({
200
- loginAlert: false,
201
- twoFactorAuth: false,
202
- })
203
-
204
- // 会话列表
205
- const sessions = ref([])
206
-
207
- // 密码验证规则
208
- const passwordRules = {
209
- oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
210
- newPassword: [
211
- { required: true, message: '请输入新密码', trigger: 'blur' },
212
- // { min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
213
- ],
214
- confirmPassword: [
215
- { required: true, message: '请确认新密码', trigger: 'blur' },
216
- {
217
- validator: (rule, value) => {
218
- if (value !== passwordForm.newPassword) {
219
- return Promise.reject('两次输入的密码不一致')
220
- }
221
- return Promise.resolve()
222
- },
223
- trigger: 'blur',
224
- },
225
- ],
226
- }
227
-
228
- // 生命周期
229
- onMounted(() => {
230
- loadUserInfo()
231
- })
232
-
233
- // 加载用户信息
234
- const loadUserInfo = async () => {
235
- try {
236
- // 从用户存储获取基本信息
237
- const currentUser = userStore.userInfo
238
- if (currentUser) {
239
- Object.assign(userInfo, {
240
- username: currentUser.name || '',
241
- email: currentUser.email || '',
242
- phone: currentUser.mobile || '',
243
- position: currentUser.position || '',
244
- roleName: currentUser.roleId === 1 ? '管理员' : '普通用户',
245
- avatar: currentUser.profilePicture || '',
246
- lastLoginTime: currentUser.lastLoginTime || null,
247
- createTime: currentUser.createTime || null,
248
- })
249
- }
250
-
251
- // 这里可以添加API调用获取更详细的用户信息
252
- // const response = await getUserDetail()
253
- // if (response.success) {
254
- // Object.assign(userInfo, response.data)
255
- // }
256
- } catch (error) {
257
- console.error('加载用户信息失败:', error)
258
- message.error('加载用户信息失败')
259
- }
260
- }
261
-
262
- // 修改密码
263
- const changePassword = async () => {
264
- try {
265
- // 验证表单
266
- await formRef.value.validate()
267
-
268
- if (passwordForm.newPassword !== passwordForm.confirmPassword) {
269
- message.error('两次输入的密码不一致')
270
- return
271
- }
272
-
273
- // 调用修改密码接口
274
- const res = await sysAccount.updatePwd({
275
- id: userStore.userInfo.id,
276
- oldPwd: passwordForm.oldPassword,
277
- newPwd: passwordForm.newPassword,
278
- })
279
-
280
- message.success('密码修改成功')
281
- resetPasswordForm()
282
- } catch (error) {
283
- console.error('修改密码失败:', error)
284
- // message.error('修改密码失败')
285
- }
286
- }
287
-
288
- // 重置密码表单
289
- const resetPasswordForm = () => {
290
- Object.assign(passwordForm, {
291
- oldPassword: '',
292
- newPassword: '',
293
- confirmPassword: '',
294
- })
295
- }
296
-
297
- // 保存安全设置
298
- const saveSecuritySettings = async () => {
299
- try {
300
- // 这里添加保存安全设置的API调用
301
- // await updateSecuritySettings(securitySettings)
302
- message.success('安全设置保存成功')
303
- } catch (error) {
304
- console.error('保存安全设置失败:', error)
305
- message.error('保存安全设置失败')
306
- }
307
- }
308
-
309
- // 显示会话管理
310
- const showSessionManager = () => {
311
- sessionModalVisible.value = true
312
- loadSessions()
313
- }
314
-
315
- // 加载会话列表
316
- const loadSessions = async () => {
317
- try {
318
- // 这里添加获取会话列表的API调用
319
- // const response = await getUserSessions()
320
- // sessions.value = response.data || []
321
-
322
- // 模拟数据
323
- sessions.value = [
324
- {
325
- id: 1,
326
- device: 'Windows Chrome',
327
- lastActivity: new Date(),
328
- location: '中国 北京',
329
- },
330
- {
331
- id: 2,
332
- device: 'Mac Safari',
333
- lastActivity: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2小时前
334
- location: '中国 上海',
335
- },
336
- ]
337
- } catch (error) {
338
- console.error('加载会话列表失败:', error)
339
- }
340
- }
341
-
342
- // 强制下线会话
343
- const logoutSession = async sessionId => {
344
- try {
345
- // 这里添加强制下线会话的API调用
346
- // await forceLogoutSession(sessionId)
347
- message.success('会话已强制下线')
348
- loadSessions()
349
- } catch (error) {
350
- console.error('强制下线失败:', error)
351
- message.error('强制下线失败')
352
- }
353
- }
354
-
355
- // 格式化日期
356
- const formatDate = date => {
357
- if (!date) return '未知'
358
- const d = new Date(date)
359
- return d.toLocaleString('zh-CN', {
360
- year: 'numeric',
361
- month: '2-digit',
362
- day: '2-digit',
363
- hour: '2-digit',
364
- minute: '2-digit',
365
- })
366
- }
367
- </script>
368
-
369
- <style scoped>
370
-
371
-
372
- .user-center-card {
373
- border-radius: 8px;
374
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
375
- }
376
-
377
- .user-info {
378
- text-align: center;
379
- display: flex;
380
- flex-direction: column;
381
- justify-content: center;
382
- height: 100%;
383
- min-height: 400px;
384
- }
385
-
386
- .avatar-section {
387
- margin-bottom: 24px;
388
- }
389
-
390
- .user-avatar {
391
- margin-bottom: 16px;
392
- border: 3px solid #fff;
393
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
394
- background-color: #1677ff !important;
395
- font-size: 38px !important;
396
- font-weight: 600 !important;
397
- }
398
- .user-basic-info h3 {
399
- margin: 0 0 8px 0;
400
- font-size: 20px;
401
- color: #262626;
402
- }
403
-
404
- .user-role {
405
- margin: 0 0 4px 0;
406
- color: #8c8c8c;
407
- font-size: 14px;
408
- }
409
-
410
- .user-stats {
411
- margin-top: 24px;
412
- padding: 16px;
413
- background: #fafafa;
414
- border-radius: 6px;
415
- }
416
-
417
- .stat-item {
418
- text-align: center;
419
- }
420
-
421
- .stat-number {
422
- font-size: 18px;
423
- font-weight: 600;
424
- color: #1890ff;
425
- margin-bottom: 4px;
426
- }
427
-
428
- .stat-label {
429
- font-size: 12px;
430
- color: #8c8c8c;
431
- }
432
-
433
- :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) {
434
- border-radius: 6px 6px 0 0;
435
- }
436
-
437
- :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) {
438
- background: #fff;
439
- }
440
-
441
- .basic-info-container {
442
- padding: 20px;
443
- }
444
-
445
- :deep(.ant-descriptions) {
446
- border-radius: 8px;
447
- overflow: hidden;
448
- }
449
-
450
- :deep(.ant-descriptions-title) {
451
- font-size: 18px;
452
- font-weight: 600;
453
- color: #262626;
454
- margin-bottom: 16px;
455
- }
456
-
457
- :deep(.ant-descriptions-item-label) {
458
- font-weight: 500;
459
- background: #f8f9fa;
460
- }
461
-
462
- .password-form-container {
463
- display: flex;
464
- justify-content: center;
465
- align-items: center;
466
- min-height: 300px;
467
- padding: 20px;
468
- }
469
-
470
- :deep(.password-form-container .ant-form) {
471
- width: 100%;
472
- max-width: 400px;
473
- }
474
- </style>
1
+ <template>
2
+ <div class="user-center">
3
+ <a-card title="个人中心" :bordered="false" class="user-center-card">
4
+ <a-row :gutter="32">
5
+ <!-- 左侧用户信息 -->
6
+ <a-col :span="6">
7
+ <div class="user-info">
8
+ <div class="avatar-section">
9
+ <a-avatar :size="80" :src="userInfo.avatar" class="user-avatar">
10
+ {{ userInfo.username?.charAt(0) || 'U' }}
11
+ </a-avatar>
12
+ <div class="user-basic-info">
13
+ <h3>{{ userInfo.username || '用户' }}</h3>
14
+ <p class="user-role">{{ userInfo.roleName || '普通用户' }}</p>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ </a-col>
19
+
20
+ <!-- 右侧功能区域 -->
21
+ <a-col :span="18">
22
+ <a-tabs v-model:activeKey="activeTab" type="card">
23
+ <!-- 基本信息 -->
24
+ <a-tab-pane key="basic" tab="基本信息">
25
+ <div class="basic-info-container">
26
+ <a-descriptions title="用户信息" bordered :column="2">
27
+ <a-descriptions-item label="账号名称">
28
+ {{ userInfo.username || '未设置' }}
29
+ </a-descriptions-item>
30
+ <a-descriptions-item label="手机号">
31
+ {{ userInfo.phone || '未设置' }}
32
+ </a-descriptions-item>
33
+ <a-descriptions-item label="邮箱">
34
+ {{ userInfo.email || '未设置' }}
35
+ </a-descriptions-item>
36
+ <a-descriptions-item label="角色">
37
+ {{ userInfo.roleName || '普通用户' }}
38
+ </a-descriptions-item>
39
+ <a-descriptions-item label="最后登录时间" :span="2">
40
+ {{
41
+ userInfo.lastLoginTime
42
+ ? formatDate(userInfo.lastLoginTime)
43
+ : '从未登录'
44
+ }}
45
+ </a-descriptions-item>
46
+ <a-descriptions-item label="注册时间" :span="2">
47
+ {{
48
+ userInfo.createTime
49
+ ? formatDate(userInfo.createTime)
50
+ : '未知'
51
+ }}
52
+ </a-descriptions-item>
53
+ </a-descriptions>
54
+ </div>
55
+ </a-tab-pane>
56
+
57
+ <!-- 修改密码 -->
58
+ <a-tab-pane key="password" tab="修改密码">
59
+ <div class="password-form-container">
60
+ <a-form
61
+ :model="passwordForm"
62
+ :label-col="{ span: 8 }"
63
+ :wrapper-col="{ span: 12 }"
64
+ layout="horizontal"
65
+ :rules="passwordRules"
66
+ ref="formRef"
67
+ >
68
+ <a-form-item label="原密码" name="oldPassword">
69
+ <a-input-password
70
+ v-model:value="passwordForm.oldPassword"
71
+ placeholder="请输入原密码"
72
+ />
73
+ </a-form-item>
74
+ <a-form-item label="新密码" name="newPassword">
75
+ <a-input-password
76
+ v-model:value="passwordForm.newPassword"
77
+ placeholder="请输入新密码"
78
+ />
79
+ </a-form-item>
80
+ <a-form-item label="确认密码" name="confirmPassword">
81
+ <a-input-password
82
+ v-model:value="passwordForm.confirmPassword"
83
+ placeholder="请再次输入新密码"
84
+ />
85
+ </a-form-item>
86
+ <a-form-item :wrapper-col="{ offset: 8, span: 12 }">
87
+ <a-button type="primary" @click="changePassword"
88
+ >修改密码</a-button
89
+ >
90
+ <a-button
91
+ style="margin-left: 10px"
92
+ @click="resetPasswordForm"
93
+ >重置</a-button
94
+ >
95
+ </a-form-item>
96
+ </a-form>
97
+ </div>
98
+ </a-tab-pane>
99
+
100
+ <!-- 安全设置 -->
101
+ <!-- <a-tab-pane key="security" tab="安全设置">
102
+ <a-list item-layout="horizontal">
103
+ <a-list-item>
104
+ <a-list-item-meta
105
+ title="登录提醒"
106
+ description="开启后,当有新设备登录时会发送提醒"
107
+ />
108
+ <template #actions>
109
+ <a-switch v-model:checked="securitySettings.loginAlert" />
110
+ </template>
111
+ </a-list-item>
112
+ <a-list-item>
113
+ <a-list-item-meta
114
+ title="双重验证"
115
+ description="开启双重验证,提高账户安全性"
116
+ />
117
+ <template #actions>
118
+ <a-switch v-model:checked="securitySettings.twoFactorAuth" />
119
+ </template>
120
+ </a-list-item>
121
+ <a-list-item>
122
+ <a-list-item-meta
123
+ title="会话管理"
124
+ description="查看和管理当前登录的设备"
125
+ />
126
+ <template #actions>
127
+ <a-button type="link" @click="showSessionManager">管理</a-button>
128
+ </template>
129
+ </a-list-item>
130
+ </a-list>
131
+ <div style="margin-top: 20px; text-align: center;">
132
+ <a-button type="primary" @click="saveSecuritySettings">保存设置</a-button>
133
+ </div>
134
+ </a-tab-pane> -->
135
+ </a-tabs>
136
+ </a-col>
137
+ </a-row>
138
+ </a-card>
139
+
140
+ <!-- 会话管理模态框 -->
141
+ <a-modal
142
+ v-model:visible="sessionModalVisible"
143
+ title="会话管理"
144
+ width="600px"
145
+ :footer="null"
146
+ >
147
+ <a-list item-layout="horizontal" :data-source="sessions">
148
+ <template #renderItem="{ item }">
149
+ <a-list-item>
150
+ <a-list-item-meta
151
+ :title="item.device"
152
+ :description="`最后活动: ${formatDate(item.lastActivity)}`"
153
+ />
154
+ <template #actions>
155
+ <a-button type="link" danger @click="logoutSession(item.id)"
156
+ >强制下线</a-button
157
+ >
158
+ </template>
159
+ </a-list-item>
160
+ </template>
161
+ </a-list>
162
+ </a-modal>
163
+ </div>
164
+ </template>
165
+
166
+ <script setup>
167
+ import { ref, reactive, onMounted } from 'vue'
168
+ import { message } from 'ant-design-vue'
169
+ import { useUserStore } from '@/stores/user'
170
+ import sysAccount from '@/api/methods/sysAccount'
171
+
172
+ // 用户存储
173
+ const userStore = useUserStore()
174
+
175
+ // 响应式数据
176
+ const activeTab = ref('basic')
177
+ const sessionModalVisible = ref(false)
178
+ const formRef = ref(null)
179
+ // 用户信息
180
+ const userInfo = reactive({
181
+ username: '',
182
+ email: '',
183
+ phone: '',
184
+ position: '',
185
+ roleName: '',
186
+ avatar: '',
187
+ lastLoginTime: null,
188
+ createTime: null,
189
+ })
190
+
191
+ // 密码表单
192
+ const passwordForm = reactive({
193
+ oldPassword: '',
194
+ newPassword: '',
195
+ confirmPassword: '',
196
+ })
197
+
198
+ // 安全设置
199
+ const securitySettings = reactive({
200
+ loginAlert: false,
201
+ twoFactorAuth: false,
202
+ })
203
+
204
+ // 会话列表
205
+ const sessions = ref([])
206
+
207
+ // 密码验证规则
208
+ const passwordRules = {
209
+ oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
210
+ newPassword: [
211
+ { required: true, message: '请输入新密码', trigger: 'blur' },
212
+ // { min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
213
+ ],
214
+ confirmPassword: [
215
+ { required: true, message: '请确认新密码', trigger: 'blur' },
216
+ {
217
+ validator: (rule, value) => {
218
+ if (value !== passwordForm.newPassword) {
219
+ return Promise.reject('两次输入的密码不一致')
220
+ }
221
+ return Promise.resolve()
222
+ },
223
+ trigger: 'blur',
224
+ },
225
+ ],
226
+ }
227
+
228
+ // 生命周期
229
+ onMounted(() => {
230
+ loadUserInfo()
231
+ })
232
+
233
+ // 加载用户信息
234
+ const loadUserInfo = async () => {
235
+ try {
236
+ // 从用户存储获取基本信息
237
+ const currentUser = userStore.userInfo
238
+ if (currentUser) {
239
+ Object.assign(userInfo, {
240
+ username: currentUser.name || '',
241
+ email: currentUser.email || '',
242
+ phone: currentUser.mobile || '',
243
+ position: currentUser.position || '',
244
+ roleName: currentUser.roleId === 1 ? '管理员' : '普通用户',
245
+ avatar: currentUser.profilePicture || '',
246
+ lastLoginTime: currentUser.lastLoginTime || null,
247
+ createTime: currentUser.createTime || null,
248
+ })
249
+ }
250
+
251
+ // 这里可以添加API调用获取更详细的用户信息
252
+ // const response = await getUserDetail()
253
+ // if (response.success) {
254
+ // Object.assign(userInfo, response.data)
255
+ // }
256
+ } catch (error) {
257
+ console.error('加载用户信息失败:', error)
258
+ message.error('加载用户信息失败')
259
+ }
260
+ }
261
+
262
+ // 修改密码
263
+ const changePassword = async () => {
264
+ try {
265
+ // 验证表单
266
+ await formRef.value.validate()
267
+
268
+ if (passwordForm.newPassword !== passwordForm.confirmPassword) {
269
+ message.error('两次输入的密码不一致')
270
+ return
271
+ }
272
+
273
+ // 调用修改密码接口
274
+ const res = await sysAccount.updatePwd({
275
+ id: userStore.userInfo.id,
276
+ oldPwd: passwordForm.oldPassword,
277
+ newPwd: passwordForm.newPassword,
278
+ })
279
+
280
+ message.success('密码修改成功')
281
+ resetPasswordForm()
282
+ } catch (error) {
283
+ console.error('修改密码失败:', error)
284
+ // message.error('修改密码失败')
285
+ }
286
+ }
287
+
288
+ // 重置密码表单
289
+ const resetPasswordForm = () => {
290
+ Object.assign(passwordForm, {
291
+ oldPassword: '',
292
+ newPassword: '',
293
+ confirmPassword: '',
294
+ })
295
+ }
296
+
297
+ // 保存安全设置
298
+ const saveSecuritySettings = async () => {
299
+ try {
300
+ // 这里添加保存安全设置的API调用
301
+ // await updateSecuritySettings(securitySettings)
302
+ message.success('安全设置保存成功')
303
+ } catch (error) {
304
+ console.error('保存安全设置失败:', error)
305
+ message.error('保存安全设置失败')
306
+ }
307
+ }
308
+
309
+ // 显示会话管理
310
+ const showSessionManager = () => {
311
+ sessionModalVisible.value = true
312
+ loadSessions()
313
+ }
314
+
315
+ // 加载会话列表
316
+ const loadSessions = async () => {
317
+ try {
318
+ // 这里添加获取会话列表的API调用
319
+ // const response = await getUserSessions()
320
+ // sessions.value = response.data || []
321
+
322
+ // 模拟数据
323
+ sessions.value = [
324
+ {
325
+ id: 1,
326
+ device: 'Windows Chrome',
327
+ lastActivity: new Date(),
328
+ location: '中国 北京',
329
+ },
330
+ {
331
+ id: 2,
332
+ device: 'Mac Safari',
333
+ lastActivity: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2小时前
334
+ location: '中国 上海',
335
+ },
336
+ ]
337
+ } catch (error) {
338
+ console.error('加载会话列表失败:', error)
339
+ }
340
+ }
341
+
342
+ // 强制下线会话
343
+ const logoutSession = async sessionId => {
344
+ try {
345
+ // 这里添加强制下线会话的API调用
346
+ // await forceLogoutSession(sessionId)
347
+ message.success('会话已强制下线')
348
+ loadSessions()
349
+ } catch (error) {
350
+ console.error('强制下线失败:', error)
351
+ message.error('强制下线失败')
352
+ }
353
+ }
354
+
355
+ // 格式化日期
356
+ const formatDate = date => {
357
+ if (!date) return '未知'
358
+ const d = new Date(date)
359
+ return d.toLocaleString('zh-CN', {
360
+ year: 'numeric',
361
+ month: '2-digit',
362
+ day: '2-digit',
363
+ hour: '2-digit',
364
+ minute: '2-digit',
365
+ })
366
+ }
367
+ </script>
368
+
369
+ <style scoped>
370
+
371
+
372
+ .user-center-card {
373
+ border-radius: 8px;
374
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
375
+ }
376
+
377
+ .user-info {
378
+ text-align: center;
379
+ display: flex;
380
+ flex-direction: column;
381
+ justify-content: center;
382
+ height: 100%;
383
+ min-height: 400px;
384
+ }
385
+
386
+ .avatar-section {
387
+ margin-bottom: 24px;
388
+ }
389
+
390
+ .user-avatar {
391
+ margin-bottom: 16px;
392
+ border: 3px solid #fff;
393
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
394
+ background-color: #1677ff !important;
395
+ font-size: 38px !important;
396
+ font-weight: 600 !important;
397
+ }
398
+ .user-basic-info h3 {
399
+ margin: 0 0 8px 0;
400
+ font-size: 20px;
401
+ color: #262626;
402
+ }
403
+
404
+ .user-role {
405
+ margin: 0 0 4px 0;
406
+ color: #8c8c8c;
407
+ font-size: 14px;
408
+ }
409
+
410
+ .user-stats {
411
+ margin-top: 24px;
412
+ padding: 16px;
413
+ background: #fafafa;
414
+ border-radius: 6px;
415
+ }
416
+
417
+ .stat-item {
418
+ text-align: center;
419
+ }
420
+
421
+ .stat-number {
422
+ font-size: 18px;
423
+ font-weight: 600;
424
+ color: #1890ff;
425
+ margin-bottom: 4px;
426
+ }
427
+
428
+ .stat-label {
429
+ font-size: 12px;
430
+ color: #8c8c8c;
431
+ }
432
+
433
+ :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) {
434
+ border-radius: 6px 6px 0 0;
435
+ }
436
+
437
+ :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) {
438
+ background: #fff;
439
+ }
440
+
441
+ .basic-info-container {
442
+ padding: 20px;
443
+ }
444
+
445
+ :deep(.ant-descriptions) {
446
+ border-radius: 8px;
447
+ overflow: hidden;
448
+ }
449
+
450
+ :deep(.ant-descriptions-title) {
451
+ font-size: 18px;
452
+ font-weight: 600;
453
+ color: #262626;
454
+ margin-bottom: 16px;
455
+ }
456
+
457
+ :deep(.ant-descriptions-item-label) {
458
+ font-weight: 500;
459
+ background: #f8f9fa;
460
+ }
461
+
462
+ .password-form-container {
463
+ display: flex;
464
+ justify-content: center;
465
+ align-items: center;
466
+ min-height: 300px;
467
+ padding: 20px;
468
+ }
469
+
470
+ :deep(.password-form-container .ant-form) {
471
+ width: 100%;
472
+ max-width: 400px;
473
+ }
474
+ </style>