@befly-addon/admin 1.5.2 → 1.6.1

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.
@@ -75,7 +75,7 @@ const $Data = $ref({
75
75
  columns: withDefaultColumns([
76
76
  { colKey: "name", title: "接口名称" },
77
77
  { colKey: "auth", title: "登录" },
78
- { colKey: "routePath", title: "接口路径" },
78
+ { colKey: "path", title: "接口路径" },
79
79
  { colKey: "method", title: "请求方法" },
80
80
  { colKey: "addonName", title: "所属组件" }
81
81
  ]),
@@ -125,7 +125,7 @@ const $Method = {
125
125
  return;
126
126
  }
127
127
  const keyword = $Data.searchKeyword.toLowerCase();
128
- $Data.tableData = $Data.allData.filter((item) => item.name?.toLowerCase().includes(keyword) || item.routePath?.toLowerCase().includes(keyword));
128
+ $Data.tableData = $Data.allData.filter((item) => item.name?.toLowerCase().includes(keyword) || item.path?.toLowerCase().includes(keyword));
129
129
  },
130
130
 
131
131
  // 高亮行变化
@@ -12,10 +12,10 @@
12
12
 
13
13
  <!-- 接口分组列表 -->
14
14
  <div class="api-container">
15
- <div class="api-group" v-for="group in $Data.filteredApiData" :key="group.name">
16
- <div class="group-header">{{ group.title }}</div>
17
- <div class="api-checkbox-list">
18
- <TCheckboxGroup v-model="$Data.checkedApiPaths">
15
+ <TCheckboxGroup v-model="$Data.checkedApiPaths">
16
+ <div class="api-group" v-for="group in $Data.filteredApiData" :key="group.name">
17
+ <div class="group-header">{{ group.title }}</div>
18
+ <div class="api-checkbox-list">
19
19
  <TCheckbox v-for="api in group.apis" :key="api.value" :value="api.value">
20
20
  <div class="api-checkbox-label">
21
21
  <div class="api-label-main">
@@ -23,25 +23,25 @@
23
23
  </div>
24
24
  </div>
25
25
  </TCheckbox>
26
- </TCheckboxGroup>
26
+ </div>
27
27
  </div>
28
- </div>
28
+ </TCheckboxGroup>
29
29
  </div>
30
30
  </div>
31
31
 
32
32
  <template #footer>
33
33
  <div class="dialog-footer">
34
- <t-space>
34
+ <TSpace>
35
35
  <TButton theme="default" @click="$Method.onClose">取消</TButton>
36
36
  <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">保存</TButton>
37
- </t-space>
37
+ </TSpace>
38
38
  </div>
39
39
  </template>
40
40
  </TDialog>
41
41
  </template>
42
42
 
43
43
  <script setup lang="ts">
44
- import { Dialog as TDialog, Input as TInput, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Button as TButton, MessagePlugin } from "tdesign-vue-next";
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";
45
45
  import ILucideSearch from "~icons/lucide/search";
46
46
  import { $Http } from "@/plugins/http";
47
47
 
@@ -85,7 +85,8 @@ const $Method = {
85
85
  const apis = group && group.apis;
86
86
  const list = Array.isArray(apis) ? apis : [];
87
87
  for (const api of list) {
88
- if (api && api.auth === 0 && typeof api.value === "string" && api.value) {
88
+ const isPublic = api && (api.auth === 0 || api.auth === "0" || api.auth === false);
89
+ if (isPublic && typeof api.value === "string" && api.value) {
89
90
  merged.add(api.value);
90
91
  }
91
92
  }
@@ -113,32 +114,54 @@ const $Method = {
113
114
  try {
114
115
  const res = await $Http("/addon/admin/api/all");
115
116
 
116
- // 将接口列表按 addonTitle 分组
117
+ // 将接口列表按 parentPath 分组展示(routePath 已迁移为 path)
117
118
  const apiMap = new Map();
118
119
 
119
- res.data.lists.forEach((api) => {
120
- const addonTitle = api.addonTitle || api.addonName || "项目接口";
121
- const addonName = api.addonName || "app";
120
+ const lists = res && res.data && Array.isArray(res.data.lists) ? res.data.lists : [];
121
+ for (const api of lists) {
122
+ const apiPath = api && typeof api.path === "string" ? api.path : "";
123
+ if (!apiPath) {
124
+ continue;
125
+ }
126
+
127
+ const parentPath = api && typeof api.parentPath === "string" ? api.parentPath : "";
128
+ const groupKey = parentPath || "(未分组)";
122
129
 
123
- if (!apiMap.has(addonName)) {
124
- apiMap.set(addonName, {
125
- name: addonName,
126
- title: addonTitle,
130
+ if (!apiMap.has(groupKey)) {
131
+ apiMap.set(groupKey, {
132
+ name: groupKey,
133
+ title: groupKey,
127
134
  apis: []
128
135
  });
129
136
  }
130
137
 
131
- apiMap.get(addonName).apis.push({
132
- value: api.routePath,
133
- name: api.name || "",
134
- path: api.routePath || "",
135
- label: `${api.name || ""} ${api.routePath ? `(${api.routePath})` : ""}`.trim(),
136
- description: api.description,
137
- auth: api.auth
138
+ apiMap.get(groupKey).apis.push({
139
+ value: apiPath,
140
+ name: (api && typeof api.name === "string" ? api.name : "") || apiPath,
141
+ path: apiPath,
142
+ label: `${(api && typeof api.name === "string" ? api.name : "") || ""} ${apiPath ? `(${apiPath})` : ""}`.trim(),
143
+ description: api ? api.description : undefined,
144
+ auth: api ? api.auth : undefined,
145
+ parentPath: parentPath
146
+ });
147
+ }
148
+
149
+ const groups = Array.from(apiMap.values());
150
+ for (const group of groups) {
151
+ group.apis.sort((a, b) => {
152
+ const ap = typeof a.path === "string" ? a.path : "";
153
+ const bp = typeof b.path === "string" ? b.path : "";
154
+ return ap.localeCompare(bp);
138
155
  });
156
+ }
157
+
158
+ groups.sort((a, b) => {
159
+ const at = typeof a.title === "string" ? a.title : "";
160
+ const bt = typeof b.title === "string" ? b.title : "";
161
+ return at.localeCompare(bt);
139
162
  });
140
163
 
141
- $Data.apiData = Array.from(apiMap.values());
164
+ $Data.apiData = groups;
142
165
  } catch (error) {
143
166
  MessagePlugin.error("加载接口失败");
144
167
  }
@@ -169,7 +192,12 @@ const $Method = {
169
192
  const searchLower = $Data.searchText.toLowerCase();
170
193
  $Data.filteredApiData = $Data.apiData
171
194
  .map((group) => {
172
- const apis = group.apis.filter((api) => api.label.toLowerCase().includes(searchLower));
195
+ const apis = group.apis.filter((api) => {
196
+ const label = api && typeof api.label === "string" ? api.label : "";
197
+ const name = api && typeof api.name === "string" ? api.name : "";
198
+ const path = api && typeof api.path === "string" ? api.path : "";
199
+ return label.toLowerCase().includes(searchLower) || name.toLowerCase().includes(searchLower) || path.toLowerCase().includes(searchLower);
200
+ });
173
201
  return {
174
202
  name: group.name,
175
203
  title: group.title,
@@ -218,6 +246,12 @@ $Method.initData();
218
246
  flex: 1;
219
247
  overflow-y: auto;
220
248
 
249
+ /* CheckboxGroup 默认可能是 inline 布局,容易被内容撑开;这里强制占满容器宽度 */
250
+ :deep(.t-checkbox-group) {
251
+ display: block;
252
+ width: 100%;
253
+ }
254
+
221
255
  .api-group {
222
256
  margin-bottom: 16px;
223
257
  border: 1px solid var(--border-color);
@@ -225,7 +259,22 @@ $Method.initData();
225
259
  overflow: hidden;
226
260
 
227
261
  .api-checkbox-list {
228
- padding: 10px;
262
+ padding: 12px 16px;
263
+ display: flex;
264
+ flex-wrap: wrap;
265
+ gap: 12px;
266
+
267
+ :deep(.t-checkbox) {
268
+ margin: 0;
269
+ flex: 0 0 calc(33.333% - 8px);
270
+ min-width: 0;
271
+
272
+ .t-checkbox__label {
273
+ white-space: nowrap;
274
+ overflow: hidden;
275
+ text-overflow: ellipsis;
276
+ }
277
+ }
229
278
  }
230
279
 
231
280
  &:last-child {
@@ -135,8 +135,3 @@ const $Method = {
135
135
 
136
136
  $Method.initData();
137
137
  </script>
138
-
139
- <style scoped lang="scss">
140
- .comp-role-edit {
141
- }
142
- </style>
package/apis/admin/ins.ts CHANGED
@@ -6,7 +6,7 @@ export default {
6
6
  required: ["username", "password", "roleCode"],
7
7
  handler: async (befly, ctx) => {
8
8
  // 检查用户名是否已存在
9
- const existingByUsername = await befly.db.getOne({
9
+ const existingByUsername = await befly.db.getOne<{ id: number }>({
10
10
  table: "addon_admin_admin",
11
11
  where: { username: ctx.body.username }
12
12
  });
@@ -17,7 +17,7 @@ export default {
17
17
 
18
18
  // 检查昵称是否已存在
19
19
  if (ctx.body.nickname) {
20
- const existingByNickname = await befly.db.getOne({
20
+ const existingByNickname = await befly.db.getOne<{ id: number }>({
21
21
  table: "addon_admin_admin",
22
22
  where: { nickname: ctx.body.nickname }
23
23
  });
@@ -28,7 +28,7 @@ export default {
28
28
  }
29
29
 
30
30
  // 查询角色信息
31
- const roleData = await befly.db.getOne({
31
+ const roleData = await befly.db.getOne<{ id: number }>({
32
32
  table: "addon_admin_role",
33
33
  where: { code: ctx.body.roleCode }
34
34
  });
package/apis/admin/upd.ts CHANGED
@@ -8,7 +8,12 @@ export default {
8
8
  const { id, username, nickname, roleCode, ...updateData } = ctx.body;
9
9
 
10
10
  // 检查管理员是否存在
11
- const admin = await befly.db.getOne({
11
+ const admin = await befly.db.getOne<{
12
+ id: number;
13
+ username?: string;
14
+ nickname?: string;
15
+ roleCode?: string;
16
+ }>({
12
17
  table: "addon_admin_admin",
13
18
  where: { id }
14
19
  });
@@ -19,7 +24,7 @@ export default {
19
24
 
20
25
  // 检查用户名是否已被其他管理员使用
21
26
  if (username && username !== admin.data.username) {
22
- const existingUsername = await befly.db.getOne({
27
+ const existingUsername = await befly.db.getOne<{ id: number }>({
23
28
  table: "addon_admin_admin",
24
29
  where: { username, id: { $ne: id } }
25
30
  });
@@ -30,7 +35,7 @@ export default {
30
35
 
31
36
  // 检查昵称是否已被其他管理员使用
32
37
  if (nickname && nickname !== admin.data.nickname) {
33
- const existingNickname = await befly.db.getOne({
38
+ const existingNickname = await befly.db.getOne<{ id: number }>({
34
39
  table: "addon_admin_admin",
35
40
  where: { nickname, id: { $ne: id } }
36
41
  });
@@ -41,7 +46,7 @@ export default {
41
46
 
42
47
  // 检查角色是否存在
43
48
  if (roleCode && roleCode !== admin.data.roleCode) {
44
- const role = await befly.db.getOne({
49
+ const role = await befly.db.getOne<{ id: number }>({
45
50
  table: "addon_admin_role",
46
51
  where: { code: roleCode }
47
52
  });
@@ -51,7 +56,7 @@ export default {
51
56
  }
52
57
 
53
58
  // 构建更新数据
54
- const dataToUpdate: Record<string, any> = { ...updateData };
59
+ const dataToUpdate: Record<string, unknown> = { ...updateData };
55
60
  if (username) dataToUpdate.username = username;
56
61
  if (nickname) dataToUpdate.nickname = nickname;
57
62
  if (roleCode) dataToUpdate.roleCode = roleCode;
package/apis/api/list.ts CHANGED
@@ -11,7 +11,7 @@ export default {
11
11
  const result = await befly.db.getList({
12
12
  table: "addon_admin_api",
13
13
  where: {
14
- $or: ctx.body.keyword ? [{ name$like: `%${ctx.body.keyword}%` }, { routePath$like: `%${ctx.body.keyword}%` }] : undefined
14
+ $or: ctx.body.keyword ? [{ name$like: `%${ctx.body.keyword}%` }, { path$like: `%${ctx.body.keyword}%` }] : undefined
15
15
  },
16
16
  orderBy: ["id#ASC"],
17
17
  page: ctx.body.page,
@@ -47,7 +47,7 @@ export default {
47
47
  };
48
48
 
49
49
  // 根据登录类型构建查询条件
50
- const whereCondition: Record<string, any> = {};
50
+ const whereCondition: Record<string, unknown> = {};
51
51
  if (ctx.body.loginType === "username") {
52
52
  whereCondition.username = ctx.body.account;
53
53
  } else if (ctx.body.loginType === "email") {
@@ -57,7 +57,15 @@ export default {
57
57
  }
58
58
 
59
59
  // 查询管理员
60
- const admin = await befly.db.getOne({
60
+ const admin = await befly.db.getOne<{
61
+ id: number;
62
+ username: string;
63
+ nickname?: string;
64
+ password: string;
65
+ state?: number;
66
+ roleCode?: string;
67
+ roleType?: string;
68
+ }>({
61
69
  table: "addon_admin_admin",
62
70
  where: whereCondition
63
71
  });
@@ -81,7 +89,7 @@ export default {
81
89
  await befly.db.insData({ table: "addon_admin_login_log", data: logData });
82
90
  return befly.tool.No("账号或密码错误");
83
91
  }
84
- } catch (error: any) {
92
+ } catch (error: unknown) {
85
93
  befly.logger.error({ err: error, msg: "密码验证失败" });
86
94
  logData.failReason = "密码格式错误";
87
95
  await befly.db.insData({ table: "addon_admin_login_log", data: logData });
package/apis/dict/ins.ts CHANGED
@@ -6,7 +6,7 @@ export default {
6
6
  required: ["typeCode", "key", "label"],
7
7
  handler: async (befly, ctx) => {
8
8
  // 验证 typeCode 是否存在
9
- const dictType = await befly.db.getOne({
9
+ const dictType = await befly.db.getOne<{ id: number }>({
10
10
  table: "addon_admin_dict_type",
11
11
  where: { code: ctx.body.typeCode }
12
12
  });
@@ -16,7 +16,7 @@ export default {
16
16
  }
17
17
 
18
18
  // 检查 typeCode+key 是否已存在
19
- const existing = await befly.db.getOne({
19
+ const existing = await befly.db.getOne<{ id: number }>({
20
20
  table: "addon_admin_dict",
21
21
  where: {
22
22
  typeCode: ctx.body.typeCode,
@@ -6,7 +6,7 @@ export default {
6
6
  required: ["typeCode"],
7
7
  handler: async (befly, ctx) => {
8
8
  // 验证 typeCode 是否存在
9
- const dictType = await befly.db.getOne({
9
+ const dictType = await befly.db.getOne<{ id: number }>({
10
10
  table: "addon_admin_dict_type",
11
11
  where: { code: ctx.body.typeCode }
12
12
  });
package/apis/dict/upd.ts CHANGED
@@ -12,7 +12,7 @@ export default {
12
12
 
13
13
  // 如果更新了 typeCode,验证其是否存在
14
14
  if (typeCode) {
15
- const dictType = await befly.db.getOne({
15
+ const dictType = await befly.db.getOne<{ id: number }>({
16
16
  table: "addon_admin_dict_type",
17
17
  where: { code: typeCode }
18
18
  });
@@ -24,7 +24,7 @@ export default {
24
24
 
25
25
  // 如果更新了 typeCode 或 key,检查唯一性
26
26
  if (typeCode || key) {
27
- const current = await befly.db.getOne({
27
+ const current = await befly.db.getOne<{ typeCode?: string; key?: string }>({
28
28
  table: "addon_admin_dict",
29
29
  where: { id: id }
30
30
  });
@@ -32,7 +32,7 @@ export default {
32
32
  const checkTypeCode = typeCode || current.data?.typeCode;
33
33
  const checkKey = key || current.data?.key;
34
34
 
35
- const existing = await befly.db.getOne({
35
+ const existing = await befly.db.getOne<{ id: number }>({
36
36
  table: "addon_admin_dict",
37
37
  where: {
38
38
  typeCode: checkTypeCode,
@@ -46,7 +46,7 @@ export default {
46
46
  }
47
47
  }
48
48
 
49
- const updateData: Record<string, any> = {};
49
+ const updateData: Record<string, unknown> = {};
50
50
  if (typeCode !== undefined) updateData.typeCode = typeCode;
51
51
  if (key !== undefined) updateData.key = key;
52
52
  if (label !== undefined) updateData.label = label;
@@ -5,7 +5,7 @@ export default {
5
5
  handler: async (befly, ctx) => {
6
6
  const { id } = ctx.body;
7
7
 
8
- const dictType = await befly.db.getOne({
8
+ const dictType = await befly.db.getOne<{ code?: string }>({
9
9
  table: "addon_admin_dict_type",
10
10
  where: { id: id }
11
11
  });
@@ -15,7 +15,7 @@ export default {
15
15
  }
16
16
 
17
17
  // 检查是否有字典项引用此类型
18
- const dictItems = await befly.db.getOne({
18
+ const dictItems = await befly.db.getOne<{ id: number }>({
19
19
  table: "addon_admin_dict",
20
20
  where: {
21
21
  typeCode: dictType.data.code
@@ -14,7 +14,7 @@ export default {
14
14
  user: config.user,
15
15
  pass: config.pass,
16
16
  fromName: config.fromName,
17
- configured: !!config.user
17
+ configured: Boolean(config.user)
18
18
  });
19
19
  }
20
20
  };
package/apis/menu/all.ts CHANGED
@@ -12,7 +12,7 @@ export default {
12
12
  handler: async (befly, ctx) => {
13
13
  try {
14
14
  // 2. 查询角色信息获取菜单权限(使用 roleCode 而非 roleId)
15
- const role = await befly.db.getOne({
15
+ const role = await befly.db.getOne<{ id: number; menus?: unknown }>({
16
16
  table: "addon_admin_role",
17
17
  where: { code: ctx.user.roleCode }
18
18
  });
@@ -23,7 +23,7 @@ export default {
23
23
 
24
24
  // 3. 解析菜单路径列表(menu.path 数组,array_text)
25
25
  const rawMenuPaths = Array.isArray(role.data.menus) ? role.data.menus : [];
26
- const menuPaths = rawMenuPaths.map((p: any) => (typeof p === "string" ? p.trim() : "")).filter((p: string) => p.length > 0);
26
+ const menuPaths = rawMenuPaths.map((p: unknown) => (typeof p === "string" ? p.trim() : "")).filter((p: string) => p.length > 0);
27
27
 
28
28
  if (menuPaths.length === 0) {
29
29
  return befly.tool.Yes("菜单为空", { lists: [] });
@@ -46,11 +46,17 @@ export default {
46
46
 
47
47
  // 5. 根据角色权限过滤菜单(按 menu.path)
48
48
  const menuPathSet = new Set<string>(menuPaths);
49
- const authorizedMenus = allMenus.filter((menu: any) => typeof menu?.path === "string" && menuPathSet.has(String(menu.path)));
49
+ const authorizedMenus = allMenus.filter((menu: unknown) => {
50
+ if (typeof menu !== "object" || menu === null) {
51
+ return false;
52
+ }
53
+ const path = (menu as { path?: unknown }).path;
54
+ return typeof path === "string" && menuPathSet.has(path);
55
+ });
50
56
 
51
57
  // 6. 返回一维数组(由前端构建树形结构)
52
58
  return befly.tool.Yes("获取菜单成功", { lists: authorizedMenus });
53
- } catch (error: any) {
59
+ } catch (error: unknown) {
54
60
  befly.logger.error({ err: error, msg: "获取用户菜单失败" });
55
61
  return befly.tool.No("获取菜单失败");
56
62
  }
@@ -11,12 +11,13 @@ export default {
11
11
  let apiPaths: string[] = [];
12
12
  try {
13
13
  apiPaths = normalizePathnameListInput(ctx.body.apiPaths, "apiPaths", true);
14
- } catch (error: any) {
15
- return befly.tool.No(`参数不合法:${error?.message || "未知错误"}`);
14
+ } catch (error: unknown) {
15
+ const msg = error instanceof Error ? error.message : "未知错误";
16
+ return befly.tool.No(`参数不合法:${msg}`);
16
17
  }
17
18
 
18
19
  // 查询角色是否存在
19
- const role = await befly.db.getOne({
20
+ const role = await befly.db.getOne<{ id: number; code: string }>({
20
21
  table: "addon_admin_role",
21
22
  where: { code: ctx.body.roleCode }
22
23
  });
@@ -5,7 +5,12 @@ export default {
5
5
  code: { name: "配置代码", type: "string", min: 1, max: 50 }
6
6
  },
7
7
  handler: async (befly, ctx) => {
8
- const config = await befly.db.getOne({
8
+ const config = await befly.db.getOne<{
9
+ id: number;
10
+ code: string;
11
+ value: string;
12
+ valueType: string;
13
+ }>({
9
14
  table: "addon_admin_sys_config",
10
15
  where: { code: ctx.body.code }
11
16
  });
@@ -6,7 +6,7 @@ export default {
6
6
  handler: async (befly, ctx) => {
7
7
  try {
8
8
  // 检查 code 是否已存在
9
- const existing = await befly.db.getOne({
9
+ const existing = await befly.db.getOne<{ id: number }>({
10
10
  table: "addon_admin_sys_config",
11
11
  where: { code: ctx.body.code }
12
12
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@befly-addon/admin",
3
- "version": "1.5.2",
4
- "gitHead": "990c7108a9a791b9816c447309ec586d08b96ab4",
3
+ "version": "1.6.1",
4
+ "gitHead": "f836a457a4b5936d566051b629ed80eb264d6bf8",
5
5
  "private": false,
6
6
  "description": "Befly - 管理后台功能组件",
7
7
  "keywords": [
package/tables/api.json CHANGED
@@ -14,13 +14,20 @@
14
14
  "default": 1,
15
15
  "detail": "0=免登录,1=需登录"
16
16
  },
17
- "routePath": {
17
+ "path": {
18
18
  "name": "接口路径",
19
19
  "type": "string",
20
20
  "min": 1,
21
21
  "max": 200,
22
22
  "index": true
23
23
  },
24
+ "parentPath": {
25
+ "name": "父级路径",
26
+ "type": "string",
27
+ "min": 1,
28
+ "max": 200,
29
+ "index": true
30
+ },
24
31
  "addonName": {
25
32
  "name": "所属插件",
26
33
  "type": "string",
@@ -38,7 +38,7 @@ export function fieldClear<T = any>(data: T | T[], options: FieldClearOptions =
38
38
  const value = obj[key];
39
39
 
40
40
  // 1. keepMap 优先
41
- if (keepMap && Object.prototype.hasOwnProperty.call(keepMap, key)) {
41
+ if (keepMap && Object.hasOwn(keepMap, key)) {
42
42
  if (Object.is(keepMap[key], value)) {
43
43
  result[key] = value;
44
44
  continue;