@befly-addon/admin 1.8.4 → 1.8.6

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.
@@ -3,7 +3,7 @@
3
3
  <div class="main-tool">
4
4
  <div class="left"></div>
5
5
  <div class="right">
6
- <TButton shape="circle" @click="$Method.handleRefresh">
6
+ <TButton shape="circle" @click="handleRefresh">
7
7
  <template #icon>
8
8
  <ILucideRotateCw />
9
9
  </template>
@@ -22,7 +22,7 @@
22
22
  height="calc(100vh - var(--search-height) - var(--layout-gap) * 2)"
23
23
  active-row-type="single"
24
24
  :tree="{ childrenKey: 'children', treeNodeColumnIndex: 0, defaultExpandAll: true }"
25
- @active-change="$Method.onActiveChange"
25
+ @active-change="onActiveChange"
26
26
  >
27
27
  <template #state="{ row }">
28
28
  <TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
@@ -42,7 +42,7 @@
42
42
  <script setup lang="ts">
43
43
  import { Button as TButton, Table as TTable, Tag as TTag, MessagePlugin } from "tdesign-vue-next";
44
44
  import ILucideRotateCw from "~icons/lucide/rotate-cw";
45
- import DetailPanel from "@/components/DetailPanel.vue";
45
+ import DetailPanel from "@/components/detailPanel.vue";
46
46
  import { $Http } from "@/plugins/http";
47
47
  import { arrayToTree } from "befly-shared/utils/arrayToTree";
48
48
  import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
@@ -62,65 +62,55 @@ const $Data = $ref({
62
62
  activeRowKeys: []
63
63
  });
64
64
 
65
- // 方法
66
- const $Method = {
67
- async initData() {
68
- await $Method.apiMenuList();
69
- },
65
+ async function initData(): Promise<void> {
66
+ await apiMenuList();
67
+ }
70
68
 
71
- // 加载菜单列表(树形结构)
72
- async apiMenuList() {
73
- $Data.loading = true;
74
- try {
75
- const res = await $Http.post(
76
- "/addon/admin/menu/all",
77
- {},
78
- {
79
- dropValues: [""]
80
- }
81
- );
82
- const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
69
+ async function apiMenuList(): Promise<void> {
70
+ $Data.loading = true;
71
+ try {
72
+ const res = await $Http.post(
73
+ "/addon/admin/menu/all",
74
+ {},
75
+ {
76
+ dropValues: [""]
77
+ }
78
+ );
79
+ const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
83
80
 
84
- const treeResult = arrayToTree(lists, "path", "parentPath", "children", "sort");
81
+ const treeResult = arrayToTree(lists, "path", "parentPath", "children", "sort");
85
82
 
86
- // 构建树形结构(TTable tree
87
- $Data.tableData = treeResult.tree;
83
+ $Data.tableData = treeResult.tree;
88
84
 
89
- // 自动高亮第一行
90
- if ($Data.tableData.length > 0) {
91
- $Data.currentRow = $Data.tableData[0];
92
- $Data.activeRowKeys = [$Data.tableData[0].id];
93
- } else {
94
- $Data.currentRow = null;
95
- $Data.activeRowKeys = [];
96
- }
97
- } catch (error) {
98
- MessagePlugin.error("加载数据失败");
99
- } finally {
100
- $Data.loading = false;
85
+ if ($Data.tableData.length > 0) {
86
+ $Data.currentRow = $Data.tableData[0];
87
+ $Data.activeRowKeys = [$Data.tableData[0].id];
88
+ } else {
89
+ $Data.currentRow = null;
90
+ $Data.activeRowKeys = [];
101
91
  }
102
- },
92
+ } catch (error) {
93
+ MessagePlugin.error("加载数据失败");
94
+ } finally {
95
+ $Data.loading = false;
96
+ }
97
+ }
103
98
 
104
- // 刷新
105
- handleRefresh() {
106
- $Method.apiMenuList();
107
- },
99
+ function handleRefresh(): void {
100
+ apiMenuList();
101
+ }
108
102
 
109
- // 高亮行变化
110
- onActiveChange(value, context) {
111
- // 禁止取消高亮:如果新值为空,保持当前选中
112
- if (value.length === 0 && $Data.activeRowKeys.length > 0) {
113
- return;
114
- }
115
- $Data.activeRowKeys = value;
116
- // 更新当前高亮的行数据
117
- if (context.activeRowList && context.activeRowList.length > 0) {
118
- $Data.currentRow = context.activeRowList[0].row;
119
- }
103
+ function onActiveChange(value: unknown[], context: { activeRowList?: Array<{ row: unknown }> }): void {
104
+ if (value.length === 0 && $Data.activeRowKeys.length > 0) {
105
+ return;
106
+ }
107
+ $Data.activeRowKeys = value;
108
+ if (context.activeRowList && context.activeRowList.length > 0) {
109
+ $Data.currentRow = context.activeRowList[0].row as never;
120
110
  }
121
- };
111
+ }
122
112
 
123
- $Method.initData();
113
+ initData();
124
114
  </script>
125
115
 
126
116
  <style scoped lang="scss">
@@ -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-api">
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>
@@ -28,21 +28,15 @@
28
28
  </TCheckboxGroup>
29
29
  </div>
30
30
  </div>
31
-
32
- <template #footer>
33
- <div class="dialog-footer">
34
- <TSpace>
35
- <TButton theme="default" @click="$Method.onClose">取消</TButton>
36
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">保存</TButton>
37
- </TSpace>
38
- </div>
39
- </template>
40
- </TDialog>
31
+ </PageDialog>
41
32
  </template>
42
33
 
43
34
  <script setup lang="ts">
44
- import { Dialog as TDialog, Input as TInput, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Button as TButton, Space as TSpace, MessagePlugin } from "tdesign-vue-next";
35
+ import { computed } from "vue";
36
+
37
+ import { Input as TInput, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, MessagePlugin } from "tdesign-vue-next";
45
38
  import ILucideSearch from "~icons/lucide/search";
39
+ import PageDialog from "@/components/pageDialog.vue";
46
40
  import { $Http } from "@/plugins/http";
47
41
 
48
42
  const $Prop = defineProps({
@@ -61,8 +55,18 @@ const $Emit = defineEmits<{
61
55
  (e: "success"): void;
62
56
  }>();
63
57
 
58
+ type PageDialogEventContext = {
59
+ close: () => void;
60
+ };
61
+
62
+ const dialogVisible = computed({
63
+ get: () => $Prop.modelValue,
64
+ set: (value) => {
65
+ $Emit("update:modelValue", value);
66
+ }
67
+ });
68
+
64
69
  const $Data = $ref({
65
- visible: false,
66
70
  submitting: false,
67
71
  apiData: [],
68
72
  filteredApiData: [],
@@ -70,184 +74,176 @@ const $Data = $ref({
70
74
  checkedApiPaths: []
71
75
  });
72
76
 
73
- // 方法集合
74
- const $Method = {
75
- async initData() {
76
- $Method.onShow();
77
- await Promise.all([$Method.apiApiAll(), $Method.apiRoleApiDetail()]);
77
+ async function initData(): Promise<void> {
78
+ await Promise.all([apiApiAll(), apiRoleApiDetail()]);
78
79
 
79
- // auth=0(免登录)接口默认选中:与角色已有权限做并集,不覆盖。
80
- const merged = new Set();
81
- const current = Array.isArray($Data.checkedApiPaths) ? $Data.checkedApiPaths : [];
82
- for (const p of current) {
80
+ const merged = new Set<string>();
81
+ const current = Array.isArray($Data.checkedApiPaths) ? $Data.checkedApiPaths : [];
82
+ for (const p of current) {
83
+ if (typeof p === "string") {
83
84
  merged.add(p);
84
85
  }
86
+ }
85
87
 
86
- const groups = Array.isArray($Data.apiData) ? $Data.apiData : [];
87
- for (const group of groups) {
88
- const apis = group && group.apis;
89
- const list = Array.isArray(apis) ? apis : [];
90
- for (const api of list) {
91
- const isPublic = api && (api.auth === 0 || api.auth === "0" || api.auth === false);
92
- if (isPublic && typeof api.value === "string" && api.value) {
93
- merged.add(api.value);
94
- }
88
+ const groups = Array.isArray($Data.apiData) ? ($Data.apiData as unknown[]) : [];
89
+ for (const group of groups) {
90
+ const groupRecord = group && typeof group === "object" ? (group as Record<string, unknown>) : null;
91
+ const apis = groupRecord && Array.isArray(groupRecord["apis"]) ? (groupRecord["apis"] as unknown[]) : [];
92
+ for (const api of apis) {
93
+ const apiRecord = api && typeof api === "object" ? (api as Record<string, unknown>) : null;
94
+ const authValue = apiRecord ? apiRecord["auth"] : undefined;
95
+ const isPublic = authValue === 0 || authValue === "0" || authValue === false;
96
+ const value = apiRecord && typeof apiRecord["value"] === "string" ? String(apiRecord["value"]) : "";
97
+ if (isPublic && value) {
98
+ merged.add(value);
95
99
  }
96
100
  }
101
+ }
97
102
 
98
- $Data.checkedApiPaths = Array.from(merged);
99
- $Data.filteredApiData = $Data.apiData;
100
- },
101
-
102
- onShow() {
103
- setTimeout(() => {
104
- $Data.visible = $Prop.modelValue;
105
- }, 100);
106
- },
107
-
108
- onClose() {
109
- $Data.visible = false;
110
- setTimeout(() => {
111
- $Emit("update:modelValue", false);
112
- }, 300);
113
- },
114
-
115
- // 加载所有接口
116
- async apiApiAll() {
117
- try {
118
- const res = await $Http.post(
119
- "/addon/admin/api/all",
120
- {},
121
- {
122
- dropValues: [""]
123
- }
124
- );
125
-
126
- // 将接口列表按 parentPath 分组展示(routePath 已迁移为 path)
127
- const apiMap = new Map();
128
-
129
- const lists = res && res.data && Array.isArray(res.data.lists) ? res.data.lists : [];
130
- for (const api of lists) {
131
- const apiPath = api && typeof api.path === "string" ? api.path : "";
132
- if (!apiPath) {
133
- continue;
134
- }
135
-
136
- const parentPath = api && typeof api.parentPath === "string" ? api.parentPath : "";
137
- const groupKey = parentPath || "(未分组)";
138
-
139
- if (!apiMap.has(groupKey)) {
140
- apiMap.set(groupKey, {
141
- name: groupKey,
142
- title: groupKey,
143
- apis: []
144
- });
145
- }
103
+ $Data.checkedApiPaths = Array.from(merged) as never;
104
+ $Data.filteredApiData = $Data.apiData;
105
+ }
146
106
 
147
- apiMap.get(groupKey).apis.push({
148
- value: apiPath,
149
- name: (api && typeof api.name === "string" ? api.name : "") || apiPath,
150
- path: apiPath,
151
- label: `${(api && typeof api.name === "string" ? api.name : "") || ""} ${apiPath ? `(${apiPath})` : ""}`.trim(),
152
- description: api ? api.description : undefined,
153
- auth: api ? api.auth : undefined,
154
- parentPath: parentPath
155
- });
107
+ async function apiApiAll(): Promise<void> {
108
+ try {
109
+ const res = await $Http.post(
110
+ "/addon/admin/api/all",
111
+ {},
112
+ {
113
+ dropValues: [""]
156
114
  }
115
+ );
116
+
117
+ const apiMap = new Map<string, { name: string; title: string; apis: unknown[] }>();
118
+
119
+ const resRecord = res && typeof res === "object" ? (res as Record<string, unknown>) : null;
120
+ const resData = resRecord && resRecord["data"] && typeof resRecord["data"] === "object" ? (resRecord["data"] as Record<string, unknown>) : null;
121
+ const lists = resData && Array.isArray(resData["lists"]) ? (resData["lists"] as unknown[]) : [];
122
+ for (const api of lists) {
123
+ const apiRecord = api && typeof api === "object" ? (api as Record<string, unknown>) : null;
124
+ const apiPath = apiRecord && typeof apiRecord["path"] === "string" ? String(apiRecord["path"]) : "";
125
+ if (!apiPath) {
126
+ continue;
127
+ }
128
+
129
+ const parentPath = apiRecord && typeof apiRecord["parentPath"] === "string" ? String(apiRecord["parentPath"]) : "";
130
+ const groupKey = parentPath || "(未分组)";
157
131
 
158
- const groups = Array.from(apiMap.values());
159
- for (const group of groups) {
160
- group.apis.sort((a, b) => {
161
- const ap = typeof a.path === "string" ? a.path : "";
162
- const bp = typeof b.path === "string" ? b.path : "";
163
- return ap.localeCompare(bp);
132
+ if (!apiMap.has(groupKey)) {
133
+ apiMap.set(groupKey, {
134
+ name: groupKey,
135
+ title: groupKey,
136
+ apis: []
164
137
  });
165
138
  }
166
139
 
167
- groups.sort((a, b) => {
168
- const at = typeof a.title === "string" ? a.title : "";
169
- const bt = typeof b.title === "string" ? b.title : "";
170
- return at.localeCompare(bt);
140
+ apiMap.get(groupKey)?.apis.push({
141
+ value: apiPath,
142
+ name: (apiRecord && typeof apiRecord["name"] === "string" ? String(apiRecord["name"]) : "") || apiPath,
143
+ path: apiPath,
144
+ label: `${(apiRecord && typeof apiRecord["name"] === "string" ? String(apiRecord["name"]) : "") || ""} ${apiPath ? `(${apiPath})` : ""}`.trim(),
145
+ description: apiRecord ? apiRecord["description"] : undefined,
146
+ auth: apiRecord ? apiRecord["auth"] : undefined,
147
+ parentPath: parentPath
171
148
  });
149
+ }
172
150
 
173
- $Data.apiData = groups;
174
- } catch (error) {
175
- MessagePlugin.error("加载接口失败");
151
+ const groups = Array.from(apiMap.values());
152
+ for (const group of groups) {
153
+ group.apis.sort((a, b) => {
154
+ const ap = typeof a.path === "string" ? a.path : "";
155
+ const bp = typeof b.path === "string" ? b.path : "";
156
+ return ap.localeCompare(bp);
157
+ });
176
158
  }
177
- },
178
159
 
179
- // 加载该角色已分配的接口
180
- async apiRoleApiDetail() {
181
- if (!$Prop.rowData.id) return;
182
-
183
- try {
184
- const res = await $Http.post(
185
- "/addon/admin/role/apis",
186
- {
187
- roleCode: $Prop.rowData.code
188
- },
189
- {
190
- dropValues: [""]
191
- }
192
- );
160
+ groups.sort((a, b) => {
161
+ const at = typeof a.title === "string" ? a.title : "";
162
+ const bt = typeof b.title === "string" ? b.title : "";
163
+ return at.localeCompare(bt);
164
+ });
193
165
 
194
- $Data.checkedApiPaths = res.data.apiPaths || [];
195
- } catch (error) {
196
- MessagePlugin.error("加载数据失败");
197
- }
198
- },
166
+ $Data.apiData = groups as never;
167
+ } catch (error) {
168
+ MessagePlugin.error("加载接口失败");
169
+ }
170
+ }
199
171
 
200
- // 搜索过滤
201
- onSearch() {
202
- if (!$Data.searchText) {
203
- $Data.filteredApiData = $Data.apiData;
204
- return;
205
- }
172
+ async function apiRoleApiDetail(): Promise<void> {
173
+ if (!$Prop.rowData.id) return;
174
+
175
+ try {
176
+ const res = await $Http.post(
177
+ "/addon/admin/role/apis",
178
+ {
179
+ roleCode: $Prop.rowData.code
180
+ },
181
+ {
182
+ dropValues: [""]
183
+ }
184
+ );
206
185
 
207
- const searchLower = $Data.searchText.toLowerCase();
208
- $Data.filteredApiData = $Data.apiData
209
- .map((group) => {
210
- const apis = group.apis.filter((api) => {
211
- const label = api && typeof api.label === "string" ? api.label : "";
212
- const name = api && typeof api.name === "string" ? api.name : "";
213
- const path = api && typeof api.path === "string" ? api.path : "";
214
- return label.toLowerCase().includes(searchLower) || name.toLowerCase().includes(searchLower) || path.toLowerCase().includes(searchLower);
215
- });
216
- return {
217
- name: group.name,
218
- title: group.title,
219
- apis: apis
220
- };
221
- })
222
- .filter((group) => group.apis.length > 0);
223
- },
186
+ const resRecord = res && typeof res === "object" ? (res as Record<string, unknown>) : null;
187
+ const resData = resRecord && resRecord["data"] && typeof resRecord["data"] === "object" ? (resRecord["data"] as Record<string, unknown>) : null;
188
+ $Data.checkedApiPaths = resData && Array.isArray(resData["apiPaths"]) ? (resData["apiPaths"] as never) : ([] as never);
189
+ } catch (error) {
190
+ MessagePlugin.error("加载数据失败");
191
+ }
192
+ }
224
193
 
225
- // 提交表单
226
- async onSubmit() {
227
- try {
228
- $Data.submitting = true;
194
+ function onSearch(): void {
195
+ if (!$Data.searchText) {
196
+ $Data.filteredApiData = $Data.apiData;
197
+ return;
198
+ }
229
199
 
230
- const res = await $Http.post("/addon/admin/role/apiSave", {
231
- roleCode: $Prop.rowData.code,
232
- apiPaths: $Data.checkedApiPaths
200
+ const searchLower = String($Data.searchText).toLowerCase();
201
+ $Data.filteredApiData = ($Data.apiData as unknown[])
202
+ .map((group) => {
203
+ const groupRecord = group && typeof group === "object" ? (group as Record<string, unknown>) : null;
204
+ const groupApis = groupRecord && Array.isArray(groupRecord["apis"]) ? (groupRecord["apis"] as unknown[]) : [];
205
+ const apis = groupApis.filter((api) => {
206
+ const apiRecord = api && typeof api === "object" ? (api as Record<string, unknown>) : null;
207
+ const label = apiRecord && typeof apiRecord["label"] === "string" ? String(apiRecord["label"]) : "";
208
+ const name = apiRecord && typeof apiRecord["name"] === "string" ? String(apiRecord["name"]) : "";
209
+ const path = apiRecord && typeof apiRecord["path"] === "string" ? String(apiRecord["path"]) : "";
210
+ return label.toLowerCase().includes(searchLower) || name.toLowerCase().includes(searchLower) || path.toLowerCase().includes(searchLower);
233
211
  });
212
+ return {
213
+ name: groupRecord && typeof groupRecord["name"] === "string" ? String(groupRecord["name"]) : "",
214
+ title: groupRecord && typeof groupRecord["title"] === "string" ? String(groupRecord["title"]) : "",
215
+ apis: apis
216
+ };
217
+ })
218
+ .filter((group) => group.apis.length > 0) as never;
219
+ }
220
+
221
+ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
222
+ try {
223
+ $Data.submitting = true;
234
224
 
235
- if (res.code === 0) {
236
- MessagePlugin.success("保存成功");
237
- $Data.visible = false;
238
- $Emit("success");
239
- } else {
240
- MessagePlugin.error(res.msg || "保存失败");
225
+ const res = await $Http.post("/addon/admin/role/apiSave", {
226
+ roleCode: $Prop.rowData.code,
227
+ apiPaths: $Data.checkedApiPaths
228
+ });
229
+
230
+ if (res.code === 0) {
231
+ MessagePlugin.success("保存成功");
232
+ $Emit("success");
233
+ if (context && typeof context.close === "function") {
234
+ context.close();
241
235
  }
242
- } catch (error) {
243
- MessagePlugin.error("保存失败");
244
- } finally {
245
- $Data.submitting = false;
236
+ } else {
237
+ MessagePlugin.error(res.msg || "保存失败");
246
238
  }
239
+ } catch (error) {
240
+ MessagePlugin.error("保存失败");
241
+ } finally {
242
+ $Data.submitting = false;
247
243
  }
248
- };
244
+ }
249
245
 
250
- $Method.initData();
246
+ initData();
251
247
  </script>
252
248
 
253
249
  <style scoped lang="scss">
@@ -298,7 +294,7 @@ $Method.initData();
298
294
 
299
295
  .group-header {
300
296
  padding: 12px 16px;
301
- background-color: var(--bg-color-hover);
297
+ background-color: var(--primary-color-light);
302
298
  font-weight: 500;
303
299
  font-size: var(--font-size-sm);
304
300
  color: var(--text-primary);
@@ -308,11 +304,11 @@ $Method.initData();
308
304
 
309
305
  &::before {
310
306
  content: "";
311
- width: 8px;
312
- height: 8px;
307
+ width: 12px;
308
+ height: 12px;
313
309
  border-radius: 50%;
314
310
  background-color: var(--primary-color);
315
- opacity: 0.3;
311
+ box-shadow: 0 0 0 2px var(--bg-color-container);
316
312
  flex-shrink: 0;
317
313
  }
318
314
  }