@befly-addon/admin 1.8.2 → 1.8.5

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.
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <TDialog v-model:visible="$Data.visible" title="菜单权限" width="900px" :append-to-body="true" :show-footer="true" top="5vh" @close="$Method.onClose">
2
+ <PageDialog v-model="dialogVisible" title="菜单权限" width="900px" :confirm-loading="$Data.submitting" @confirm="onSubmit">
3
3
  <div class="comp-role-menu">
4
4
  <!-- 搜索框 -->
5
5
  <div class="search-box">
6
- <TInput v-model="$Data.searchText" placeholder="搜索菜单名称或路径" clearable @change="$Method.onSearch">
6
+ <TInput v-model="$Data.searchText" placeholder="搜索菜单名称或路径" clearable @change="onSearch">
7
7
  <template #prefix-icon>
8
8
  <ILucideSearch />
9
9
  </template>
@@ -29,20 +29,15 @@
29
29
  </div>
30
30
  </div>
31
31
  </div>
32
- <template #footer>
33
- <div class="dialog-footer">
34
- <t-space>
35
- <TButton theme="default" @click="$Method.onClose">取消</TButton>
36
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">保存</TButton>
37
- </t-space>
38
- </div>
39
- </template>
40
- </TDialog>
32
+ </PageDialog>
41
33
  </template>
42
34
 
43
35
  <script setup lang="ts">
44
- import { Dialog as TDialog, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Button as TButton, Input as TInput, MessagePlugin } from "tdesign-vue-next";
36
+ import { computed } from "vue";
37
+
38
+ import { CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Input as TInput, MessagePlugin } from "tdesign-vue-next";
45
39
  import ILucideSearch from "~icons/lucide/search";
40
+ import PageDialog from "@/components/pageDialog.vue";
46
41
  import { $Http } from "@/plugins/http";
47
42
  import { arrayToTree } from "befly-shared/utils/arrayToTree";
48
43
 
@@ -57,10 +52,23 @@ const $Prop = defineProps({
57
52
  }
58
53
  });
59
54
 
60
- const $Emit = defineEmits(["update:modelValue", "success"]);
55
+ const $Emit = defineEmits<{
56
+ (e: "update:modelValue", value: boolean): void;
57
+ (e: "success"): void;
58
+ }>();
59
+
60
+ type PageDialogEventContext = {
61
+ close: () => void;
62
+ };
63
+
64
+ const dialogVisible = computed({
65
+ get: () => $Prop.modelValue,
66
+ set: (value) => {
67
+ $Emit("update:modelValue", value);
68
+ }
69
+ });
61
70
 
62
71
  const $Data = $ref({
63
- visible: false,
64
72
  submitting: false,
65
73
  searchText: "",
66
74
  menuGroups: [],
@@ -68,158 +76,142 @@ const $Data = $ref({
68
76
  checkedMenuPaths: []
69
77
  });
70
78
 
71
- // 方法集合
72
- const $Method = {
73
- async initData() {
74
- await Promise.all([$Method.apiMenuAll(), $Method.apiRoleMenuDetail()]);
75
- $Data.filteredMenuGroups = $Data.menuGroups;
76
- $Method.onShow();
77
- },
78
-
79
- onShow() {
80
- setTimeout(() => {
81
- $Data.visible = $Prop.modelValue;
82
- }, 100);
83
- },
84
-
85
- onClose() {
86
- $Data.visible = false;
87
- setTimeout(() => {
88
- $Emit("update:modelValue", false);
89
- }, 300);
90
- },
79
+ async function initData(): Promise<void> {
80
+ await Promise.all([apiMenuAll(), apiRoleMenuDetail()]);
81
+ $Data.filteredMenuGroups = $Data.menuGroups;
82
+ }
91
83
 
92
- // 加载菜单树(用于配置权限)
93
- async apiMenuAll() {
94
- try {
95
- const res = await $Http.post(
96
- "/addon/admin/menu/all",
97
- {},
98
- {
99
- dropValues: [""]
84
+ async function apiMenuAll(): Promise<void> {
85
+ try {
86
+ const res = await $Http.post(
87
+ "/addon/admin/menu/all",
88
+ {},
89
+ {
90
+ dropValues: [""]
91
+ }
92
+ );
93
+ const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
94
+
95
+ const treeResult = arrayToTree(lists, "path", "parentPath", "children", "sort");
96
+ const roots = Array.isArray(treeResult?.tree) ? treeResult.tree : [];
97
+
98
+ const groups: Array<{ name: string; title: string; menus: unknown[] }> = [];
99
+ for (const root of roots) {
100
+ const rootPath = typeof root?.path === "string" ? root.path : "";
101
+ const rootName = typeof root?.name === "string" ? root.name : "";
102
+
103
+ const menus: unknown[] = [];
104
+
105
+ const walk = (node: Record<string, unknown>, depth: number): void => {
106
+ const name = typeof node["name"] === "string" ? String(node["name"]) : "";
107
+ const path = typeof node["path"] === "string" ? String(node["path"]) : "";
108
+ if (path.length > 0) {
109
+ menus.push({
110
+ value: path,
111
+ name: name,
112
+ path: path,
113
+ depth: depth,
114
+ label: `${name} ${path}`.trim()
115
+ });
100
116
  }
101
- );
102
- const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
103
-
104
- const treeResult = arrayToTree(lists, "path", "parentPath", "children", "sort");
105
- const roots = Array.isArray(treeResult?.tree) ? treeResult.tree : [];
106
-
107
- const groups = [];
108
- for (const root of roots) {
109
- const rootPath = typeof root?.path === "string" ? root.path : "";
110
- const rootName = typeof root?.name === "string" ? root.name : "";
111
-
112
- const menus = [];
113
-
114
- const walk = (node, depth) => {
115
- const name = typeof node?.name === "string" ? node.name : "";
116
- const path = typeof node?.path === "string" ? node.path : "";
117
- if (path.length > 0) {
118
- menus.push({
119
- value: path,
120
- name: name,
121
- path: path,
122
- depth: depth,
123
- label: `${name} ${path}`.trim()
124
- });
125
- }
126
117
 
127
- const children = Array.isArray(node?.children) ? node.children : [];
128
- for (const child of children) {
129
- walk(child, depth + 1);
118
+ const children = Array.isArray(node["children"]) ? (node["children"] as unknown[]) : [];
119
+ for (const child of children) {
120
+ if (child && typeof child === "object") {
121
+ walk(child as Record<string, unknown>, depth + 1);
130
122
  }
131
- };
123
+ }
124
+ };
132
125
 
133
- walk(root, 0);
126
+ walk(root as Record<string, unknown>, 0);
134
127
 
135
- const groupTitle = rootName.length > 0 ? rootName : rootPath;
136
- groups.push({
137
- name: rootPath.length > 0 ? rootPath : groupTitle,
138
- title: groupTitle.length > 0 ? groupTitle : "未命名菜单",
139
- menus: menus
140
- });
141
- }
142
-
143
- $Data.menuGroups = groups;
144
- } catch (error) {
145
- MessagePlugin.error("加载菜单失败");
128
+ const groupTitle = rootName.length > 0 ? rootName : rootPath;
129
+ groups.push({
130
+ name: rootPath.length > 0 ? rootPath : groupTitle,
131
+ title: groupTitle.length > 0 ? groupTitle : "未命名菜单",
132
+ menus: menus
133
+ });
146
134
  }
147
- },
148
135
 
149
- // 加载该角色已分配的菜单
150
- async apiRoleMenuDetail() {
151
- if (!$Prop.rowData.id) return;
152
-
153
- try {
154
- const res = await $Http.post(
155
- "/addon/admin/role/menus",
156
- {
157
- roleCode: $Prop.rowData.code
158
- },
159
- {
160
- dropValues: [""]
161
- }
162
- );
136
+ $Data.menuGroups = groups as never;
137
+ } catch (error) {
138
+ MessagePlugin.error("加载菜单失败");
139
+ }
140
+ }
163
141
 
164
- // menus 返回的 data 直接就是菜单 path 数组
165
- $Data.checkedMenuPaths = Array.isArray(res.data) ? res.data : [];
166
- } catch (error) {
167
- MessagePlugin.error("加载数据失败");
168
- }
169
- },
142
+ async function apiRoleMenuDetail(): Promise<void> {
143
+ if (!$Prop.rowData.id) return;
144
+
145
+ try {
146
+ const res = await $Http.post(
147
+ "/addon/admin/role/menus",
148
+ {
149
+ roleCode: $Prop.rowData.code
150
+ },
151
+ {
152
+ dropValues: [""]
153
+ }
154
+ );
170
155
 
171
- // 搜索过滤(按“名称 + 路径”匹配;展示结构与接口弹框一致)
172
- onSearch() {
173
- const kw = typeof $Data.searchText === "string" ? $Data.searchText.trim().toLowerCase() : "";
174
- if (kw.length === 0) {
175
- $Data.filteredMenuGroups = $Data.menuGroups;
176
- return;
177
- }
156
+ $Data.checkedMenuPaths = Array.isArray(res.data) ? res.data : [];
157
+ } catch (error) {
158
+ MessagePlugin.error("加载数据失败");
159
+ }
160
+ }
178
161
 
179
- $Data.filteredMenuGroups = $Data.menuGroups
180
- .map((group) => {
181
- const menus = Array.isArray(group?.menus)
182
- ? group.menus.filter((menu) => {
183
- const label = typeof menu?.label === "string" ? menu.label : "";
184
- return label.toLowerCase().includes(kw);
185
- })
186
- : [];
187
-
188
- return {
189
- name: group.name,
190
- title: group.title,
191
- menus: menus
192
- };
193
- })
194
- .filter((group) => Array.isArray(group.menus) && group.menus.length > 0);
195
- },
162
+ function onSearch(): void {
163
+ const kw = typeof $Data.searchText === "string" ? $Data.searchText.trim().toLowerCase() : "";
164
+ if (kw.length === 0) {
165
+ $Data.filteredMenuGroups = $Data.menuGroups;
166
+ return;
167
+ }
196
168
 
197
- // 提交表单
198
- async onSubmit() {
199
- try {
200
- $Data.submitting = true;
169
+ $Data.filteredMenuGroups = ($Data.menuGroups as unknown[])
170
+ .map((group) => {
171
+ const groupRecord = group && typeof group === "object" ? (group as Record<string, unknown>) : null;
172
+ const groupMenus = groupRecord && Array.isArray(groupRecord["menus"]) ? (groupRecord["menus"] as unknown[]) : [];
201
173
 
202
- const res = await $Http.post("/addon/admin/role/menuSave", {
203
- roleCode: $Prop.rowData.code,
204
- menuPaths: $Data.checkedMenuPaths
174
+ const menus = groupMenus.filter((menu) => {
175
+ const menuRecord = menu && typeof menu === "object" ? (menu as Record<string, unknown>) : null;
176
+ const label = menuRecord && typeof menuRecord["label"] === "string" ? String(menuRecord["label"]) : "";
177
+ return label.toLowerCase().includes(kw);
205
178
  });
206
179
 
207
- if (res.code === 0) {
208
- MessagePlugin.success("保存成功");
209
- $Data.visible = false;
210
- $Emit("success");
211
- } else {
212
- MessagePlugin.error(res.msg || "保存失败");
180
+ return {
181
+ name: groupRecord && typeof groupRecord["name"] === "string" ? String(groupRecord["name"]) : "",
182
+ title: groupRecord && typeof groupRecord["title"] === "string" ? String(groupRecord["title"]) : "",
183
+ menus: menus
184
+ };
185
+ })
186
+ .filter((group) => group.menus.length > 0) as never;
187
+ }
188
+
189
+ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
190
+ try {
191
+ $Data.submitting = true;
192
+
193
+ const res = await $Http.post("/addon/admin/role/menuSave", {
194
+ roleCode: $Prop.rowData.code,
195
+ menuPaths: $Data.checkedMenuPaths
196
+ });
197
+
198
+ if (res.code === 0) {
199
+ MessagePlugin.success("保存成功");
200
+ $Emit("success");
201
+ if (context && typeof context.close === "function") {
202
+ context.close();
213
203
  }
214
- } catch (error) {
215
- MessagePlugin.error("保存失败");
216
- } finally {
217
- $Data.submitting = false;
204
+ } else {
205
+ MessagePlugin.error(res.msg || "保存失败");
218
206
  }
207
+ } catch (error) {
208
+ MessagePlugin.error("保存失败");
209
+ } finally {
210
+ $Data.submitting = false;
219
211
  }
220
- };
212
+ }
221
213
 
222
- $Method.initData();
214
+ initData();
223
215
  </script>
224
216
 
225
217
  <style scoped lang="scss">
@@ -245,7 +237,7 @@ $Method.initData();
245
237
 
246
238
  .group-header {
247
239
  padding: 12px 16px;
248
- background-color: var(--bg-color-hover);
240
+ background-color: var(--primary-color-light);
249
241
  font-weight: 500;
250
242
  font-size: var(--font-size-sm);
251
243
  color: var(--text-primary);
@@ -255,11 +247,11 @@ $Method.initData();
255
247
 
256
248
  &::before {
257
249
  content: "";
258
- width: 8px;
259
- height: 8px;
250
+ width: 12px;
251
+ height: 12px;
260
252
  border-radius: 50%;
261
253
  background-color: var(--primary-color);
262
- opacity: 0.3;
254
+ box-shadow: 0 0 0 2px var(--bg-color-container);
263
255
  flex-shrink: 0;
264
256
  }
265
257
  }