@befly-addon/admin 1.1.34 → 1.2.0

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/apis/admin/cacheRefresh.ts +2 -2
  2. package/apis/menu/all.ts +8 -9
  3. package/apis/menu/list.ts +1 -2
  4. package/apis/role/apiSave.ts +3 -3
  5. package/apis/role/apis.ts +3 -3
  6. package/apis/role/menuSave.ts +2 -2
  7. package/apis/role/menus.ts +3 -3
  8. package/package.json +21 -21
  9. package/plugins/email.ts +3 -8
  10. package/tables/api.json +1 -20
  11. package/tables/menu.json +6 -11
  12. package/tables/role.json +4 -4
  13. package/views/403_1/index.vue +0 -75
  14. package/views/config/dict/components/edit.vue +0 -109
  15. package/views/config/dict/index.vue +0 -266
  16. package/views/config/dictType/components/edit.vue +0 -100
  17. package/views/config/dictType/index.vue +0 -244
  18. package/views/config/index.vue +0 -12
  19. package/views/config/system/components/edit.vue +0 -171
  20. package/views/config/system/index.vue +0 -286
  21. package/views/index/components/addonList.vue +0 -132
  22. package/views/index/components/environmentInfo.vue +0 -100
  23. package/views/index/components/operationLogs.vue +0 -112
  24. package/views/index/components/performanceMetrics.vue +0 -145
  25. package/views/index/components/quickActions.vue +0 -30
  26. package/views/index/components/serviceStatus.vue +0 -192
  27. package/views/index/components/systemNotifications.vue +0 -137
  28. package/views/index/components/systemOverview.vue +0 -190
  29. package/views/index/components/systemResources.vue +0 -111
  30. package/views/index/components/userInfo.vue +0 -204
  31. package/views/index/index.vue +0 -74
  32. package/views/log/email/index.vue +0 -292
  33. package/views/log/index.vue +0 -12
  34. package/views/log/login/index.vue +0 -187
  35. package/views/log/operate/index.vue +0 -249
  36. package/views/login_1/index.vue +0 -415
  37. package/views/people/admin/components/edit.vue +0 -168
  38. package/views/people/admin/index.vue +0 -240
  39. package/views/people/index.vue +0 -12
  40. package/views/permission/api/index.vue +0 -149
  41. package/views/permission/index.vue +0 -12
  42. package/views/permission/menu/index.vue +0 -128
  43. package/views/permission/role/components/api.vue +0 -261
  44. package/views/permission/role/components/edit.vue +0 -142
  45. package/views/permission/role/components/menu.vue +0 -116
  46. package/views/permission/role/index.vue +0 -263
@@ -1,128 +0,0 @@
1
- <template>
2
- <div class="page-menu page-table">
3
- <div class="main-tool">
4
- <div class="left"></div>
5
- <div class="right">
6
- <TButton shape="circle" @click="$Method.handleRefresh">
7
- <template #icon>
8
- <ILucideRotateCw />
9
- </template>
10
- </TButton>
11
- </div>
12
- </div>
13
-
14
- <div class="main-content">
15
- <div class="main-table">
16
- <TTable
17
- :data="$Data.tableData"
18
- :columns="$Data.columns"
19
- :loading="$Data.loading"
20
- :active-row-keys="$Data.activeRowKeys"
21
- row-key="id"
22
- height="calc(100vh - var(--search-height) - var(--layout-gap) * 2)"
23
- active-row-type="single"
24
- :tree="{ childrenKey: 'children', treeNodeColumnIndex: 0, defaultExpandAll: true }"
25
- @active-change="$Method.onActiveChange"
26
- >
27
- <template #state="{ row }">
28
- <TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
29
- <TTag v-else-if="row.state === 2" shape="round" theme="warning" variant="light-outline">禁用</TTag>
30
- <TTag v-else-if="row.state === 0" shape="round" theme="danger" variant="light-outline">删除</TTag>
31
- </template>
32
- </TTable>
33
- </div>
34
-
35
- <div class="main-detail">
36
- <DetailPanel :data="$Data.currentRow" :fields="$Data.columns" />
37
- </div>
38
- </div>
39
- </div>
40
- </template>
41
-
42
- <script setup>
43
- import { Button as TButton, Table as TTable, Tag as TTag, MessagePlugin } from "tdesign-vue-next";
44
- import ILucideRotateCw from "~icons/lucide/rotate-cw";
45
- import DetailPanel from "@/components/DetailPanel.vue";
46
- import { $Http } from "@/plugins/http";
47
- import { arrayToTree } from "befly-vite/utils/arrayToTree";
48
- import { withDefaultColumns } from "befly-vite/utils/withDefaultColumns";
49
-
50
- definePage({
51
- meta: {
52
- title: "菜单列表",
53
- order: 2
54
- }
55
- });
56
-
57
- // 响应式数据
58
- const $Data = $ref({
59
- tableData: [],
60
- loading: false,
61
- columns: withDefaultColumns([
62
- { colKey: "name", title: "菜单名称" },
63
- { colKey: "id", title: "序号" },
64
- { colKey: "path", title: "路由路径" },
65
- { colKey: "icon", title: "图标" },
66
- { colKey: "sort", title: "排序" },
67
- { colKey: "state", title: "状态" }
68
- ]),
69
- currentRow: null,
70
- activeRowKeys: []
71
- });
72
-
73
- // 方法
74
- const $Method = {
75
- async initData() {
76
- await $Method.apiMenuList();
77
- },
78
-
79
- // 加载菜单列表(树形结构)
80
- async apiMenuList() {
81
- $Data.loading = true;
82
- try {
83
- const res = await $Http("/addon/admin/menu/all");
84
- const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
85
-
86
- // 构建树形结构
87
- $Data.tableData = arrayToTree(lists);
88
-
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;
101
- }
102
- },
103
-
104
- // 刷新
105
- handleRefresh() {
106
- $Method.apiMenuList();
107
- },
108
-
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
- }
120
- }
121
- };
122
-
123
- $Method.initData();
124
- </script>
125
-
126
- <style scoped lang="scss">
127
- // 样式继承自全局 page-table
128
- </style>
@@ -1,261 +0,0 @@
1
- <template>
2
- <TDialog v-model:visible="$Data.visible" title="接口权限" width="900px" :append-to-body="true" :show-footer="true" top="5vh" @close="$Method.onClose">
3
- <div class="comp-role-api">
4
- <!-- 搜索框 -->
5
- <div class="search-box">
6
- <TInput v-model="$Data.searchText" placeholder="搜索接口名称或路径" clearable @change="$Method.onSearch">
7
- <template #prefix-icon>
8
- <ILucideSearch />
9
- </template>
10
- </TInput>
11
- </div>
12
-
13
- <!-- 接口分组列表 -->
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.checkedApiIds">
19
- <TCheckbox v-for="api in group.apis" :key="api.id" :value="api.id">
20
- {{ api.label }}
21
- </TCheckbox>
22
- </TCheckboxGroup>
23
- </div>
24
- </div>
25
- </div>
26
- </div>
27
-
28
- <template #footer>
29
- <div class="dialog-footer">
30
- <t-space>
31
- <TButton theme="default" @click="$Method.onClose">取消</TButton>
32
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">保存</TButton>
33
- </t-space>
34
- </div>
35
- </template>
36
- </TDialog>
37
- </template>
38
-
39
- <script setup>
40
- import { Dialog as TDialog, Input as TInput, CheckboxGroup as TCheckboxGroup, Checkbox as TCheckbox, Button as TButton, MessagePlugin } from "tdesign-vue-next";
41
- import ILucideSearch from "~icons/lucide/search";
42
- import { $Http } from "@/plugins/http";
43
-
44
- const $Prop = defineProps({
45
- modelValue: {
46
- type: Boolean,
47
- default: false
48
- },
49
- rowData: {
50
- type: Object,
51
- default: () => ({})
52
- }
53
- });
54
-
55
- const $Emit = defineEmits(["update:modelValue", "success"]);
56
-
57
- const $Data = $ref({
58
- visible: false,
59
- submitting: false,
60
- apiData: [],
61
- filteredApiData: [],
62
- searchText: "",
63
- checkedApiIds: []
64
- });
65
-
66
- // 方法集合
67
- const $Method = {
68
- async initData() {
69
- $Method.onShow();
70
- await Promise.all([$Method.apiApiAll(), $Method.apiRoleApiDetail()]);
71
- $Data.filteredApiData = $Data.apiData;
72
- },
73
-
74
- onShow() {
75
- setTimeout(() => {
76
- $Data.visible = $Prop.modelValue;
77
- }, 100);
78
- },
79
-
80
- onClose() {
81
- $Data.visible = false;
82
- setTimeout(() => {
83
- $Emit("update:modelValue", false);
84
- }, 300);
85
- },
86
-
87
- // 加载所有接口
88
- async apiApiAll() {
89
- try {
90
- const res = await $Http("/addon/admin/api/all");
91
-
92
- // 将接口列表按 addonTitle 分组
93
- const apiMap = new Map();
94
-
95
- res.data.lists.forEach((api) => {
96
- const addonTitle = api.addonTitle || api.addonName || "项目接口";
97
- const addonName = api.addonName || "project";
98
-
99
- if (!apiMap.has(addonName)) {
100
- apiMap.set(addonName, {
101
- name: addonName,
102
- title: addonTitle,
103
- apis: []
104
- });
105
- }
106
-
107
- apiMap.get(addonName).apis.push({
108
- id: api.id,
109
- label: `${api.name}`,
110
- description: api.description
111
- });
112
- });
113
-
114
- $Data.apiData = Array.from(apiMap.values());
115
- } catch (error) {
116
- MessagePlugin.error("加载接口失败");
117
- }
118
- },
119
-
120
- // 加载该角色已分配的接口
121
- async apiRoleApiDetail() {
122
- if (!$Prop.rowData.id) return;
123
-
124
- try {
125
- const res = await $Http("/addon/admin/role/apis", {
126
- roleCode: $Prop.rowData.code
127
- });
128
-
129
- $Data.checkedApiIds = res.data.apiIds || [];
130
- } catch (error) {
131
- MessagePlugin.error("加载数据失败");
132
- }
133
- },
134
-
135
- // 搜索过滤
136
- onSearch() {
137
- if (!$Data.searchText) {
138
- $Data.filteredApiData = $Data.apiData;
139
- return;
140
- }
141
-
142
- const searchLower = $Data.searchText.toLowerCase();
143
- $Data.filteredApiData = $Data.apiData
144
- .map((group) => ({
145
- ...group,
146
- apis: group.apis.filter((api) => api.label.toLowerCase().includes(searchLower))
147
- }))
148
- .filter((group) => group.apis.length > 0);
149
- },
150
-
151
- // 提交表单
152
- async onSubmit() {
153
- try {
154
- $Data.submitting = true;
155
-
156
- const res = await $Http("/addon/admin/role/apiSave", {
157
- roleCode: $Prop.rowData.code,
158
- apiIds: $Data.checkedApiIds
159
- });
160
-
161
- if (res.code === 0) {
162
- MessagePlugin.success("保存成功");
163
- $Data.visible = false;
164
- $Emit("success");
165
- } else {
166
- MessagePlugin.error(res.msg || "保存失败");
167
- }
168
- } catch (error) {
169
- MessagePlugin.error("保存失败");
170
- } finally {
171
- $Data.submitting = false;
172
- }
173
- }
174
- };
175
-
176
- $Method.initData();
177
- </script>
178
-
179
- <style scoped lang="scss">
180
- .comp-role-api {
181
- height: 60vh;
182
- display: flex;
183
- flex-direction: column;
184
- gap: 12px;
185
-
186
- .search-box {
187
- }
188
-
189
- .api-container {
190
- flex: 1;
191
- overflow-y: auto;
192
-
193
- .api-group {
194
- margin-bottom: 16px;
195
- border: 1px solid var(--border-color);
196
- border-radius: var(--border-radius-small);
197
- overflow: hidden;
198
-
199
- .api-checkbox-list {
200
- padding: 10px;
201
- }
202
-
203
- &:last-child {
204
- margin-bottom: 0;
205
- }
206
-
207
- .group-header {
208
- padding: 12px 16px;
209
- background-color: var(--bg-color-hover);
210
- font-weight: 500;
211
- font-size: var(--font-size-sm);
212
- color: var(--text-primary);
213
- display: flex;
214
- align-items: center;
215
- gap: 8px;
216
-
217
- &::before {
218
- content: "";
219
- width: 8px;
220
- height: 8px;
221
- border-radius: 50%;
222
- background-color: var(--primary-color);
223
- opacity: 0.3;
224
- flex-shrink: 0;
225
- }
226
- }
227
-
228
- .api-item {
229
- padding: 8px 16px;
230
- cursor: pointer;
231
- transition: background-color 0.2s;
232
- background-color: var(--bg-color-container);
233
-
234
- :deep(.t-checkbox-group) {
235
- display: flex;
236
- flex-wrap: wrap;
237
- gap: 12px;
238
- width: 100%;
239
- }
240
-
241
- :deep(.t-checkbox) {
242
- flex: 0 0 calc(33.333% - 8px);
243
- margin: 0;
244
-
245
- .t-checkbox__label {
246
- white-space: nowrap;
247
- overflow: hidden;
248
- text-overflow: ellipsis;
249
- }
250
- }
251
- }
252
- }
253
- }
254
- }
255
-
256
- .dialog-footer {
257
- width: 100%;
258
- display: flex;
259
- justify-content: center;
260
- }
261
- </style>
@@ -1,142 +0,0 @@
1
- <template>
2
- <TDialog v-model:visible="$Data.visible" :header="$Prop.actionType === 'upd' ? '更新角色' : '添加角色'" width="600px" :append-to-body="true" :show-footer="true" :esc-closable="false" top="10vh" @close="$Method.onClose">
3
- <div class="comp-role-edit">
4
- <TForm :model="$Data.formData" label-width="120px" label-position="left" :rules="$Data2.formRules" :ref="(el) => ($From.form = el)">
5
- <TFormItem label="角色名称" prop="name">
6
- <TInput v-model="$Data.formData.name" placeholder="请输入角色名称" />
7
- </TFormItem>
8
- <TFormItem label="角色代码" prop="code">
9
- <TInput v-model="$Data.formData.code" placeholder="请输入角色代码,如:admin" />
10
- </TFormItem>
11
- <TFormItem label="角色描述" prop="description">
12
- <TInput v-model="$Data.formData.description" placeholder="请输入角色描述" />
13
- </TFormItem>
14
- <TFormItem label="排序" prop="sort">
15
- <TInputNumber v-model="$Data.formData.sort" :min="0" :max="9999" />
16
- </TFormItem>
17
- <TFormItem label="状态" prop="state">
18
- <TRadioGroup v-model="$Data.formData.state">
19
- <TRadio :value="1">正常</TRadio>
20
- <TRadio :value="2">禁用</TRadio>
21
- </TRadioGroup>
22
- </TFormItem>
23
- </TForm>
24
- </div>
25
- <template #footer>
26
- <TButton @click="$Method.onClose">取消</TButton>
27
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">确定</TButton>
28
- </template>
29
- </TDialog>
30
- </template>
31
-
32
- <script setup>
33
- import {
34
- //
35
- Dialog as TDialog,
36
- Form as TForm,
37
- FormItem as TFormItem,
38
- Input as TInput,
39
- InputNumber as TInputNumber,
40
- RadioGroup as TRadioGroup,
41
- Radio as TRadio,
42
- Button as TButton,
43
- MessagePlugin
44
- } from "tdesign-vue-next";
45
- import { fieldClear } from "befly-vite/utils/fieldClear";
46
- import { $Http } from "@/plugins/http";
47
-
48
- const $Prop = defineProps({
49
- modelValue: {
50
- type: Boolean,
51
- default: false
52
- },
53
- actionType: {
54
- type: String,
55
- default: "add"
56
- },
57
- rowData: {
58
- type: Object,
59
- default: {}
60
- }
61
- });
62
-
63
- const $Emit = defineEmits(["update:modelValue", "success"]);
64
-
65
- // 表单引用
66
- const $From = $shallowRef({
67
- form: null
68
- });
69
-
70
- const $Computed = {};
71
-
72
- const $Data = $ref({
73
- visible: false,
74
- submitting: false,
75
- formData: {
76
- id: 0,
77
- name: "",
78
- code: "",
79
- description: "",
80
- sort: 0,
81
- state: 1
82
- }
83
- });
84
-
85
- const $Data2 = $shallowRef({
86
- formRules: {
87
- name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
88
- code: [
89
- { required: true, message: "请输入角色代码", trigger: "blur" },
90
- { pattern: /^[a-zA-Z0-9_]+$/, message: "角色代码只能包含字母、数字和下划线", trigger: "blur" }
91
- ],
92
- sort: [{ type: "number", message: "排序必须是数字", trigger: "blur" }]
93
- }
94
- });
95
-
96
- // 方法集合
97
- const $Method = {
98
- async initData() {
99
- if ($Prop.actionType === "upd" && $Prop.rowData.id) {
100
- $Data.formData = Object.assign({}, $Prop.rowData);
101
- }
102
- $Method.onShow();
103
- },
104
- onShow() {
105
- setTimeout(() => {
106
- $Data.visible = $Prop.modelValue;
107
- }, 100);
108
- },
109
- // 关闭抽屉事件
110
- onClose() {
111
- $Data.visible = false;
112
- setTimeout(() => {
113
- $Emit("update:modelValue", false);
114
- }, 300);
115
- },
116
- async onSubmit() {
117
- try {
118
- const valid = await $From.form.validate();
119
- if (!valid) return;
120
-
121
- $Data.submitting = true;
122
- const formData = $Prop.actionType === "add" ? fieldClear($Data.formData, { omitKeys: ["id", "state"] }) : $Data.formData;
123
- const res = await $Http($Prop.actionType === "upd" ? "/addon/admin/role/upd" : "/addon/admin/role/ins", formData);
124
-
125
- MessagePlugin.success(res.msg);
126
- $Emit("success");
127
- $Method.onClose();
128
- } catch (error) {
129
- MessagePlugin.error(error.msg || "提交失败");
130
- } finally {
131
- $Data.submitting = false;
132
- }
133
- }
134
- };
135
-
136
- $Method.initData();
137
- </script>
138
-
139
- <style scoped lang="scss">
140
- .comp-role-edit {
141
- }
142
- </style>
@@ -1,116 +0,0 @@
1
- <template>
2
- <TDialog v-model:visible="$Data.visible" title="菜单权限" width="600px" :append-to-body="true" :show-footer="true" top="10vh" @close="$Method.onClose">
3
- <div class="comp-role-menu">
4
- <TTree v-model:value="$Data.menuTreeCheckedKeys" :data="$Data.menuTreeData" value-mode="all" :keys="{ value: 'id', label: 'name', children: 'children' }" checkable expand-all />
5
- </div>
6
- <template #footer>
7
- <TButton @click="$Method.onClose">取消</TButton>
8
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">保存</TButton>
9
- </template>
10
- </TDialog>
11
- </template>
12
-
13
- <script setup>
14
- import { Dialog as TDialog, Tree as TTree, Button as TButton, MessagePlugin } from "tdesign-vue-next";
15
- import { arrayToTree } from "befly-vite/utils/arrayToTree";
16
- import { $Http } from "@/plugins/http";
17
-
18
- const $Prop = defineProps({
19
- modelValue: {
20
- type: Boolean,
21
- default: false
22
- },
23
- rowData: {
24
- type: Object,
25
- default: () => ({})
26
- }
27
- });
28
-
29
- const $Emit = defineEmits(["update:modelValue", "success"]);
30
-
31
- const $Data = $ref({
32
- visible: false,
33
- submitting: false,
34
- menuTreeData: [],
35
- menuTreeCheckedKeys: []
36
- });
37
-
38
- // 方法集合
39
- const $Method = {
40
- async initData() {
41
- await Promise.all([$Method.apiMenuAll(), $Method.apiRoleMenuDetail()]);
42
- $Method.onShow();
43
- },
44
-
45
- onShow() {
46
- setTimeout(() => {
47
- $Data.visible = $Prop.modelValue;
48
- }, 100);
49
- },
50
-
51
- onClose() {
52
- $Data.visible = false;
53
- setTimeout(() => {
54
- $Emit("update:modelValue", false);
55
- }, 300);
56
- },
57
-
58
- // 加载菜单树(用于配置权限)
59
- async apiMenuAll() {
60
- try {
61
- const res = await $Http("/addon/admin/menu/all");
62
- const lists = Array.isArray(res?.data?.lists) ? res.data.lists : [];
63
- $Data.menuTreeData = arrayToTree(lists);
64
- } catch (error) {
65
- MessagePlugin.error("加载菜单失败");
66
- }
67
- },
68
-
69
- // 加载该角色已分配的菜单
70
- async apiRoleMenuDetail() {
71
- if (!$Prop.rowData.id) return;
72
-
73
- try {
74
- const res = await $Http("/addon/admin/role/menus", {
75
- roleCode: $Prop.rowData.code
76
- });
77
-
78
- // menus 返回的 data 直接就是菜单 ID 数组
79
- $Data.menuTreeCheckedKeys = Array.isArray(res.data) ? res.data : [];
80
- } catch (error) {
81
- MessagePlugin.error("加载数据失败");
82
- }
83
- },
84
-
85
- // 提交表单
86
- async onSubmit() {
87
- try {
88
- $Data.submitting = true;
89
-
90
- const res = await $Http("/addon/admin/role/menuSave", {
91
- roleCode: $Prop.rowData.code,
92
- menuIds: $Data.menuTreeCheckedKeys
93
- });
94
-
95
- if (res.code === 0) {
96
- MessagePlugin.success("保存成功");
97
- $Data.visible = false;
98
- $Emit("success");
99
- } else {
100
- MessagePlugin.error(res.msg || "保存失败");
101
- }
102
- } catch (error) {
103
- MessagePlugin.error("保存失败");
104
- } finally {
105
- $Data.submitting = false;
106
- }
107
- }
108
- };
109
-
110
- $Method.initData();
111
- </script>
112
-
113
- <style scoped lang="scss">
114
- .comp-role-menu {
115
- }
116
- </style>