@befly-addon/admin 1.8.6 → 1.8.8
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/adminViews/config/dict/components/edit.vue +16 -8
- package/adminViews/config/dict/index.vue +3 -3
- package/adminViews/config/dictType/components/edit.vue +16 -8
- package/adminViews/config/dictType/index.vue +2 -1
- package/adminViews/config/system/components/edit.vue +17 -9
- package/adminViews/config/system/index.vue +2 -1
- package/adminViews/index/components/addonList.vue +10 -2
- package/adminViews/index/components/environmentInfo.vue +12 -2
- package/adminViews/index/components/operationLogs.vue +16 -2
- package/adminViews/index/components/performanceMetrics.vue +9 -3
- package/adminViews/index/components/serviceStatus.vue +11 -4
- package/adminViews/index/components/systemNotifications.vue +13 -2
- package/adminViews/index/components/systemOverview.vue +3 -2
- package/adminViews/index/components/systemResources.vue +10 -3
- package/adminViews/index/components/userInfo.vue +19 -5
- package/adminViews/log/email/index.vue +21 -12
- package/adminViews/log/login/index.vue +2 -1
- package/adminViews/log/operate/index.vue +2 -1
- package/adminViews/login_1/index.vue +11 -8
- package/adminViews/people/admin/components/edit.vue +17 -9
- package/adminViews/people/admin/index.vue +2 -1
- package/adminViews/permission/api/index.vue +3 -2
- package/adminViews/permission/menu/index.vue +3 -2
- package/adminViews/permission/role/components/api.vue +5 -5
- package/adminViews/permission/role/components/edit.vue +16 -8
- package/adminViews/permission/role/components/menu.vue +5 -5
- package/adminViews/permission/role/index.vue +2 -1
- package/apis/admin/cacheRefresh.ts +15 -3
- package/apis/api/all.ts +7 -14
- package/apis/menu/all.ts +21 -34
- package/apis/role/apis.ts +4 -12
- package/apis/role/menuSave.ts +10 -2
- package/apis/role/menus.ts +4 -11
- package/package.json +3 -3
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
</template>
|
|
25
25
|
|
|
26
26
|
<template #sendResult="{ row }">
|
|
27
|
-
<TTag v-if="row.sendResult === 1" shape="round" theme="success" variant="light-outline"
|
|
28
|
-
<TTag v-else shape="round" theme="danger" variant="light-outline"
|
|
27
|
+
<TTag v-if="row.sendResult === 1" shape="round" theme="success" variant="light-outline"> 成功 </TTag>
|
|
28
|
+
<TTag v-else shape="round" theme="danger" variant="light-outline"> 失败 </TTag>
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<template #sendTime="{ row }">
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
<template #detail="scope">
|
|
36
36
|
<DetailPanel :data="scope.row" :fields="$Data.columns">
|
|
37
37
|
<template #sendResult="slotScope">
|
|
38
|
-
<TTag v-if="slotScope.value === 1" shape="round" theme="success" variant="light-outline"
|
|
39
|
-
<TTag v-else shape="round" theme="danger" variant="light-outline"
|
|
38
|
+
<TTag v-if="slotScope.value === 1" shape="round" theme="success" variant="light-outline"> 成功 </TTag>
|
|
39
|
+
<TTag v-else shape="round" theme="danger" variant="light-outline"> 失败 </TTag>
|
|
40
40
|
</template>
|
|
41
41
|
<template #sendTime="slotScope">
|
|
42
42
|
{{ formatTime(slotScope.value) }}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
<template #dialogs="scope">
|
|
51
51
|
<PageDialog v-model="$Data.sendDialogVisible" title="发送邮件" :confirm-loading="$Data.sending" @confirm="(context) => onSend(scope.reload, context)" @cancel="onCancelSend" @close="onCancelSend">
|
|
52
|
-
<TForm
|
|
52
|
+
<TForm ref="sendFormRef" :data="$Data.sendForm" :rules="$Data.sendRules" label-width="80px">
|
|
53
53
|
<TFormItem label="收件人" name="to">
|
|
54
54
|
<TInput v-model="$Data.sendForm.to" placeholder="请输入收件人邮箱" />
|
|
55
55
|
</TFormItem>
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
</template>
|
|
70
70
|
|
|
71
71
|
<script setup lang="ts">
|
|
72
|
+
import { reactive, ref } from "vue";
|
|
72
73
|
import { Button as TButton, Form as TForm, FormItem as TFormItem, Input as TInput, MessagePlugin, Space as TSpace, Tag as TTag, Textarea as TTextarea } from "tdesign-vue-next";
|
|
73
74
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
74
75
|
import ILucideSend from "~icons/lucide/send";
|
|
@@ -79,12 +80,14 @@ import { $Http } from "@/plugins/http";
|
|
|
79
80
|
import PagedTableDetail from "@/components/pagedTableDetail.vue";
|
|
80
81
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
83
|
+
type TDesignFormInstance = {
|
|
84
|
+
validate: () => Promise<unknown>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const sendFormRef = ref<TDesignFormInstance | null>(null);
|
|
85
88
|
|
|
86
89
|
// 响应式数据
|
|
87
|
-
const $Data =
|
|
90
|
+
const $Data = reactive({
|
|
88
91
|
columns: withDefaultColumns([
|
|
89
92
|
{ colKey: "username", title: "发送人", fixed: "left" },
|
|
90
93
|
{ colKey: "toEmail", title: "收件人" },
|
|
@@ -142,7 +145,13 @@ type PageDialogEventContext = {
|
|
|
142
145
|
};
|
|
143
146
|
|
|
144
147
|
async function onSend(reload: ((options: { keepSelection?: boolean; resetPage?: boolean }) => void) | null, context?: PageDialogEventContext): Promise<void> {
|
|
145
|
-
const
|
|
148
|
+
const form = sendFormRef.value;
|
|
149
|
+
if (!form) {
|
|
150
|
+
MessagePlugin.warning("表单未就绪");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const valid = await form.validate();
|
|
146
155
|
if (valid !== true) return;
|
|
147
156
|
|
|
148
157
|
$Data.sending = true;
|
|
@@ -168,7 +177,7 @@ async function onSend(reload: ((options: { keepSelection?: boolean; resetPage?:
|
|
|
168
177
|
} else {
|
|
169
178
|
MessagePlugin.error(res.msg || "发送失败");
|
|
170
179
|
}
|
|
171
|
-
} catch (
|
|
180
|
+
} catch (_error) {
|
|
172
181
|
MessagePlugin.error("发送失败");
|
|
173
182
|
} finally {
|
|
174
183
|
$Data.sending = false;
|
|
@@ -183,7 +192,7 @@ async function onVerify(): Promise<void> {
|
|
|
183
192
|
} else {
|
|
184
193
|
MessagePlugin.error(res.msg || "配置异常");
|
|
185
194
|
}
|
|
186
|
-
} catch (
|
|
195
|
+
} catch (_error) {
|
|
187
196
|
MessagePlugin.error("验证失败");
|
|
188
197
|
}
|
|
189
198
|
}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
</template>
|
|
40
40
|
|
|
41
41
|
<script setup lang="ts">
|
|
42
|
+
import { reactive } from "vue";
|
|
42
43
|
import { Button as TButton, Tag as TTag } from "tdesign-vue-next";
|
|
43
44
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
44
45
|
import DetailPanel from "@/components/detailPanel.vue";
|
|
@@ -46,7 +47,7 @@ import PagedTableDetail from "@/components/pagedTableDetail.vue";
|
|
|
46
47
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
47
48
|
|
|
48
49
|
// 响应式数据
|
|
49
|
-
const $Data =
|
|
50
|
+
const $Data = reactive({
|
|
50
51
|
columns: withDefaultColumns([
|
|
51
52
|
{ colKey: "username", title: "用户名", fixed: "left" },
|
|
52
53
|
{ colKey: "ip", title: "登录IP" },
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
</template>
|
|
63
63
|
|
|
64
64
|
<script setup lang="ts">
|
|
65
|
+
import { reactive } from "vue";
|
|
65
66
|
import { Button as TButton, Option as TOption, Select as TSelect, Tag as TTag } from "tdesign-vue-next";
|
|
66
67
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
67
68
|
import DetailPanel from "@/components/detailPanel.vue";
|
|
@@ -69,7 +70,7 @@ import PagedTableDetail from "@/components/pagedTableDetail.vue";
|
|
|
69
70
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
70
71
|
|
|
71
72
|
// 响应式数据
|
|
72
|
-
const $Data =
|
|
73
|
+
const $Data = reactive({
|
|
73
74
|
columns: withDefaultColumns([
|
|
74
75
|
{ colKey: "username", title: "操作人", fixed: "left", width: 100 },
|
|
75
76
|
{ colKey: "module", title: "模块", width: 100 },
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<p class="login-subtitle">请登录您的账户</p>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
-
<TForm :model="$Data.formData" :rules="$Data.formRules"
|
|
24
|
+
<TForm :model="$Data.formData" :rules="$Data.formRules" ref="formRef" class="login-form" :show-message="false" label-width="0">
|
|
25
25
|
<TFormItem prop="account">
|
|
26
26
|
<TInputAdornment>
|
|
27
27
|
<template #prepend>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
<div class="form-options">
|
|
51
51
|
<TCheckbox v-model="$Data.rememberMe">记住我</TCheckbox>
|
|
52
|
-
<a href="#" class="link-text"
|
|
52
|
+
<a href="#" class="link-text"> 忘记密码? </a>
|
|
53
53
|
</div>
|
|
54
54
|
|
|
55
55
|
<TButton theme="primary" class="login-btn" size="large" block :loading="$Data.loading" @click="apiLogin"> 登录 </TButton>
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
</template>
|
|
65
65
|
|
|
66
66
|
<script setup lang="ts">
|
|
67
|
+
import { reactive, ref } from "vue";
|
|
67
68
|
import { useRouter } from "vue-router";
|
|
68
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";
|
|
69
70
|
import ILucideUser from "~icons/lucide/user";
|
|
@@ -74,13 +75,10 @@ import { hashPassword } from "befly-shared/utils/hashPassword";
|
|
|
74
75
|
|
|
75
76
|
const router = useRouter();
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
const $From = $shallowRef({
|
|
79
|
-
form: null
|
|
80
|
-
});
|
|
78
|
+
const formRef = ref<null | { validate: () => Promise<boolean> }>(null);
|
|
81
79
|
|
|
82
80
|
// 数据定义
|
|
83
|
-
const $Data =
|
|
81
|
+
const $Data = reactive({
|
|
84
82
|
loading: false,
|
|
85
83
|
rememberMe: false,
|
|
86
84
|
formRules: {
|
|
@@ -96,7 +94,12 @@ const $Data = $ref({
|
|
|
96
94
|
|
|
97
95
|
async function apiLogin(): Promise<void> {
|
|
98
96
|
try {
|
|
99
|
-
|
|
97
|
+
if (formRef.value === null) {
|
|
98
|
+
MessagePlugin.error("表单未就绪");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await formRef.value.validate();
|
|
100
103
|
|
|
101
104
|
$Data.loading = true;
|
|
102
105
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<PageDialog v-model="dialogVisible" :title="$Prop.actionType === 'upd' ? '编辑管理员' : '添加管理员'" :confirm-loading="$Data.submitting" @confirm="onSubmit">
|
|
3
3
|
<div class="dialog-wrapper">
|
|
4
|
-
<TForm :model="$Data.formData" label-width="80px" label-position="left" label-align="left" :rules="$Data2.formRules"
|
|
4
|
+
<TForm :model="$Data.formData" label-width="80px" label-position="left" label-align="left" :rules="$Data2.formRules" ref="formRef">
|
|
5
5
|
<TFormItem label="角色" prop="roleCode">
|
|
6
6
|
<TSelect v-model="$Data.formData.roleCode" :options="$Data.allRoleLists" :keys="$Data.keys" placeholder="请选择角色" />
|
|
7
7
|
</TFormItem>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</template>
|
|
27
27
|
|
|
28
28
|
<script setup lang="ts">
|
|
29
|
-
import { computed } from "vue";
|
|
29
|
+
import { computed, reactive, ref } from "vue";
|
|
30
30
|
|
|
31
31
|
import {
|
|
32
32
|
//
|
|
@@ -68,9 +68,11 @@ type PageDialogEventContext = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
// 表单引用
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
71
|
+
type TDesignFormInstance = {
|
|
72
|
+
validate: () => Promise<unknown>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const formRef = ref<TDesignFormInstance | null>(null);
|
|
74
76
|
|
|
75
77
|
const dialogVisible = computed({
|
|
76
78
|
get: () => $Prop.modelValue,
|
|
@@ -79,7 +81,7 @@ const dialogVisible = computed({
|
|
|
79
81
|
}
|
|
80
82
|
});
|
|
81
83
|
|
|
82
|
-
const $Data =
|
|
84
|
+
const $Data = reactive({
|
|
83
85
|
submitting: false,
|
|
84
86
|
allRoleLists: [],
|
|
85
87
|
keys: {
|
|
@@ -96,7 +98,7 @@ const $Data = $ref({
|
|
|
96
98
|
}
|
|
97
99
|
});
|
|
98
100
|
|
|
99
|
-
const $Data2 =
|
|
101
|
+
const $Data2 = reactive({
|
|
100
102
|
formRules: {
|
|
101
103
|
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
|
102
104
|
password: [
|
|
@@ -125,14 +127,20 @@ async function apiRoleLists(): Promise<void> {
|
|
|
125
127
|
}
|
|
126
128
|
);
|
|
127
129
|
$Data.allRoleLists = result.data || [];
|
|
128
|
-
} catch (
|
|
130
|
+
} catch (_error) {
|
|
129
131
|
MessagePlugin.error("加载角色列表失败");
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
async function onSubmit(context?: PageDialogEventContext): Promise<void> {
|
|
134
136
|
try {
|
|
135
|
-
const
|
|
137
|
+
const form = formRef.value;
|
|
138
|
+
if (!form) {
|
|
139
|
+
MessagePlugin.warning("表单未就绪");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const valid = await form.validate();
|
|
136
144
|
if (!valid) return;
|
|
137
145
|
|
|
138
146
|
$Data.submitting = true;
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
</template>
|
|
49
49
|
|
|
50
50
|
<script setup lang="ts">
|
|
51
|
+
import { reactive } from "vue";
|
|
51
52
|
import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Tag as TTag } from "tdesign-vue-next";
|
|
52
53
|
import ILucidePlus from "~icons/lucide/plus";
|
|
53
54
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
@@ -59,7 +60,7 @@ import PagedTableDetail from "@/components/pagedTableDetail.vue";
|
|
|
59
60
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
60
61
|
|
|
61
62
|
// 响应式数据
|
|
62
|
-
const $Data =
|
|
63
|
+
const $Data = reactive({
|
|
63
64
|
columns: withDefaultColumns([
|
|
64
65
|
{ colKey: "username", title: "用户名", fixed: "left" },
|
|
65
66
|
{ colKey: "nickname", title: "昵称" },
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
</template>
|
|
60
60
|
|
|
61
61
|
<script setup lang="ts">
|
|
62
|
+
import { reactive } from "vue";
|
|
62
63
|
import { Button as TButton, Table as TTable, Tag as TTag, Input as TInput, MessagePlugin } from "tdesign-vue-next";
|
|
63
64
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
64
65
|
import ILucideSearch from "~icons/lucide/search";
|
|
@@ -67,7 +68,7 @@ import { $Http } from "@/plugins/http";
|
|
|
67
68
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
68
69
|
|
|
69
70
|
// 响应式数据
|
|
70
|
-
const $Data =
|
|
71
|
+
const $Data = reactive({
|
|
71
72
|
tableData: [],
|
|
72
73
|
allData: [],
|
|
73
74
|
loading: false,
|
|
@@ -108,7 +109,7 @@ async function loadApiAll(): Promise<void> {
|
|
|
108
109
|
$Data.currentRow = null;
|
|
109
110
|
$Data.activeRowKeys = [];
|
|
110
111
|
}
|
|
111
|
-
} catch (
|
|
112
|
+
} catch (_error) {
|
|
112
113
|
MessagePlugin.error("加载数据失败");
|
|
113
114
|
} finally {
|
|
114
115
|
$Data.loading = false;
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
</template>
|
|
41
41
|
|
|
42
42
|
<script setup lang="ts">
|
|
43
|
+
import { reactive } from "vue";
|
|
43
44
|
import { Button as TButton, Table as TTable, Tag as TTag, MessagePlugin } from "tdesign-vue-next";
|
|
44
45
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
45
46
|
import DetailPanel from "@/components/detailPanel.vue";
|
|
@@ -48,7 +49,7 @@ import { arrayToTree } from "befly-shared/utils/arrayToTree";
|
|
|
48
49
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
49
50
|
|
|
50
51
|
// 响应式数据
|
|
51
|
-
const $Data =
|
|
52
|
+
const $Data = reactive({
|
|
52
53
|
tableData: [],
|
|
53
54
|
loading: false,
|
|
54
55
|
columns: withDefaultColumns([
|
|
@@ -89,7 +90,7 @@ async function apiMenuList(): Promise<void> {
|
|
|
89
90
|
$Data.currentRow = null;
|
|
90
91
|
$Data.activeRowKeys = [];
|
|
91
92
|
}
|
|
92
|
-
} catch (
|
|
93
|
+
} catch (_error) {
|
|
93
94
|
MessagePlugin.error("加载数据失败");
|
|
94
95
|
} finally {
|
|
95
96
|
$Data.loading = false;
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</template>
|
|
33
33
|
|
|
34
34
|
<script setup lang="ts">
|
|
35
|
-
import { computed } from "vue";
|
|
35
|
+
import { computed, reactive } from "vue";
|
|
36
36
|
|
|
37
37
|
import { Input as TInput, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, MessagePlugin } from "tdesign-vue-next";
|
|
38
38
|
import ILucideSearch from "~icons/lucide/search";
|
|
@@ -66,7 +66,7 @@ const dialogVisible = computed({
|
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
const $Data =
|
|
69
|
+
const $Data = reactive({
|
|
70
70
|
submitting: false,
|
|
71
71
|
apiData: [],
|
|
72
72
|
filteredApiData: [],
|
|
@@ -164,7 +164,7 @@ async function apiApiAll(): Promise<void> {
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
$Data.apiData = groups as never;
|
|
167
|
-
} catch (
|
|
167
|
+
} catch (_error) {
|
|
168
168
|
MessagePlugin.error("加载接口失败");
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -186,7 +186,7 @@ async function apiRoleApiDetail(): Promise<void> {
|
|
|
186
186
|
const resRecord = res && typeof res === "object" ? (res as Record<string, unknown>) : null;
|
|
187
187
|
const resData = resRecord && resRecord["data"] && typeof resRecord["data"] === "object" ? (resRecord["data"] as Record<string, unknown>) : null;
|
|
188
188
|
$Data.checkedApiPaths = resData && Array.isArray(resData["apiPaths"]) ? (resData["apiPaths"] as never) : ([] as never);
|
|
189
|
-
} catch (
|
|
189
|
+
} catch (_error) {
|
|
190
190
|
MessagePlugin.error("加载数据失败");
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -236,7 +236,7 @@ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
|
|
|
236
236
|
} else {
|
|
237
237
|
MessagePlugin.error(res.msg || "保存失败");
|
|
238
238
|
}
|
|
239
|
-
} catch (
|
|
239
|
+
} catch (_error) {
|
|
240
240
|
MessagePlugin.error("保存失败");
|
|
241
241
|
} finally {
|
|
242
242
|
$Data.submitting = false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<PageDialog v-model="dialogVisible" :title="$Prop.actionType === 'upd' ? '更新角色' : '添加角色'" :confirm-loading="$Data.submitting" @confirm="onSubmit">
|
|
3
3
|
<div class="comp-role-edit">
|
|
4
|
-
<TForm :model="$Data.formData" label-width="120px" label-position="left" :rules="$Data2.formRules"
|
|
4
|
+
<TForm :model="$Data.formData" label-width="120px" label-position="left" :rules="$Data2.formRules" ref="formRef">
|
|
5
5
|
<TFormItem label="角色名称" prop="name">
|
|
6
6
|
<TInput v-model="$Data.formData.name" placeholder="请输入角色名称" />
|
|
7
7
|
</TFormItem>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</template>
|
|
27
27
|
|
|
28
28
|
<script setup lang="ts">
|
|
29
|
-
import { computed } from "vue";
|
|
29
|
+
import { computed, reactive, ref } from "vue";
|
|
30
30
|
|
|
31
31
|
import {
|
|
32
32
|
//
|
|
@@ -68,13 +68,15 @@ type PageDialogEventContext = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
// 表单引用
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
71
|
+
type TDesignFormInstance = {
|
|
72
|
+
validate: () => Promise<unknown>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const formRef = ref<TDesignFormInstance | null>(null);
|
|
74
76
|
|
|
75
77
|
const $Computed = {};
|
|
76
78
|
|
|
77
|
-
const $Data =
|
|
79
|
+
const $Data = reactive({
|
|
78
80
|
submitting: false,
|
|
79
81
|
formData: {
|
|
80
82
|
id: 0,
|
|
@@ -86,7 +88,7 @@ const $Data = $ref({
|
|
|
86
88
|
}
|
|
87
89
|
});
|
|
88
90
|
|
|
89
|
-
const $Data2 =
|
|
91
|
+
const $Data2 = reactive({
|
|
90
92
|
formRules: {
|
|
91
93
|
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
|
|
92
94
|
code: [
|
|
@@ -112,7 +114,13 @@ const dialogVisible = computed({
|
|
|
112
114
|
|
|
113
115
|
async function onSubmit(context?: PageDialogEventContext): Promise<void> {
|
|
114
116
|
try {
|
|
115
|
-
const
|
|
117
|
+
const form = formRef.value;
|
|
118
|
+
if (!form) {
|
|
119
|
+
MessagePlugin.warning("表单未就绪");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const valid = await form.validate();
|
|
116
124
|
if (!valid) return;
|
|
117
125
|
|
|
118
126
|
$Data.submitting = true;
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</template>
|
|
34
34
|
|
|
35
35
|
<script setup lang="ts">
|
|
36
|
-
import { computed } from "vue";
|
|
36
|
+
import { computed, reactive } from "vue";
|
|
37
37
|
|
|
38
38
|
import { CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Input as TInput, MessagePlugin } from "tdesign-vue-next";
|
|
39
39
|
import ILucideSearch from "~icons/lucide/search";
|
|
@@ -68,7 +68,7 @@ const dialogVisible = computed({
|
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
const $Data =
|
|
71
|
+
const $Data = reactive({
|
|
72
72
|
submitting: false,
|
|
73
73
|
searchText: "",
|
|
74
74
|
menuGroups: [],
|
|
@@ -134,7 +134,7 @@ async function apiMenuAll(): Promise<void> {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
$Data.menuGroups = groups as never;
|
|
137
|
-
} catch (
|
|
137
|
+
} catch (_error) {
|
|
138
138
|
MessagePlugin.error("加载菜单失败");
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -154,7 +154,7 @@ async function apiRoleMenuDetail(): Promise<void> {
|
|
|
154
154
|
);
|
|
155
155
|
|
|
156
156
|
$Data.checkedMenuPaths = Array.isArray(res.data) ? res.data : [];
|
|
157
|
-
} catch (
|
|
157
|
+
} catch (_error) {
|
|
158
158
|
MessagePlugin.error("加载数据失败");
|
|
159
159
|
}
|
|
160
160
|
}
|
|
@@ -204,7 +204,7 @@ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
|
|
|
204
204
|
} else {
|
|
205
205
|
MessagePlugin.error(res.msg || "保存失败");
|
|
206
206
|
}
|
|
207
|
-
} catch (
|
|
207
|
+
} catch (_error) {
|
|
208
208
|
MessagePlugin.error("保存失败");
|
|
209
209
|
} finally {
|
|
210
210
|
$Data.submitting = false;
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
</template>
|
|
72
72
|
|
|
73
73
|
<script setup lang="ts">
|
|
74
|
+
import { reactive } from "vue";
|
|
74
75
|
import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Tag as TTag } from "tdesign-vue-next";
|
|
75
76
|
import ILucidePlus from "~icons/lucide/plus";
|
|
76
77
|
import ILucideRotateCw from "~icons/lucide/rotate-cw";
|
|
@@ -86,7 +87,7 @@ import PagedTableDetail from "@/components/pagedTableDetail.vue";
|
|
|
86
87
|
import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
|
|
87
88
|
|
|
88
89
|
// 响应式数据
|
|
89
|
-
const $Data =
|
|
90
|
+
const $Data = reactive({
|
|
90
91
|
columns: withDefaultColumns([
|
|
91
92
|
{ colKey: "name", title: "角色名称" },
|
|
92
93
|
{ colKey: "code", title: "角色代码", width: 150 },
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* 功能:
|
|
5
5
|
* 1. 刷新接口缓存(apis:all)
|
|
6
6
|
* 2. 刷新菜单缓存(menus:all)
|
|
7
|
-
* 3.
|
|
7
|
+
* 3. 刷新角色缓存(role:info:{code})
|
|
8
|
+
* 4. 重建角色接口权限缓存(role:apis:{code},Set)
|
|
9
|
+
* 5. 重建角色菜单权限缓存(role:menus:{code},Set)
|
|
8
10
|
*
|
|
9
11
|
* 使用场景:
|
|
10
12
|
* - 执行数据库同步后
|
|
@@ -22,7 +24,8 @@ const route: ApiRoute = {
|
|
|
22
24
|
apis: { success: false, count: 0 },
|
|
23
25
|
menus: { success: false, count: 0 },
|
|
24
26
|
roles: { success: false, count: 0 },
|
|
25
|
-
roleApiPermissions: { success: false }
|
|
27
|
+
roleApiPermissions: { success: false },
|
|
28
|
+
roleMenuPermissions: { success: false }
|
|
26
29
|
};
|
|
27
30
|
|
|
28
31
|
// 1. 刷新接口缓存
|
|
@@ -89,8 +92,17 @@ const route: ApiRoute = {
|
|
|
89
92
|
results["roleApiPermissions"] = { success: false, error: error.message };
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
// 5. 重建角色菜单权限缓存
|
|
96
|
+
try {
|
|
97
|
+
await befly.cache.rebuildRoleMenuPermissions();
|
|
98
|
+
results["roleMenuPermissions"] = { success: true };
|
|
99
|
+
} catch (error: any) {
|
|
100
|
+
befly.logger.error({ err: error, msg: "重建角色菜单权限缓存失败" });
|
|
101
|
+
results["roleMenuPermissions"] = { success: false, error: error.message };
|
|
102
|
+
}
|
|
103
|
+
|
|
92
104
|
// 检查是否全部成功
|
|
93
|
-
const allSuccess = results["apis"].success && results["menus"].success && results["roles"].success && results["roleApiPermissions"].success;
|
|
105
|
+
const allSuccess = results["apis"].success && results["menus"].success && results["roles"].success && results["roleApiPermissions"].success && results["roleMenuPermissions"].success;
|
|
94
106
|
|
|
95
107
|
if (allSuccess) {
|
|
96
108
|
return befly.tool.Yes("全部缓存刷新成功", results);
|
package/apis/api/all.ts
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import type { DbJsonRow } from "../../utils/dbJsonRow";
|
|
2
1
|
import type { ApiRoute } from "befly/types/api";
|
|
2
|
+
import type { JsonValue, KeyValue } from "befly/types/common";
|
|
3
|
+
|
|
4
|
+
type ApiRow = KeyValue<JsonValue>;
|
|
3
5
|
|
|
4
6
|
const route: ApiRoute = {
|
|
5
7
|
name: "获取所有接口",
|
|
6
8
|
handler: async (befly) => {
|
|
7
9
|
try {
|
|
8
10
|
// 从缓存获取所有接口
|
|
9
|
-
|
|
11
|
+
const allApis = (await befly.cache.getApis()) as ApiRow[];
|
|
10
12
|
|
|
11
|
-
//
|
|
13
|
+
// 强制缓存:不回退 DB
|
|
12
14
|
if (allApis.length === 0) {
|
|
13
|
-
|
|
14
|
-
table: "addon_admin_api",
|
|
15
|
-
orderBy: ["id#ASC"]
|
|
16
|
-
});
|
|
17
|
-
allApis = result.data.lists;
|
|
18
|
-
|
|
19
|
-
// 缓存到 Redis
|
|
20
|
-
if (allApis.length > 0) {
|
|
21
|
-
await befly.cache.cacheApis();
|
|
22
|
-
}
|
|
15
|
+
return befly.tool.No("接口缓存不存在,请刷新缓存", { lists: [] });
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
const lists = allApis.filter((api)
|
|
18
|
+
const lists = allApis.filter((api) => api && typeof api === "object");
|
|
26
19
|
|
|
27
20
|
return befly.tool.Yes("操作成功", { lists: lists });
|
|
28
21
|
} catch (error: unknown) {
|
package/apis/menu/all.ts
CHANGED
|
@@ -1,61 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 获取当前用户的菜单权限
|
|
3
3
|
* 说明:
|
|
4
|
-
* 1. 从 Redis
|
|
5
|
-
* 2.
|
|
6
|
-
* 3.
|
|
7
|
-
* 4.
|
|
4
|
+
* 1. 从 Redis 缓存读取所有菜单(强制:不回退 DB)
|
|
5
|
+
* 2. 从 Redis Set 读取角色菜单权限(role:menus:{roleCode}),拿到该角色可访问的菜单 path 列表
|
|
6
|
+
* 3. 根据角色菜单 path 列表过滤可访问的菜单
|
|
7
|
+
* 4. 返回一维数组(由前端构建树形结构)
|
|
8
|
+
* 5. 仅返回状态为启用的菜单
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import type { DbJsonRow } from "../../utils/dbJsonRow";
|
|
11
11
|
import type { ApiRoute } from "befly/types/api";
|
|
12
|
+
import type { JsonValue, KeyValue } from "befly/types/common";
|
|
13
|
+
|
|
14
|
+
type MenuRow = KeyValue<JsonValue>;
|
|
12
15
|
|
|
13
16
|
const route: ApiRoute = {
|
|
14
17
|
name: "获取用户菜单",
|
|
15
18
|
handler: async (befly, ctx) => {
|
|
16
19
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
where: { code: ctx.user["roleCode"] }
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
if (!role.data?.id) {
|
|
24
|
-
return befly.tool.No("角色不存在", { lists: [] });
|
|
20
|
+
const roleCode = typeof ctx.user["roleCode"] === "string" ? ctx.user["roleCode"] : "";
|
|
21
|
+
if (roleCode.length === 0) {
|
|
22
|
+
return befly.tool.No("角色缺失", { lists: [] });
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
const menuPaths =
|
|
25
|
+
// 2. 从 Redis Set 读取角色菜单权限(由 core 的 cache 体系维护)
|
|
26
|
+
// 约定:key = role:menus:{roleCode}(会自动加上 Redis prefix)
|
|
27
|
+
const menuPaths = await befly.cache.getRoleMenuPermissions(roleCode);
|
|
30
28
|
|
|
31
29
|
if (menuPaths.length === 0) {
|
|
32
30
|
return befly.tool.Yes("菜单为空", { lists: [] });
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
// 4.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// 如果缓存不存在,从数据库查询
|
|
39
|
-
if (allMenus.length === 0) {
|
|
40
|
-
const result = await befly.db.getAll<DbJsonRow>({
|
|
41
|
-
table: "addon_admin_menu"
|
|
42
|
-
});
|
|
43
|
-
allMenus = result.data.lists;
|
|
44
|
-
}
|
|
33
|
+
// 4. 从缓存获取所有菜单(强制:不回退 DB)
|
|
34
|
+
const allMenus = (await befly.cache.getMenus()) as MenuRow[];
|
|
45
35
|
|
|
46
36
|
if (allMenus.length === 0) {
|
|
47
|
-
return befly.tool.
|
|
37
|
+
return befly.tool.No("菜单缓存不存在,请刷新缓存", { lists: [] });
|
|
48
38
|
}
|
|
49
39
|
|
|
50
40
|
// 5. 根据角色权限过滤菜单(按 menu.path)
|
|
51
41
|
const menuPathSet = new Set<string>(menuPaths);
|
|
52
|
-
const authorizedMenus = allMenus
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
return typeof path === "string" && menuPathSet.has(path);
|
|
57
|
-
})
|
|
58
|
-
.map((menu) => menu as DbJsonRow);
|
|
42
|
+
const authorizedMenus = allMenus.filter((menu) => {
|
|
43
|
+
const path = menu["path"];
|
|
44
|
+
return typeof path === "string" && menuPathSet.has(path);
|
|
45
|
+
});
|
|
59
46
|
|
|
60
47
|
// 6. 返回一维数组(由前端构建树形结构)
|
|
61
48
|
return befly.tool.Yes("获取菜单成功", { lists: authorizedMenus });
|