befly-admin-ui 1.8.14

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 (46) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +188 -0
  3. package/jsconfig.json +14 -0
  4. package/package.json +51 -0
  5. package/styles/variables.scss +148 -0
  6. package/utils/arrayToTree.js +115 -0
  7. package/utils/cleanParams.js +29 -0
  8. package/utils/fieldClear.js +62 -0
  9. package/utils/genShortId.js +12 -0
  10. package/utils/hashPassword.js +9 -0
  11. package/utils/scanViewsDir.js +120 -0
  12. package/utils/withDefaultColumns.js +46 -0
  13. package/views/config/dict/components/edit.vue +120 -0
  14. package/views/config/dict/index.vue +188 -0
  15. package/views/config/dictType/components/edit.vue +110 -0
  16. package/views/config/dictType/index.vue +153 -0
  17. package/views/config/index.vue +3 -0
  18. package/views/config/system/components/edit.vue +184 -0
  19. package/views/config/system/index.vue +188 -0
  20. package/views/index/components/addonList.vue +148 -0
  21. package/views/index/components/environmentInfo.vue +116 -0
  22. package/views/index/components/operationLogs.vue +127 -0
  23. package/views/index/components/performanceMetrics.vue +153 -0
  24. package/views/index/components/quickActions.vue +30 -0
  25. package/views/index/components/serviceStatus.vue +197 -0
  26. package/views/index/components/systemNotifications.vue +144 -0
  27. package/views/index/components/systemOverview.vue +194 -0
  28. package/views/index/components/systemResources.vue +121 -0
  29. package/views/index/components/userInfo.vue +210 -0
  30. package/views/index/index.vue +67 -0
  31. package/views/jsconfig.json +15 -0
  32. package/views/log/email/index.vue +221 -0
  33. package/views/log/index.vue +3 -0
  34. package/views/log/login/index.vue +95 -0
  35. package/views/log/operate/index.vue +169 -0
  36. package/views/login_1/index.vue +400 -0
  37. package/views/people/admin/components/edit.vue +173 -0
  38. package/views/people/admin/index.vue +121 -0
  39. package/views/people/index.vue +3 -0
  40. package/views/permission/api/index.vue +146 -0
  41. package/views/permission/index.vue +3 -0
  42. package/views/permission/menu/index.vue +109 -0
  43. package/views/permission/role/components/api.vue +371 -0
  44. package/views/permission/role/components/edit.vue +143 -0
  45. package/views/permission/role/components/menu.vue +310 -0
  46. package/views/permission/role/index.vue +175 -0
@@ -0,0 +1,400 @@
1
+ <template>
2
+ <div class="login-container">
3
+ <!-- 左侧装饰区域 -->
4
+ <div class="left-section">
5
+ <div class="bg-decoration">
6
+ <div class="circle circle-1"></div>
7
+ <div class="circle circle-2"></div>
8
+ <div class="circle circle-3"></div>
9
+ </div>
10
+ <div class="welcome-content">
11
+ <h1 class="brand-title">Befly</h1>
12
+ <p class="brand-subtitle">轻量级业务快速开发框架</p>
13
+ </div>
14
+ </div>
15
+
16
+ <!-- 右侧登录区域 -->
17
+ <div class="right-section">
18
+ <div class="login-box">
19
+ <div class="login-header">
20
+ <h2 class="login-title">欢迎回来</h2>
21
+ <p class="login-subtitle">请登录您的账户</p>
22
+ </div>
23
+
24
+ <TForm :model="$Data.formData" :rules="$Data.formRules" ref="formRef" class="login-form" :show-message="false" label-width="0">
25
+ <TFormItem prop="account">
26
+ <TInputAdornment>
27
+ <template #prepend>
28
+ <TSelect v-model="$Data.formData.loginType" :style="{ width: '110px' }" size="large" :popup-props="{ overlayClassName: 'login-type-select-popup' }">
29
+ <TOption value="username" label="用户名" />
30
+ <TOption value="email" label="邮箱" />
31
+ <TOption value="phone" label="手机号" />
32
+ </TSelect>
33
+ </template>
34
+ <TInput v-model="$Data.formData.account" :placeholder="$Data.formData.loginType === 'username' ? '请输入用户名' : $Data.formData.loginType === 'email' ? '请输入邮箱' : '请输入手机号'" size="large" clearable @enter="apiLogin">
35
+ <template #prefix-icon>
36
+ <UserIcon />
37
+ </template>
38
+ </TInput>
39
+ </TInputAdornment>
40
+ </TFormItem>
41
+
42
+ <TFormItem prop="password">
43
+ <TInput v-model="$Data.formData.password" type="password" placeholder="密码" size="large" clearable @enter="apiLogin">
44
+ <template #prefix-icon>
45
+ <LockOnIcon />
46
+ </template>
47
+ </TInput>
48
+ </TFormItem>
49
+
50
+ <div class="form-options">
51
+ <TCheckbox v-model="$Data.rememberMe">记住我</TCheckbox>
52
+ <a href="#" class="link-text">忘记密码?</a>
53
+ </div>
54
+
55
+ <TButton theme="primary" class="login-btn" size="large" block :loading="$Data.loading" @click="apiLogin">登录</TButton>
56
+ </TForm>
57
+
58
+ <div class="login-footer">
59
+ <p class="copyright">© 2024 Befly. All rights reserved.</p>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ import { reactive, ref } from "vue";
68
+ import { useRouter } from "vue-router";
69
+ import { Form as TForm, FormItem as TFormItem, Input as TInput, Button as TButton, Checkbox as TCheckbox, InputAdornment as TInputAdornment, Select as TSelect, Option as TOption, MessagePlugin } from "tdesign-vue-next";
70
+ import { LockOnIcon, UserIcon } from "tdesign-icons-vue-next";
71
+ import { $Http } from "@/plugins/http";
72
+ import { $Storage } from "@/plugins/storage";
73
+ import { hashPassword } from "befly-admin-ui/utils/hashPassword";
74
+
75
+ const router = useRouter();
76
+
77
+ const formRef = ref<null | { validate: () => Promise<boolean> }>(null);
78
+
79
+ // 数据定义
80
+ const $Data = reactive({
81
+ loading: false,
82
+ rememberMe: false,
83
+ formRules: {
84
+ account: [{ required: true, message: "请输入账号", trigger: "blur" }],
85
+ password: [{ required: true, message: "请输入密码", trigger: "blur" }]
86
+ },
87
+ formData: {
88
+ loginType: "username",
89
+ account: "",
90
+ password: ""
91
+ }
92
+ });
93
+
94
+ async function apiLogin(): Promise<void> {
95
+ try {
96
+ if (formRef.value === null) {
97
+ MessagePlugin.error("表单未就绪");
98
+ return;
99
+ }
100
+
101
+ await formRef.value.validate();
102
+
103
+ $Data.loading = true;
104
+
105
+ const hashedPassword = await hashPassword($Data.formData.password);
106
+
107
+ const res = await $Http.post("/core/auth/login", {
108
+ loginType: $Data.formData.loginType,
109
+ account: $Data.formData.account,
110
+ password: hashedPassword
111
+ });
112
+
113
+ $Storage.local.set("token", res.data.token);
114
+
115
+ if (res.data.userInfo) {
116
+ $Storage.local.set("userInfo", res.data.userInfo);
117
+ }
118
+
119
+ MessagePlugin.success(res.msg || "登录成功");
120
+
121
+ await router.push("/");
122
+ } catch (error) {
123
+ MessagePlugin.error("登录失败");
124
+ } finally {
125
+ $Data.loading = false;
126
+ }
127
+ }
128
+ </script>
129
+
130
+ <style scoped lang="scss">
131
+ .login-container {
132
+ display: flex;
133
+ width: 100%;
134
+ min-height: 100vh;
135
+ background: var(--login-bg);
136
+ }
137
+
138
+ // 左侧装饰区域
139
+ .left-section {
140
+ flex: 1;
141
+ position: relative;
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: center;
145
+ background: linear-gradient(135deg, var(--login-left-gradient-start) 0%, var(--login-left-gradient-end) 100%);
146
+ overflow: hidden;
147
+ }
148
+
149
+ .bg-decoration {
150
+ position: absolute;
151
+ width: 100%;
152
+ height: 100%;
153
+ overflow: hidden;
154
+
155
+ .circle {
156
+ position: absolute;
157
+ border-radius: 50%;
158
+ background: var(--login-circle-bg);
159
+ animation: float 20s infinite ease-in-out;
160
+
161
+ &.circle-1 {
162
+ width: 400px;
163
+ height: 400px;
164
+ top: -100px;
165
+ left: -100px;
166
+ animation-delay: 0s;
167
+ }
168
+
169
+ &.circle-2 {
170
+ width: 300px;
171
+ height: 300px;
172
+ bottom: -50px;
173
+ right: -50px;
174
+ animation-delay: 7s;
175
+ }
176
+
177
+ &.circle-3 {
178
+ width: 200px;
179
+ height: 200px;
180
+ top: 50%;
181
+ right: 10%;
182
+ animation-delay: 14s;
183
+ }
184
+ }
185
+ }
186
+
187
+ @keyframes float {
188
+ 0%,
189
+ 100% {
190
+ transform: translateY(0) scale(1);
191
+ }
192
+ 50% {
193
+ transform: translateY(-20px) scale(1.05);
194
+ }
195
+ }
196
+
197
+ .welcome-content {
198
+ position: relative;
199
+ z-index: 1;
200
+ color: #fff;
201
+ text-align: center;
202
+ padding: 2rem;
203
+ }
204
+
205
+ .brand-title {
206
+ font-size: 4rem;
207
+ font-weight: 700;
208
+ margin-bottom: 1rem;
209
+ letter-spacing: 2px;
210
+ color: var(--login-brand-title);
211
+ }
212
+
213
+ .brand-subtitle {
214
+ font-size: 1.25rem;
215
+ margin-bottom: 3rem;
216
+ color: var(--login-brand-subtitle);
217
+ }
218
+
219
+ // 右侧登录区域
220
+ .right-section {
221
+ flex: 1;
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ padding: 2rem;
226
+ }
227
+
228
+ .login-box {
229
+ width: 100%;
230
+ max-width: 440px;
231
+ background: var(--login-card-bg);
232
+ border: 1px solid var(--login-card-border);
233
+ border-radius: 16px;
234
+ box-shadow: 0 10px 40px var(--login-card-shadow);
235
+ padding: 3rem 2.5rem;
236
+ }
237
+
238
+ .login-header {
239
+ text-align: center;
240
+ margin-bottom: 2.5rem;
241
+ }
242
+
243
+ .login-title {
244
+ font-size: 2rem;
245
+ font-weight: 600;
246
+ color: var(--login-title);
247
+ margin-bottom: 0.5rem;
248
+ }
249
+
250
+ .login-subtitle {
251
+ font-size: 1rem;
252
+ color: var(--login-subtitle);
253
+ }
254
+
255
+ .login-footer {
256
+ margin-top: 2rem;
257
+ text-align: center;
258
+ }
259
+
260
+ .copyright {
261
+ font-size: 0.875rem;
262
+ color: var(--login-subtitle);
263
+ }
264
+
265
+ .login-form {
266
+ width: 100%;
267
+
268
+ :deep(.t-form__item) {
269
+ margin-bottom: 1.25rem;
270
+ }
271
+
272
+ :deep(.t-form__controls) {
273
+ width: 100%;
274
+ }
275
+
276
+ :deep(.t-input-adornment) {
277
+ width: 100%;
278
+ }
279
+
280
+ :deep(.t-input-adornment__prepend) {
281
+ padding: 0;
282
+ border-right: 1px solid var(--login-card-border);
283
+ }
284
+
285
+ :deep(.t-input) {
286
+ width: 100%;
287
+ border-radius: 8px;
288
+ transition: all 0.3s;
289
+
290
+ &:hover {
291
+ border-color: #667eea;
292
+ }
293
+
294
+ &:focus-within {
295
+ border-color: #667eea;
296
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
297
+ }
298
+ }
299
+
300
+ :deep(.t-input__wrap) {
301
+ width: 100%;
302
+ }
303
+ }
304
+
305
+ .form-options {
306
+ display: flex;
307
+ justify-content: space-between;
308
+ align-items: center;
309
+ margin-bottom: 1.5rem;
310
+ font-size: 0.875rem;
311
+
312
+ .link-text {
313
+ color: var(--login-link);
314
+ text-decoration: none;
315
+ transition: color 0.3s;
316
+
317
+ &:hover {
318
+ color: var(--login-link-hover);
319
+ }
320
+ }
321
+ }
322
+
323
+ .login-btn {
324
+ width: 100%;
325
+ height: 48px;
326
+ border-radius: 8px;
327
+ font-size: 1rem;
328
+ font-weight: 600;
329
+ background: linear-gradient(135deg, var(--login-btn-gradient-start) 0%, var(--login-btn-gradient-end) 100%);
330
+ border: none;
331
+ transition: all 0.3s;
332
+
333
+ &:hover {
334
+ transform: translateY(-2px);
335
+ box-shadow: 0 8px 20px var(--login-btn-shadow);
336
+ }
337
+
338
+ &:active {
339
+ transform: translateY(0);
340
+ }
341
+
342
+ :deep(.t-button__text) {
343
+ color: #fff;
344
+ }
345
+ }
346
+
347
+ // 响应式设计
348
+ @media (max-width: 1024px) {
349
+ .login-container {
350
+ flex-direction: column;
351
+ }
352
+
353
+ .left-section {
354
+ min-height: 300px;
355
+ flex: none;
356
+ }
357
+
358
+ .brand-title {
359
+ font-size: 3rem;
360
+ }
361
+
362
+ .right-section {
363
+ flex: 1;
364
+ }
365
+ }
366
+
367
+ @media (max-width: 768px) {
368
+ .left-section {
369
+ min-height: 200px;
370
+ }
371
+
372
+ .brand-title {
373
+ font-size: 2.5rem;
374
+ }
375
+
376
+ .brand-subtitle {
377
+ font-size: 1rem;
378
+ margin-bottom: 2rem;
379
+ }
380
+
381
+ .login-box {
382
+ padding: 2rem 1.5rem;
383
+ }
384
+
385
+ .login-title {
386
+ font-size: 1.75rem;
387
+ }
388
+ }
389
+
390
+ @media (max-width: 480px) {
391
+ .right-section {
392
+ padding: 1rem;
393
+ }
394
+
395
+ .login-box {
396
+ padding: 1.5rem 1rem;
397
+ border-radius: 12px;
398
+ }
399
+ }
400
+ </style>
@@ -0,0 +1,173 @@
1
+ <template>
2
+ <PageDialog v-model="dialogVisible" :title="$Prop.actionType === 'upd' ? '编辑管理员' : '添加管理员'" :confirm-loading="$Data.submitting" @confirm="onSubmit">
3
+ <div class="dialog-wrapper">
4
+ <TForm :model="$Data.formData" label-width="80px" label-position="left" label-align="left" :rules="$Data2.formRules" ref="formRef">
5
+ <TFormItem label="角色" prop="roleCode">
6
+ <TSelect v-model="$Data.formData.roleCode" :options="$Data.allRoleLists" :keys="$Data.keys" placeholder="请选择角色" />
7
+ </TFormItem>
8
+ <TFormItem label="用户名" prop="username">
9
+ <TInput v-model="$Data.formData.username" placeholder="请输入用户名" :disabled="$Prop.actionType === 'upd'" />
10
+ </TFormItem>
11
+ <TFormItem v-if="$Prop.actionType === 'add'" label="密码" prop="password">
12
+ <TInput v-model="$Data.formData.password" type="password" placeholder="请输入密码,至少6位" />
13
+ </TFormItem>
14
+ <TFormItem label="昵称" prop="nickname">
15
+ <TInput v-model="$Data.formData.nickname" placeholder="请输入昵称" />
16
+ </TFormItem>
17
+ <TFormItem v-if="$Prop.actionType === 'upd'" label="状态" prop="state">
18
+ <TRadioGroup v-model="$Data.formData.state">
19
+ <TRadio :label="1">正常</TRadio>
20
+ <TRadio :label="2">禁用</TRadio>
21
+ </TRadioGroup>
22
+ </TFormItem>
23
+ </TForm>
24
+ </div>
25
+ </PageDialog>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import { computed, reactive, ref } from "vue";
30
+
31
+ import {
32
+ //
33
+ Form as TForm,
34
+ FormItem as TFormItem,
35
+ Input as TInput,
36
+ Select as TSelect,
37
+ RadioGroup as TRadioGroup,
38
+ Radio as TRadio,
39
+ MessagePlugin
40
+ } from "tdesign-vue-next";
41
+ import PageDialog from "@/components/pageDialog.vue";
42
+ import { $Http } from "@/plugins/http";
43
+ import { fieldClear } from "befly-admin-ui/utils/fieldClear";
44
+ import { hashPassword } from "befly-admin-ui/utils/hashPassword";
45
+
46
+ const $Prop = defineProps({
47
+ modelValue: {
48
+ type: Boolean,
49
+ default: false
50
+ },
51
+ actionType: {
52
+ type: String,
53
+ default: "add"
54
+ },
55
+ rowData: {
56
+ type: Object,
57
+ default: {}
58
+ }
59
+ });
60
+
61
+ const $Emit = defineEmits<{
62
+ (e: "update:modelValue", value: boolean): void;
63
+ (e: "success"): void;
64
+ }>();
65
+
66
+ type PageDialogEventContext = {
67
+ close: () => void;
68
+ };
69
+
70
+ // 表单引用
71
+ type TDesignFormInstance = {
72
+ validate: () => Promise<unknown>;
73
+ };
74
+
75
+ const formRef = ref<TDesignFormInstance | null>(null);
76
+
77
+ const dialogVisible = computed({
78
+ get: () => $Prop.modelValue,
79
+ set: (value) => {
80
+ $Emit("update:modelValue", value);
81
+ }
82
+ });
83
+
84
+ const $Data = reactive({
85
+ submitting: false,
86
+ allRoleLists: [],
87
+ keys: {
88
+ label: "name",
89
+ value: "code"
90
+ },
91
+ formData: {
92
+ id: null,
93
+ username: "",
94
+ password: "",
95
+ nickname: "",
96
+ roleCode: null,
97
+ state: 1
98
+ }
99
+ });
100
+
101
+ const $Data2 = reactive({
102
+ formRules: {
103
+ username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
104
+ password: [
105
+ { required: true, message: "请输入密码", trigger: "blur" },
106
+ { min: 6, message: "密码至少6位", trigger: "blur" }
107
+ ],
108
+ roleCode: [{ required: true, message: "请选择角色", trigger: "change" }],
109
+ nickname: [{ min: 2, max: 50, message: "昵称长度在 2 到 50 个字符", trigger: "blur" }]
110
+ }
111
+ });
112
+
113
+ async function initData(): Promise<void> {
114
+ await apiRoleLists();
115
+ if ($Prop.actionType === "upd" && $Prop.rowData.id) {
116
+ $Data.formData = Object.assign({}, $Prop.rowData);
117
+ }
118
+ }
119
+
120
+ async function apiRoleLists(): Promise<void> {
121
+ try {
122
+ const result = await $Http.post(
123
+ "/core/role/all",
124
+ {},
125
+ {
126
+ dropValues: [""]
127
+ }
128
+ );
129
+ $Data.allRoleLists = result.data || [];
130
+ } catch (_error) {
131
+ MessagePlugin.error("加载角色列表失败");
132
+ }
133
+ }
134
+
135
+ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
136
+ try {
137
+ const form = formRef.value;
138
+ if (!form) {
139
+ MessagePlugin.warning("表单未就绪");
140
+ return;
141
+ }
142
+
143
+ const valid = await form.validate();
144
+ if (!valid) return;
145
+
146
+ $Data.submitting = true;
147
+ const formData = ($Prop.actionType === "add" ? fieldClear($Data.formData, { omitKeys: ["id", "state"] }) : fieldClear($Data.formData, { omitKeys: ["password"] })) as Record<string, unknown>;
148
+
149
+ const password = typeof formData["password"] === "string" ? String(formData["password"]) : "";
150
+ if ($Prop.actionType === "add" && password) {
151
+ formData["password"] = await hashPassword(password);
152
+ }
153
+
154
+ const result = await $Http.post($Prop.actionType === "upd" ? "/core/admin/upd" : "/core/admin/ins", formData);
155
+
156
+ MessagePlugin.success(result.msg);
157
+ $Emit("success");
158
+ if (context && typeof context.close === "function") {
159
+ context.close();
160
+ }
161
+ } catch (error) {
162
+ MessagePlugin.error((error as { msg?: string }).msg || "提交失败");
163
+ } finally {
164
+ $Data.submitting = false;
165
+ }
166
+ }
167
+
168
+ initData();
169
+ </script>
170
+
171
+ <style scoped lang="scss">
172
+ // 可根据需要添加样式
173
+ </style>
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <PagedTableDetail class="page-admin" :columns="$Data.columns" :endpoints="$Data.endpoints">
3
+ <template #toolLeft>
4
+ <TButton theme="primary" @click="onAdd">
5
+ <template #icon>
6
+ <AddIcon />
7
+ </template>
8
+ </TButton>
9
+ </template>
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
+ <template #state="{ row }">
20
+ <TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
21
+ <TTag v-else-if="row.state === 2" shape="round" theme="warning" variant="light-outline">禁用</TTag>
22
+ <TTag v-else-if="row.state === 0" shape="round" theme="danger" variant="light-outline">删除</TTag>
23
+ </template>
24
+
25
+ <template #operation="{ row, deleteRow }">
26
+ <TDropdown trigger="click" placement="bottom-right" @click="onDropdownAction($event, row, deleteRow)">
27
+ <TButton theme="primary" size="small">
28
+ 操作
29
+ <template #suffix><ChevronDownIcon /></template>
30
+ </TButton>
31
+ <TDropdownMenu slot="dropdown">
32
+ <TDropdownItem value="upd">
33
+ <EditIcon />
34
+ 编辑
35
+ </TDropdownItem>
36
+ <TDropdownItem value="del" :divider="true">
37
+ <DeleteIcon />
38
+ 删除
39
+ </TDropdownItem>
40
+ </TDropdownMenu>
41
+ </TDropdown>
42
+ </template>
43
+
44
+ <template #dialogs="scope">
45
+ <EditDialog v-if="$Data.editVisible" v-model="$Data.editVisible" :action-type="$Data.actionType" :row-data="$Data.rowData" @success="onDialogSuccess(scope.reload)" />
46
+ </template>
47
+ </PagedTableDetail>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ import { reactive } from "vue";
52
+ 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, RefreshIcon } from "tdesign-icons-vue-next";
54
+ import EditDialog from "./components/edit.vue";
55
+ import PagedTableDetail from "@/components/pagedTableDetail.vue";
56
+ import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
57
+
58
+ // 响应式数据
59
+ const $Data = reactive({
60
+ columns: withDefaultColumns([
61
+ { colKey: "username", title: "用户名", fixed: "left" },
62
+ { colKey: "nickname", title: "昵称" },
63
+ { colKey: "roleCode", title: "角色" },
64
+ { colKey: "state", title: "状态" },
65
+ { colKey: "operation", title: "操作" }
66
+ ]),
67
+ endpoints: {
68
+ list: {
69
+ path: "/core/admin/list",
70
+ dropValues: [""]
71
+ },
72
+ delete: {
73
+ path: "/core/admin/del",
74
+ idKey: "id",
75
+ confirm: (row) => ({
76
+ header: "确认删除",
77
+ body: `确认删除“${row.username}”吗?`,
78
+ confirmBtn: "删除"
79
+ })
80
+ }
81
+ },
82
+ editVisible: false,
83
+ actionType: "add",
84
+ rowData: {}
85
+ });
86
+
87
+ function onAdd(): void {
88
+ onAction("add", {});
89
+ }
90
+
91
+ function onReload(reload: (options: { keepSelection?: boolean }) => void): void {
92
+ reload({ keepSelection: true });
93
+ }
94
+
95
+ function onDialogSuccess(reload: (options: { keepSelection?: boolean }) => void): void {
96
+ reload({ keepSelection: true });
97
+ }
98
+
99
+ function onAction(command: string, rowData: Record<string, unknown>): void {
100
+ $Data.actionType = command;
101
+ $Data.rowData = rowData;
102
+ if (command === "add" || command === "upd") {
103
+ $Data.editVisible = true;
104
+ }
105
+ }
106
+
107
+ function onDropdownAction(data: unknown, rowData: Record<string, unknown>, deleteRow: (row: Record<string, unknown>) => void): void {
108
+ const record = data as Record<string, unknown>;
109
+ const rawValue = record && record["value"] ? record["value"] : "";
110
+ const cmd = rawValue ? String(rawValue) : "";
111
+ if (cmd === "del") {
112
+ deleteRow(rowData);
113
+ return;
114
+ }
115
+ onAction(cmd, rowData);
116
+ }
117
+ </script>
118
+
119
+ <style scoped lang="scss">
120
+ // 样式继承自全局 page-table
121
+ </style>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <RouterView />
3
+ </template>