befly-admin-ui 1.9.6 → 1.9.9
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/components/pageDialog.vue +8 -5
- package/layouts/default.vue +182 -129
- package/package.json +2 -2
- package/views/people/admin/components/edit.vue +118 -81
- package/views/people/admin/index.vue +1 -13
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<TDialog v-model:visible="innerVisible" :header="props.title === '' ? true : props.title" :width="props.width" placement="center" attach="body" :close-btn="false" :footer="true" :confirm-loading="props.confirmLoading" :close-on-overlay-click="false" :close-on-esc-keydown="false" @close="onDialogClose" @confirm="onDialogConfirm" @cancel="onDialogCancel">
|
|
2
|
+
<TDialog v-model:visible="innerVisible" :header="props.title === '' ? true : props.title" :width="props.width" :dialog-class-name="props.dialogClassName" placement="center" attach="body" :close-btn="false" :footer="true" :confirm-loading="props.confirmLoading" :close-on-overlay-click="false" :close-on-esc-keydown="false" @close="onDialogClose" @confirm="onDialogConfirm" @cancel="onDialogCancel">
|
|
3
3
|
<template #footer>
|
|
4
4
|
<div class="dialog-footer">
|
|
5
5
|
<TButton variant="outline" @click="onFooterClose">关闭</TButton>
|
|
6
|
-
<
|
|
7
|
-
<TButton theme="primary" :loading="props.confirmLoading">确定</TButton>
|
|
8
|
-
</TPopconfirm>
|
|
6
|
+
<TButton v-if="props.isConfirm" theme="primary" :loading="props.confirmLoading" @click="onFooterConfirm">确定</TButton>
|
|
9
7
|
</div>
|
|
10
8
|
</template>
|
|
11
9
|
<div class="dialog-wrapper">
|
|
@@ -17,7 +15,7 @@
|
|
|
17
15
|
<script setup>
|
|
18
16
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
19
17
|
|
|
20
|
-
import { Button as TButton, Dialog as TDialog
|
|
18
|
+
import { Button as TButton, Dialog as TDialog } from "tdesign-vue-next";
|
|
21
19
|
|
|
22
20
|
defineOptions({
|
|
23
21
|
inheritAttrs: false
|
|
@@ -39,6 +37,10 @@ const props = defineProps({
|
|
|
39
37
|
type: String,
|
|
40
38
|
default: "600px"
|
|
41
39
|
},
|
|
40
|
+
dialogClassName: {
|
|
41
|
+
type: String,
|
|
42
|
+
default: ""
|
|
43
|
+
},
|
|
42
44
|
confirmLoading: {
|
|
43
45
|
type: Boolean,
|
|
44
46
|
default: false
|
|
@@ -190,6 +192,7 @@ defineExpose({
|
|
|
190
192
|
background-color: var(--bg-color-page);
|
|
191
193
|
padding: var(--spacing-md);
|
|
192
194
|
border-radius: var(--border-radius);
|
|
195
|
+
max-height: 80vh;
|
|
193
196
|
}
|
|
194
197
|
.dialog-footer {
|
|
195
198
|
width: 100%;
|
package/layouts/default.vue
CHANGED
|
@@ -37,49 +37,62 @@
|
|
|
37
37
|
</template>
|
|
38
38
|
</t-menu>
|
|
39
39
|
</div>
|
|
40
|
+
</div>
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
<div class="footer-user">
|
|
48
|
-
<t-upload :action="$Config.uploadPath" :headers="uploadHeaders" :show-upload-list="false" accept="image/*" @success="onAvatarUploadSuccess">
|
|
49
|
-
<div class="user-avatar" :class="{ 'has-avatar': $Data.userInfo.avatar }">
|
|
42
|
+
<!-- 右侧内容区域 -->
|
|
43
|
+
<div class="layout-main">
|
|
44
|
+
<div class="main-toolbar">
|
|
45
|
+
<TDropdown trigger="click" placement="bottom-right" @click="onUserDropdownAction">
|
|
46
|
+
<div class="toolbar-user">
|
|
47
|
+
<div class="toolbar-user-avatar">
|
|
50
48
|
<img v-if="$Data.userInfo.avatar" :src="$Data.userInfo.avatar" alt="avatar" />
|
|
51
49
|
<UserIcon v-else style="width: 16px; height: 16px; color: #fff" />
|
|
52
|
-
<div class="avatar-overlay">
|
|
53
|
-
<CloudIcon style="width: 14px; height: 14px; color: #fff" />
|
|
54
|
-
</div>
|
|
55
50
|
</div>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
<div class="toolbar-user-info">
|
|
52
|
+
<span class="user-name">{{ $Data.userInfo.nickname || "管理员" }}</span>
|
|
53
|
+
<span class="user-role">{{ $Data.userInfo.role || "超级管理员" }}</span>
|
|
54
|
+
</div>
|
|
55
|
+
<ChevronDownIcon class="toolbar-user-arrow" style="width: 16px; height: 16px" />
|
|
60
56
|
</div>
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
<TDropdownMenu slot="dropdown">
|
|
58
|
+
<TDropdownItem value="password">
|
|
59
|
+
<LockOnIcon style="width: 14px; height: 14px; margin-right: 6px" />
|
|
60
|
+
修改密码
|
|
61
|
+
</TDropdownItem>
|
|
62
|
+
<TDropdownItem value="logout" :divider="true">
|
|
63
|
+
<CloseCircleIcon style="width: 14px; height: 14px; margin-right: 6px" />
|
|
64
|
+
退出登录
|
|
65
|
+
</TDropdownItem>
|
|
66
|
+
</TDropdownMenu>
|
|
67
|
+
</TDropdown>
|
|
67
68
|
</div>
|
|
68
|
-
</div>
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
<div class="main-content">
|
|
71
|
+
<RouterView />
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<PageDialog v-if="$Data.passwordDialogVisible" v-model="$Data.passwordDialogVisible" title="修改密码" :confirm-loading="$Data.passwordSubmitting" @confirm="onPasswordSubmit">
|
|
75
|
+
<TForm ref="passwordFormRef" :model="$Data.passwordForm" label-width="100px" label-position="left" :rules="passwordFormRules">
|
|
76
|
+
<TFormItem label="新密码" prop="password">
|
|
77
|
+
<TInput v-model="$Data.passwordForm.password" type="password" placeholder="请输入新密码,至少6位" autocomplete="new-password" />
|
|
78
|
+
</TFormItem>
|
|
79
|
+
<TFormItem label="确认密码" prop="confirmPassword">
|
|
80
|
+
<TInput v-model="$Data.passwordForm.confirmPassword" type="password" placeholder="请再次输入新密码" autocomplete="new-password" />
|
|
81
|
+
</TFormItem>
|
|
82
|
+
</TForm>
|
|
83
|
+
</PageDialog>
|
|
73
84
|
</div>
|
|
74
85
|
</div>
|
|
75
86
|
</template>
|
|
76
87
|
|
|
77
88
|
<script setup>
|
|
78
89
|
import { arrayToTree } from "befly-admin-ui/utils/arrayToTree";
|
|
79
|
-
import
|
|
80
|
-
import {
|
|
90
|
+
import PageDialog from "befly-admin-ui/components/pageDialog.vue";
|
|
91
|
+
import { hashPassword } from "befly-admin-ui/utils/hashPassword";
|
|
92
|
+
import { DialogPlugin, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Form as TForm, FormItem as TFormItem, Input as TInput, Menu as TMenu, MenuItem as TMenuItem, MessagePlugin, Submenu as TSubmenu } from "tdesign-vue-next";
|
|
93
|
+
import { AppIcon, ChevronDownIcon, CloseCircleIcon, ControlPlatformIcon, HomeIcon, LockOnIcon, UserIcon } from "tdesign-icons-vue-next";
|
|
81
94
|
|
|
82
|
-
import { reactive, watch } from "vue";
|
|
95
|
+
import { reactive, ref, watch } from "vue";
|
|
83
96
|
import { useRoute, useRouter } from "vue-router";
|
|
84
97
|
import { $Http } from "@/plugins/http.js";
|
|
85
98
|
import { $Config } from "@/plugins/config.js";
|
|
@@ -87,7 +100,7 @@ import { $Store } from "@/plugins/store.js";
|
|
|
87
100
|
|
|
88
101
|
const router = useRouter();
|
|
89
102
|
const route = useRoute();
|
|
90
|
-
const
|
|
103
|
+
const passwordFormRef = ref(null);
|
|
91
104
|
|
|
92
105
|
function isString(value) {
|
|
93
106
|
return typeof value === "string";
|
|
@@ -122,19 +135,39 @@ const normalizeParentPath = (parentPath) => {
|
|
|
122
135
|
return normalized;
|
|
123
136
|
};
|
|
124
137
|
|
|
138
|
+
function createPasswordForm() {
|
|
139
|
+
return {
|
|
140
|
+
password: "",
|
|
141
|
+
confirmPassword: ""
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
125
145
|
// 响应式数据
|
|
126
146
|
const $Data = reactive({
|
|
127
147
|
userMenus: [],
|
|
128
148
|
userMenusFlat: [], // 一维菜单数据
|
|
129
149
|
expandedKeys: [],
|
|
130
150
|
currentMenuKey: "",
|
|
151
|
+
passwordDialogVisible: false,
|
|
152
|
+
passwordSubmitting: false,
|
|
153
|
+
passwordForm: createPasswordForm(),
|
|
131
154
|
userInfo: {
|
|
155
|
+
id: 0,
|
|
132
156
|
nickname: "管理员",
|
|
133
157
|
role: "超级管理员",
|
|
134
|
-
avatar: ""
|
|
158
|
+
avatar: "",
|
|
159
|
+
roleCode: ""
|
|
135
160
|
}
|
|
136
161
|
});
|
|
137
162
|
|
|
163
|
+
const passwordFormRules = {
|
|
164
|
+
password: [
|
|
165
|
+
{ required: true, message: "请输入新密码", trigger: "blur" },
|
|
166
|
+
{ min: 6, message: "新密码至少6位", trigger: "blur" }
|
|
167
|
+
],
|
|
168
|
+
confirmPassword: [{ required: true, message: "请再次输入新密码", trigger: "blur" }]
|
|
169
|
+
};
|
|
170
|
+
|
|
138
171
|
function normalizeAvatarUrl(value) {
|
|
139
172
|
if (!isString(value) || value.length === 0) {
|
|
140
173
|
return "";
|
|
@@ -263,27 +296,67 @@ async function handleLogout() {
|
|
|
263
296
|
});
|
|
264
297
|
}
|
|
265
298
|
|
|
266
|
-
function
|
|
267
|
-
|
|
299
|
+
function openPasswordDialog() {
|
|
300
|
+
$Data.passwordForm = createPasswordForm();
|
|
301
|
+
$Data.passwordDialogVisible = true;
|
|
268
302
|
}
|
|
269
303
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
304
|
+
function onUserDropdownAction(data) {
|
|
305
|
+
const record = data;
|
|
306
|
+
const rawValue = record && record["value"] ? record["value"] : "";
|
|
307
|
+
const cmd = rawValue ? String(rawValue) : "";
|
|
273
308
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
avatar: avatarUrl
|
|
279
|
-
});
|
|
280
|
-
}
|
|
309
|
+
if (cmd === "password") {
|
|
310
|
+
openPasswordDialog();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
281
313
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
314
|
+
if (cmd === "logout") {
|
|
315
|
+
handleLogout();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function onPasswordSubmit(context) {
|
|
320
|
+
const form = passwordFormRef.value;
|
|
321
|
+
if (form === null) {
|
|
322
|
+
MessagePlugin.warning("表单未就绪");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const valid = await form.validate();
|
|
327
|
+
if (valid !== true) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if ($Data.passwordForm.password !== $Data.passwordForm.confirmPassword) {
|
|
332
|
+
MessagePlugin.error("两次输入的密码不一致");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!$Data.userInfo.id) {
|
|
337
|
+
MessagePlugin.error("用户信息未就绪");
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
$Data.passwordSubmitting = true;
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const hashedPassword = await hashPassword($Data.passwordForm.password);
|
|
345
|
+
const storedPassword = await hashPassword(hashedPassword);
|
|
346
|
+
|
|
347
|
+
await $Http("/core/admin/upd", {
|
|
348
|
+
id: $Data.userInfo.id,
|
|
349
|
+
password: storedPassword
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
MessagePlugin.success("密码修改成功");
|
|
353
|
+
if (context && typeof context.close === "function") {
|
|
354
|
+
context.close();
|
|
286
355
|
}
|
|
356
|
+
} catch (error) {
|
|
357
|
+
MessagePlugin.error(error.msg || error.message || "密码修改失败");
|
|
358
|
+
} finally {
|
|
359
|
+
$Data.passwordSubmitting = false;
|
|
287
360
|
}
|
|
288
361
|
}
|
|
289
362
|
|
|
@@ -413,112 +486,92 @@ watch(
|
|
|
413
486
|
}
|
|
414
487
|
}
|
|
415
488
|
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 右侧主内容区域
|
|
492
|
+
.layout-main {
|
|
493
|
+
flex: 1;
|
|
494
|
+
min-width: 0;
|
|
495
|
+
display: flex;
|
|
496
|
+
flex-direction: column;
|
|
497
|
+
gap: var(--layout-gap);
|
|
498
|
+
overflow: hidden;
|
|
416
499
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
500
|
+
.main-toolbar {
|
|
501
|
+
flex-shrink: 0;
|
|
502
|
+
display: flex;
|
|
503
|
+
justify-content: flex-end;
|
|
504
|
+
align-items: center;
|
|
505
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
506
|
+
background: var(--bg-color-container);
|
|
507
|
+
border-radius: var(--border-radius-large);
|
|
508
|
+
box-shadow: var(--shadow-1);
|
|
421
509
|
|
|
422
|
-
.
|
|
510
|
+
.toolbar-user {
|
|
423
511
|
display: flex;
|
|
424
512
|
align-items: center;
|
|
425
513
|
gap: var(--spacing-sm);
|
|
426
|
-
padding:
|
|
514
|
+
padding: 4px 8px;
|
|
427
515
|
border-radius: var(--border-radius);
|
|
428
|
-
color: var(--text-secondary);
|
|
429
516
|
cursor: pointer;
|
|
430
|
-
transition:
|
|
517
|
+
transition: background-color var(--transition-fast);
|
|
431
518
|
|
|
432
519
|
&:hover {
|
|
433
520
|
background-color: var(--bg-color-hover);
|
|
434
|
-
color: var(--text-primary);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
span {
|
|
438
|
-
font-size: var(--font-size-sm);
|
|
439
|
-
white-space: nowrap;
|
|
440
521
|
}
|
|
441
522
|
}
|
|
442
523
|
|
|
443
|
-
.
|
|
524
|
+
.toolbar-user-avatar {
|
|
525
|
+
width: 36px;
|
|
526
|
+
height: 36px;
|
|
527
|
+
min-width: 36px;
|
|
444
528
|
display: flex;
|
|
445
529
|
align-items: center;
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
.user-avatar {
|
|
453
|
-
width: 32px;
|
|
454
|
-
height: 32px;
|
|
455
|
-
min-width: 32px;
|
|
456
|
-
display: flex;
|
|
457
|
-
align-items: center;
|
|
458
|
-
justify-content: center;
|
|
459
|
-
background: var(--primary-color);
|
|
460
|
-
border-radius: 50%;
|
|
461
|
-
flex-shrink: 0;
|
|
462
|
-
cursor: pointer;
|
|
463
|
-
position: relative;
|
|
464
|
-
overflow: hidden;
|
|
465
|
-
|
|
466
|
-
img {
|
|
467
|
-
width: 100%;
|
|
468
|
-
height: 100%;
|
|
469
|
-
object-fit: cover;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.avatar-overlay {
|
|
473
|
-
position: absolute;
|
|
474
|
-
top: 0;
|
|
475
|
-
left: 0;
|
|
476
|
-
right: 0;
|
|
477
|
-
bottom: 0;
|
|
478
|
-
background: rgba(0, 0, 0, 0.5);
|
|
479
|
-
display: flex;
|
|
480
|
-
align-items: center;
|
|
481
|
-
justify-content: center;
|
|
482
|
-
opacity: 0;
|
|
483
|
-
transition: opacity var(--transition-fast);
|
|
484
|
-
}
|
|
530
|
+
justify-content: center;
|
|
531
|
+
background: var(--primary-color);
|
|
532
|
+
border-radius: 50%;
|
|
533
|
+
overflow: hidden;
|
|
534
|
+
flex-shrink: 0;
|
|
485
535
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
536
|
+
img {
|
|
537
|
+
width: 100%;
|
|
538
|
+
height: 100%;
|
|
539
|
+
object-fit: cover;
|
|
489
540
|
}
|
|
541
|
+
}
|
|
490
542
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
flex-direction: column;
|
|
543
|
+
.toolbar-user-info {
|
|
544
|
+
min-width: 0;
|
|
545
|
+
display: flex;
|
|
546
|
+
flex-direction: column;
|
|
496
547
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
white-space: nowrap;
|
|
505
|
-
}
|
|
548
|
+
.user-name {
|
|
549
|
+
font-size: var(--font-size-sm);
|
|
550
|
+
font-weight: var(--font-weight-medium);
|
|
551
|
+
color: var(--text-primary);
|
|
552
|
+
line-height: 1.3;
|
|
553
|
+
white-space: nowrap;
|
|
554
|
+
}
|
|
506
555
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
556
|
+
.user-role {
|
|
557
|
+
font-size: var(--font-size-xs);
|
|
558
|
+
color: var(--text-placeholder);
|
|
559
|
+
line-height: 1.3;
|
|
560
|
+
white-space: nowrap;
|
|
512
561
|
}
|
|
513
562
|
}
|
|
563
|
+
|
|
564
|
+
.toolbar-user-arrow {
|
|
565
|
+
color: var(--text-placeholder);
|
|
566
|
+
flex-shrink: 0;
|
|
567
|
+
}
|
|
514
568
|
}
|
|
515
|
-
}
|
|
516
569
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
570
|
+
.main-content {
|
|
571
|
+
flex: 1;
|
|
572
|
+
min-height: 0;
|
|
573
|
+
overflow: hidden;
|
|
574
|
+
}
|
|
522
575
|
}
|
|
523
576
|
}
|
|
524
577
|
</style>
|
package/package.json
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<PageDialog v-model="dialogVisible" :title="$
|
|
2
|
+
<PageDialog v-model="$Computed.dialogVisible" :title="$Computed.dialogTitle" :confirm-loading="$Data.submitting" @confirm="$Method.onSubmit">
|
|
3
3
|
<div class="dialog-wrapper">
|
|
4
|
-
<TForm :model="$Data.formData" label-width="80px" label-position="left" label-align="left" :rules="$
|
|
4
|
+
<TForm :model="$Data.formData" label-width="80px" label-position="left" label-align="left" :rules="$Const.formRules" :ref="(el) => ($From.formRef = el)">
|
|
5
5
|
<TFormItem label="角色" prop="roleCode">
|
|
6
|
-
<TSelect v-model="$Data.formData.roleCode" :options="$
|
|
6
|
+
<TSelect v-model="$Data.formData.roleCode" :options="$Type.roleOptions" placeholder="请选择角色" />
|
|
7
7
|
</TFormItem>
|
|
8
8
|
<TFormItem label="用户名" prop="username">
|
|
9
|
-
<TInput v-model="$Data.formData.username" placeholder="请输入用户名" :disabled="$
|
|
9
|
+
<TInput v-model="$Data.formData.username" placeholder="请输入用户名" :disabled="$Computed.isUpdate" />
|
|
10
10
|
</TFormItem>
|
|
11
|
-
<TFormItem v-if="$
|
|
11
|
+
<TFormItem v-if="$Computed.isAdd" label="密码" prop="password">
|
|
12
12
|
<TInput v-model="$Data.formData.password" type="password" placeholder="请输入密码,至少6位" />
|
|
13
13
|
</TFormItem>
|
|
14
14
|
<TFormItem label="昵称" prop="nickname">
|
|
15
15
|
<TInput v-model="$Data.formData.nickname" placeholder="请输入昵称" />
|
|
16
16
|
</TFormItem>
|
|
17
|
-
<TFormItem v-if="$
|
|
17
|
+
<TFormItem v-if="$Computed.isUpdate" label="状态" prop="state">
|
|
18
18
|
<TRadioGroup v-model="$Data.formData.state">
|
|
19
|
-
<TRadio :
|
|
20
|
-
<TRadio :label="2">禁用</TRadio>
|
|
19
|
+
<TRadio v-for="item in $Type.stateOptions" :key="item.value" :value="item.value">{{ item.label }}</TRadio>
|
|
21
20
|
</TRadioGroup>
|
|
22
21
|
</TFormItem>
|
|
23
22
|
</TForm>
|
|
@@ -26,7 +25,7 @@
|
|
|
26
25
|
</template>
|
|
27
26
|
|
|
28
27
|
<script setup>
|
|
29
|
-
import { computed, reactive
|
|
28
|
+
import { computed, reactive } from "vue";
|
|
30
29
|
|
|
31
30
|
import {
|
|
32
31
|
//
|
|
@@ -54,40 +53,27 @@ const $Prop = defineProps({
|
|
|
54
53
|
},
|
|
55
54
|
rowData: {
|
|
56
55
|
type: Object,
|
|
57
|
-
default: {}
|
|
56
|
+
default: () => ({})
|
|
58
57
|
}
|
|
59
58
|
});
|
|
60
59
|
|
|
61
60
|
const $Emit = defineEmits(["update:modelValue", "success"]);
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
label: "name",
|
|
78
|
-
value: "code"
|
|
62
|
+
const $Const = {
|
|
63
|
+
actionTitleMap: {
|
|
64
|
+
add: "添加管理员",
|
|
65
|
+
upd: "编辑管理员"
|
|
66
|
+
},
|
|
67
|
+
createDefaultFormData() {
|
|
68
|
+
return {
|
|
69
|
+
id: null,
|
|
70
|
+
username: "",
|
|
71
|
+
password: "",
|
|
72
|
+
nickname: "",
|
|
73
|
+
roleCode: "",
|
|
74
|
+
state: 1
|
|
75
|
+
};
|
|
79
76
|
},
|
|
80
|
-
formData: {
|
|
81
|
-
id: null,
|
|
82
|
-
username: "",
|
|
83
|
-
password: "",
|
|
84
|
-
nickname: "",
|
|
85
|
-
roleCode: null,
|
|
86
|
-
state: 1
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const $Data2 = reactive({
|
|
91
77
|
formRules: {
|
|
92
78
|
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
|
93
79
|
password: [
|
|
@@ -97,60 +83,111 @@ const $Data2 = reactive({
|
|
|
97
83
|
roleCode: [{ required: true, message: "请选择角色", trigger: "change" }],
|
|
98
84
|
nickname: [{ min: 2, max: 50, message: "昵称长度在 2 到 50 个字符", trigger: "blur" }]
|
|
99
85
|
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const $Type = reactive({
|
|
89
|
+
roleOptions: [],
|
|
90
|
+
stateOptions: [
|
|
91
|
+
{ label: "正常", value: 1 },
|
|
92
|
+
{ label: "禁用", value: 2 }
|
|
93
|
+
]
|
|
100
94
|
});
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
async function apiRoleLists() {
|
|
110
|
-
try {
|
|
111
|
-
const result = await $Http("/core/role/all", {}, [""]);
|
|
112
|
-
$Data.allRoleLists = result.data || [];
|
|
113
|
-
} catch (error) {
|
|
114
|
-
MessagePlugin.error(error.msg || error.message || "加载角色列表失败");
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function onSubmit(context) {
|
|
119
|
-
try {
|
|
120
|
-
const form = formRef.value;
|
|
121
|
-
if (!form) {
|
|
122
|
-
MessagePlugin.warning("表单未就绪");
|
|
123
|
-
return;
|
|
96
|
+
const $Computed = reactive({
|
|
97
|
+
isAdd: computed(() => $Prop.actionType === "add"),
|
|
98
|
+
isUpdate: computed(() => $Prop.actionType === "upd"),
|
|
99
|
+
dialogVisible: computed({
|
|
100
|
+
get: () => $Prop.modelValue,
|
|
101
|
+
set: (value) => {
|
|
102
|
+
$Emit("update:modelValue", value);
|
|
124
103
|
}
|
|
104
|
+
}),
|
|
105
|
+
dialogTitle: computed(() => $Const.actionTitleMap[$Prop.actionType] || "添加管理员")
|
|
106
|
+
});
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
const $From = {
|
|
109
|
+
formRef: null
|
|
110
|
+
};
|
|
128
111
|
|
|
129
|
-
|
|
130
|
-
|
|
112
|
+
const $Data = reactive({
|
|
113
|
+
submitting: false,
|
|
114
|
+
formData: $Const.createDefaultFormData()
|
|
115
|
+
});
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
const $Method = {
|
|
118
|
+
resetFormData() {
|
|
119
|
+
Object.assign($Data.formData, $Const.createDefaultFormData());
|
|
120
|
+
},
|
|
121
|
+
assignFormData(rowData) {
|
|
122
|
+
Object.assign($Data.formData, $Const.createDefaultFormData(), {
|
|
123
|
+
id: rowData.id ?? null,
|
|
124
|
+
username: rowData.username || "",
|
|
125
|
+
nickname: rowData.nickname || "",
|
|
126
|
+
roleCode: rowData.roleCode || "",
|
|
127
|
+
state: typeof rowData.state === "number" ? rowData.state : 1
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
async apiRoleLists() {
|
|
131
|
+
try {
|
|
132
|
+
const result = await $Http("/core/role/all", {}, [""]);
|
|
133
|
+
const roleList = Array.isArray(result.data?.lists) ? result.data.lists : [];
|
|
134
|
+
$Type.roleOptions = roleList.map((item) => {
|
|
135
|
+
return {
|
|
136
|
+
label: item.name || item.code || "",
|
|
137
|
+
value: item.code || ""
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
MessagePlugin.error(error.msg || error.message || "加载角色列表失败");
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
buildSubmitData() {
|
|
145
|
+
return $Computed.isAdd ? fieldClear($Data.formData, { omitKeys: ["id", "state"] }) : fieldClear($Data.formData, { omitKeys: ["password"] });
|
|
146
|
+
},
|
|
147
|
+
async initData() {
|
|
148
|
+
await $Method.apiRoleLists();
|
|
149
|
+
if ($Computed.isUpdate && $Prop.rowData.id) {
|
|
150
|
+
$Method.assignFormData($Prop.rowData);
|
|
151
|
+
return;
|
|
135
152
|
}
|
|
136
153
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
$Method.resetFormData();
|
|
155
|
+
},
|
|
156
|
+
async onSubmit(context) {
|
|
157
|
+
try {
|
|
158
|
+
const form = $From.formRef;
|
|
159
|
+
if (!form) {
|
|
160
|
+
MessagePlugin.warning("表单未就绪");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const valid = await form.validate();
|
|
165
|
+
if (!valid) return;
|
|
166
|
+
|
|
167
|
+
$Data.submitting = true;
|
|
168
|
+
const formData = $Method.buildSubmitData();
|
|
169
|
+
|
|
170
|
+
const password = typeof formData.password === "string" ? String(formData.password) : "";
|
|
171
|
+
if ($Computed.isAdd && password) {
|
|
172
|
+
formData.password = await hashPassword(password);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const result = await $Http($Computed.isUpdate ? "/core/admin/upd" : "/core/admin/ins", formData);
|
|
176
|
+
|
|
177
|
+
MessagePlugin.success(result.msg);
|
|
178
|
+
$Emit("success");
|
|
179
|
+
if (context && typeof context.close === "function") {
|
|
180
|
+
context.close();
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
MessagePlugin.error(error.msg || error.message || "提交失败");
|
|
184
|
+
} finally {
|
|
185
|
+
$Data.submitting = false;
|
|
143
186
|
}
|
|
144
|
-
} catch (error) {
|
|
145
|
-
MessagePlugin.error(error.msg || error.message || "提交失败");
|
|
146
|
-
} finally {
|
|
147
|
-
$Data.submitting = false;
|
|
148
187
|
}
|
|
149
|
-
}
|
|
188
|
+
};
|
|
150
189
|
|
|
151
|
-
initData();
|
|
190
|
+
$Method.initData();
|
|
152
191
|
</script>
|
|
153
192
|
|
|
154
|
-
<style scoped lang="scss">
|
|
155
|
-
// 可根据需要添加样式
|
|
156
|
-
</style>
|
|
193
|
+
<style scoped lang="scss"></style>
|
|
@@ -8,14 +8,6 @@
|
|
|
8
8
|
</TButton>
|
|
9
9
|
</template>
|
|
10
10
|
|
|
11
|
-
<template #toolRight="scope">
|
|
12
|
-
<TButton shape="circle" @click="onReload(scope.reload)">
|
|
13
|
-
<template #icon>
|
|
14
|
-
<RefreshIcon />
|
|
15
|
-
</template>
|
|
16
|
-
</TButton>
|
|
17
|
-
</template>
|
|
18
|
-
|
|
19
11
|
<template #state="{ row }">
|
|
20
12
|
<TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
|
|
21
13
|
<TTag v-else-if="row.state === 2" shape="round" theme="warning" variant="light-outline">禁用</TTag>
|
|
@@ -50,7 +42,7 @@
|
|
|
50
42
|
<script setup>
|
|
51
43
|
import { reactive } from "vue";
|
|
52
44
|
import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Tag as TTag } from "tdesign-vue-next";
|
|
53
|
-
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon
|
|
45
|
+
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon } from "tdesign-icons-vue-next";
|
|
54
46
|
import EditDialog from "./components/edit.vue";
|
|
55
47
|
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
56
48
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
@@ -88,10 +80,6 @@ function onAdd() {
|
|
|
88
80
|
onAction("add", {});
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
function onReload(reload) {
|
|
92
|
-
reload({ keepSelection: true });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
83
|
function onDialogSuccess(reload) {
|
|
96
84
|
reload({ keepSelection: true });
|
|
97
85
|
}
|