befly-admin-ui 1.10.0 → 1.10.2

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 (34) hide show
  1. package/components/detailPanel.vue +52 -54
  2. package/components/pageDialog.vue +131 -117
  3. package/components/pageTableDetail.vue +381 -398
  4. package/layouts/default.vue +212 -215
  5. package/package.json +2 -2
  6. package/views/config/dict/components/edit.vue +83 -60
  7. package/views/config/dict/index.vue +53 -68
  8. package/views/config/dictType/components/edit.vue +81 -57
  9. package/views/config/dictType/index.vue +39 -51
  10. package/views/config/system/components/edit.vue +97 -86
  11. package/views/config/system/index.vue +37 -51
  12. package/views/index/components/addonList.vue +14 -11
  13. package/views/index/components/environmentInfo.vue +25 -22
  14. package/views/index/components/operationLogs.vue +20 -16
  15. package/views/index/components/performanceMetrics.vue +24 -21
  16. package/views/index/components/serviceStatus.vue +26 -22
  17. package/views/index/components/systemNotifications.vue +24 -19
  18. package/views/index/components/systemOverview.vue +368 -385
  19. package/views/index/components/systemResources.vue +22 -19
  20. package/views/index/components/userInfo.vue +56 -56
  21. package/views/log/email/index.vue +99 -100
  22. package/views/log/error/index.vue +112 -130
  23. package/views/log/login/index.vue +17 -28
  24. package/views/log/operate/index.vue +33 -46
  25. package/views/login_1/index.vue +50 -32
  26. package/views/people/admin/components/edit.vue +1 -1
  27. package/views/people/admin/index.vue +28 -29
  28. package/views/permission/api/index.vue +46 -58
  29. package/views/permission/menu/index.vue +46 -52
  30. package/views/permission/role/components/api.vue +143 -144
  31. package/views/permission/role/components/edit.vue +74 -52
  32. package/views/permission/role/components/menu.vue +119 -121
  33. package/views/permission/role/index.vue +49 -63
  34. package/views/resource/gallery/index.vue +78 -88
@@ -9,22 +9,22 @@
9
9
  <div class="resource-compact-item">
10
10
  <div class="resource-compact-header">
11
11
  <span class="resource-label">CPU</span>
12
- <span class="resource-value">{{ systemResources.cpu.usage }}%</span>
13
- <span class="resource-desc">{{ systemResources.cpu.cores }}核心</span>
12
+ <span class="resource-value">{{ $Data.systemResources.cpu.usage }}%</span>
13
+ <span class="resource-desc">{{ $Data.systemResources.cpu.cores }}核心</span>
14
14
  </div>
15
15
  </div>
16
16
  <div class="resource-compact-item">
17
17
  <div class="resource-compact-header">
18
18
  <span class="resource-label">内存</span>
19
- <span class="resource-value">{{ systemResources.memory.percentage }}%</span>
20
- <span class="resource-desc">{{ systemResources.memory.used }}GB / {{ systemResources.memory.total }}GB</span>
19
+ <span class="resource-value">{{ $Data.systemResources.memory.percentage }}%</span>
20
+ <span class="resource-desc">{{ $Data.systemResources.memory.used }}GB / {{ $Data.systemResources.memory.total }}GB</span>
21
21
  </div>
22
22
  </div>
23
23
  <div class="resource-compact-item">
24
24
  <div class="resource-compact-header">
25
25
  <span class="resource-label">磁盘</span>
26
- <span class="resource-value">{{ systemResources.disk.percentage }}%</span>
27
- <span class="resource-desc">{{ systemResources.disk.used }}GB / {{ systemResources.disk.total }}GB</span>
26
+ <span class="resource-value">{{ $Data.systemResources.disk.percentage }}%</span>
27
+ <span class="resource-desc">{{ $Data.systemResources.disk.used }}GB / {{ $Data.systemResources.disk.total }}GB</span>
28
28
  </div>
29
29
  </div>
30
30
  </div>
@@ -35,26 +35,29 @@
35
35
  <script setup>
36
36
  import { reactive } from "vue";
37
37
  import { ChartIcon } from "tdesign-icons-vue-next";
38
- import { $Http } from "@/plugins/http";
38
+ import { $Http } from "@/plugins/http.js";
39
39
 
40
40
  // 组件内部数据
41
- const systemResources = reactive({
42
- cpu: { usage: 0, cores: 0 },
43
- memory: { used: 0, total: 0, percentage: 0 },
44
- disk: { used: 0, total: 0, percentage: 0 }
41
+ const $Data = reactive({
42
+ systemResources: {
43
+ cpu: { usage: 0, cores: 0 },
44
+ memory: { used: 0, total: 0, percentage: 0 },
45
+ disk: { used: 0, total: 0, percentage: 0 }
46
+ }
45
47
  });
46
48
 
47
- // 获取数据
48
- const fetchData = async () => {
49
- try {
50
- const res = await $Http("/core/dashboard/systemResources", {}, [""]);
51
- Object.assign(systemResources, res.data);
52
- } catch (_error) {
53
- // 静默失败:不阻断页面展示
49
+ const $Method = {
50
+ async fetchData() {
51
+ try {
52
+ const res = await $Http("/core/dashboard/systemResources", {}, [""]);
53
+ Object.assign($Data.systemResources, res.data);
54
+ } catch (_error) {
55
+ // 静默失败:不阻断页面展示
56
+ }
54
57
  }
55
58
  };
56
59
 
57
- fetchData();
60
+ $Method.fetchData();
58
61
  </script>
59
62
 
60
63
  <style scoped lang="scss">
@@ -22,13 +22,13 @@
22
22
  </div>
23
23
  <div v-if="$Data.userInfo.lastLoginTime" class="detail-item">
24
24
  <TimeIcon />
25
- <span>{{ formatTime($Data.userInfo.lastLoginTime) }}</span>
25
+ <span>{{ $Method.formatTime($Data.userInfo.lastLoginTime) }}</span>
26
26
  </div>
27
27
  </div>
28
28
 
29
29
  <!-- 仅 dev 角色显示刷新缓存按钮 -->
30
30
  <div v-if="$Data.userInfo.roleCode === 'dev'" class="user-actions">
31
- <TButton theme="primary" size="mini" :loading="$Data.refreshing" @click="handleRefreshCache">
31
+ <TButton theme="primary" size="mini" :loading="$Data.refreshing" @click="$Method.handleRefreshCache">
32
32
  <template #icon>
33
33
  <RefreshIcon />
34
34
  </template>
@@ -42,7 +42,7 @@
42
42
  import { reactive } from "vue";
43
43
  import { Button as TButton, MessagePlugin } from "tdesign-vue-next";
44
44
  import { CallIcon, MailIcon, RefreshIcon, TimeIcon, UserIcon } from "tdesign-icons-vue-next";
45
- import { $Http } from "@/plugins/http";
45
+ import { $Http } from "@/plugins/http.js";
46
46
 
47
47
  // 响应式数据
48
48
  const $Data = reactive({
@@ -50,65 +50,65 @@ const $Data = reactive({
50
50
  refreshing: false
51
51
  });
52
52
 
53
- async function fetchData() {
54
- try {
55
- const res = await $Http("/core/admin/detail", {}, [""]);
56
- Object.assign($Data.userInfo, res.data);
57
- } catch (error) {
58
- MessagePlugin.error(error.msg || error.message || "获取用户信息失败");
59
- }
60
- }
61
-
62
- async function handleRefreshCache() {
63
- try {
64
- $Data.refreshing = true;
65
- const result = await $Http("/core/admin/cacheRefresh");
66
-
67
- const apis = result.data.apis;
68
- const menus = result.data.menus;
69
- const roles = result.data.roles;
70
- const messages = [];
53
+ const $Method = {
54
+ async fetchData() {
55
+ try {
56
+ const res = await $Http("/core/admin/detail", {}, [""]);
57
+ Object.assign($Data.userInfo, res.data);
58
+ } catch (error) {
59
+ MessagePlugin.error(error.msg || error.message || "获取用户信息失败");
60
+ }
61
+ },
62
+ async handleRefreshCache() {
63
+ try {
64
+ $Data.refreshing = true;
65
+ const result = await $Http("/core/admin/cacheRefresh");
66
+
67
+ const apis = result.data.apis;
68
+ const menus = result.data.menus;
69
+ const roles = result.data.roles;
70
+ const messages = [];
71
+
72
+ if (apis && apis.success) {
73
+ messages.push(`接口缓存: ${apis.count} 个`);
74
+ }
75
+ if (menus && menus.success) {
76
+ messages.push(`菜单缓存: ${menus.count} 个`);
77
+ }
78
+ if (roles && roles.success) {
79
+ messages.push(`角色缓存: ${roles.count} 个`);
80
+ }
71
81
 
72
- if (apis && apis.success) {
73
- messages.push(`接口缓存: ${apis.count} 个`);
82
+ MessagePlugin.success(`缓存刷新成功!${messages.join(",")}`);
83
+ } catch (error) {
84
+ MessagePlugin.error(error.msg || error.message || "刷新缓存失败,请稍后重试");
85
+ } finally {
86
+ $Data.refreshing = false;
74
87
  }
75
- if (menus && menus.success) {
76
- messages.push(`菜单缓存: ${menus.count} 个`);
88
+ },
89
+ formatTime(timestamp) {
90
+ if (!timestamp) return "";
91
+ const date = new Date(Number(timestamp));
92
+ const now = new Date();
93
+ const diff = now.getTime() - date.getTime();
94
+
95
+ if (diff < 60000) {
96
+ return "刚刚";
77
97
  }
78
- if (roles && roles.success) {
79
- messages.push(`角色缓存: ${roles.count} 个`);
98
+ if (diff < 3600000) {
99
+ return `${Math.floor(diff / 60000)}分钟前`;
80
100
  }
81
-
82
- MessagePlugin.success(`缓存刷新成功!${messages.join(",")}`);
83
- } catch (error) {
84
- MessagePlugin.error(error.msg || error.message || "刷新缓存失败,请稍后重试");
85
- } finally {
86
- $Data.refreshing = false;
87
- }
88
- }
89
-
90
- function formatTime(timestamp) {
91
- if (!timestamp) return "";
92
- const date = new Date(Number(timestamp));
93
- const now = new Date();
94
- const diff = now.getTime() - date.getTime();
95
-
96
- if (diff < 60000) {
97
- return "刚刚";
98
- }
99
- if (diff < 3600000) {
100
- return `${Math.floor(diff / 60000)}分钟前`;
101
- }
102
- if (diff < 86400000) {
103
- return `${Math.floor(diff / 3600000)}小时前`;
104
- }
105
- if (diff < 604800000) {
106
- return `${Math.floor(diff / 86400000)}天前`;
101
+ if (diff < 86400000) {
102
+ return `${Math.floor(diff / 3600000)}小时前`;
103
+ }
104
+ if (diff < 604800000) {
105
+ return `${Math.floor(diff / 86400000)}天前`;
106
+ }
107
+ return `${date.getMonth() + 1}月${date.getDate()}日`;
107
108
  }
108
- return `${date.getMonth() + 1}月${date.getDate()}日`;
109
- }
109
+ };
110
110
 
111
- fetchData();
111
+ $Method.fetchData();
112
112
  </script>
113
113
 
114
114
  <style scoped lang="scss">
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <PageTableDetail class="page-email page-table" :columns="$Data.columns" :endpoints="$Data.endpoints" :table-slot-names="['sendResult', 'sendTime']">
3
3
  <template #toolLeft>
4
- <TButton theme="primary" @click="openSendDialog">
4
+ <TButton theme="primary" @click="$Method.openSendDialog">
5
5
  <template #icon>
6
6
  <SendIcon />
7
7
  </template>
8
8
  发送邮件
9
9
  </TButton>
10
- <TButton variant="outline" @click="onVerify">
10
+ <TButton variant="outline" @click="$Method.onVerify">
11
11
  <template #icon>
12
12
  <CheckCircleIcon />
13
13
  </template>
@@ -15,21 +15,13 @@
15
15
  </TButton>
16
16
  </template>
17
17
 
18
- <template #toolRight="scope">
19
- <TButton shape="circle" @click="onReload(scope.reload)">
20
- <template #icon>
21
- <RefreshIcon />
22
- </template>
23
- </TButton>
24
- </template>
25
-
26
18
  <template #sendResult="{ row }">
27
19
  <TTag v-if="row.sendResult === 1" shape="round" theme="success" variant="light-outline">成功</TTag>
28
20
  <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
29
21
  </template>
30
22
 
31
23
  <template #sendTime="{ row }">
32
- {{ formatTime(row.sendTime) }}
24
+ {{ $Method.formatTime(row.sendTime) }}
33
25
  </template>
34
26
 
35
27
  <template #detail="scope">
@@ -39,7 +31,7 @@
39
31
  <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
40
32
  </template>
41
33
  <template #sendTime="slotScope">
42
- {{ formatTime(slotScope.value) }}
34
+ {{ $Method.formatTime(slotScope.value) }}
43
35
  </template>
44
36
  <template #content="slotScope">
45
37
  <div class="email-content" v-html="slotScope.value"></div>
@@ -48,8 +40,17 @@
48
40
  </template>
49
41
 
50
42
  <template #dialogs="scope">
51
- <PageDialog v-model="$Data.sendDialogVisible" title="发送邮件" :confirm-loading="$Data.sending" @confirm="(context) => onSend(scope.reload, context)" @cancel="onCancelSend" @close="onCancelSend">
52
- <TForm ref="sendFormRef" :data="$Data.sendForm" :rules="$Data.sendRules" label-width="80px">
43
+ <PageDialog v-model="$Data.sendDialogVisible" title="发送邮件" :confirm-loading="$Data.sending" @confirm="(context) => $Method.onSend(scope.reload, context)" @cancel="$Method.onCancelSend" @close="$Method.onCancelSend">
44
+ <TForm
45
+ :ref="
46
+ (value) => {
47
+ $From.sendFormRef = value;
48
+ }
49
+ "
50
+ :data="$Data.sendForm"
51
+ :rules="$Const.sendRules"
52
+ label-width="80px"
53
+ >
53
54
  <TFormItem label="收件人" name="to">
54
55
  <TInput v-model="$Data.sendForm.to" placeholder="请输入收件人邮箱" />
55
56
  </TFormItem>
@@ -69,15 +70,34 @@
69
70
  </template>
70
71
 
71
72
  <script setup>
72
- import { reactive, ref } from "vue";
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";
74
- import { CheckCircleIcon, RefreshIcon, SendIcon } from "tdesign-icons-vue-next";
73
+ import { reactive } from "vue";
74
+ import { Button as TButton, Form as TForm, FormItem as TFormItem, Input as TInput, MessagePlugin, Tag as TTag, Textarea as TTextarea } from "tdesign-vue-next";
75
+ import { CheckCircleIcon, SendIcon } from "tdesign-icons-vue-next";
75
76
  import PageDialog from "befly-admin-ui/components/pageDialog.vue";
76
77
  import DetailPanel from "befly-admin-ui/components/detailPanel.vue";
77
- import { $Http } from "@/plugins/http";
78
+ import { $Http } from "@/plugins/http.js";
78
79
  import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
79
80
  import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
80
- const sendFormRef = ref(null);
81
+
82
+ const $Const = {
83
+ createSendForm() {
84
+ return {
85
+ to: "",
86
+ cc: "",
87
+ subject: "",
88
+ content: ""
89
+ };
90
+ },
91
+ sendRules: {
92
+ to: [{ required: true, message: "请输入收件人邮箱", trigger: "blur" }],
93
+ subject: [{ required: true, message: "请输入邮件主题", trigger: "blur" }],
94
+ content: [{ required: true, message: "请输入邮件内容", trigger: "blur" }]
95
+ }
96
+ };
97
+
98
+ const $From = {
99
+ sendFormRef: null
100
+ };
81
101
 
82
102
  // 响应式数据
83
103
  const $Data = reactive({
@@ -102,93 +122,72 @@ const $Data = reactive({
102
122
  },
103
123
  sendDialogVisible: false,
104
124
  sending: false,
105
- sendForm: {
106
- to: "",
107
- cc: "",
108
- subject: "",
109
- content: ""
110
- },
111
- sendRules: {
112
- to: [{ required: true, message: "请输入收件人邮箱", trigger: "blur" }],
113
- subject: [{ required: true, message: "请输入邮件主题", trigger: "blur" }],
114
- content: [{ required: true, message: "请输入邮件内容", trigger: "blur" }]
115
- }
125
+ sendForm: $Const.createSendForm()
116
126
  });
117
127
 
118
- function onReload(reload) {
119
- reload({ keepSelection: true });
120
- }
121
-
122
- function onCancelSend() {
123
- $Data.sendDialogVisible = false;
124
- }
125
-
126
- function openSendDialog() {
127
- $Data.sendForm = {
128
- to: "",
129
- cc: "",
130
- subject: "",
131
- content: ""
132
- };
133
- $Data.sendDialogVisible = true;
134
- }
135
-
136
- async function onSend(reload, context) {
137
- const form = sendFormRef.value;
138
- if (!form) {
139
- MessagePlugin.warning("表单未就绪");
140
- return;
141
- }
142
-
143
- const valid = await form.validate();
144
- if (valid !== true) return;
145
-
146
- $Data.sending = true;
147
- try {
148
- const res = await $Http("/core/email/send", {
149
- to: $Data.sendForm.to,
150
- subject: $Data.sendForm.subject,
151
- content: $Data.sendForm.content,
152
- cc: $Data.sendForm.cc || undefined,
153
- isHtml: true
154
- });
128
+ const $Method = {
129
+ onCancelSend() {
130
+ $Data.sendDialogVisible = false;
131
+ },
132
+ openSendDialog() {
133
+ $Data.sendForm = $Const.createSendForm();
134
+ $Data.sendDialogVisible = true;
135
+ },
136
+ async onSend(reload, context) {
137
+ const form = $From.sendFormRef;
138
+ if (!form) {
139
+ MessagePlugin.warning("表单未就绪");
140
+ return;
141
+ }
155
142
 
156
- MessagePlugin.success("发送成功");
157
- if (context && typeof context.close === "function") {
158
- context.close();
159
- } else {
160
- $Data.sendDialogVisible = false;
143
+ const valid = await form.validate();
144
+ if (valid !== true) return;
145
+
146
+ $Data.sending = true;
147
+ try {
148
+ await $Http("/core/email/send", {
149
+ to: $Data.sendForm.to,
150
+ subject: $Data.sendForm.subject,
151
+ content: $Data.sendForm.content,
152
+ cc: $Data.sendForm.cc || undefined,
153
+ isHtml: true
154
+ });
155
+
156
+ MessagePlugin.success("发送成功");
157
+ if (context && typeof context.close === "function") {
158
+ context.close();
159
+ } else {
160
+ $Data.sendDialogVisible = false;
161
+ }
162
+ if (reload) {
163
+ reload({ keepSelection: false, resetPage: true });
164
+ }
165
+ } catch (error) {
166
+ MessagePlugin.error(error.msg || error.message || "发送失败");
167
+ } finally {
168
+ $Data.sending = false;
161
169
  }
162
- if (reload) {
163
- reload({ keepSelection: false, resetPage: true });
170
+ },
171
+ async onVerify() {
172
+ try {
173
+ await $Http("/core/email/verify");
174
+ MessagePlugin.success("邮件服务配置正常");
175
+ } catch (error) {
176
+ MessagePlugin.error(error.msg || error.message || "验证失败");
164
177
  }
165
- } catch (error) {
166
- MessagePlugin.error(error.msg || error.message || "发送失败");
167
- } finally {
168
- $Data.sending = false;
169
- }
170
- }
171
-
172
- async function onVerify() {
173
- try {
174
- await $Http("/core/email/verify");
175
- MessagePlugin.success("邮件服务配置正常");
176
- } catch (error) {
177
- MessagePlugin.error(error.msg || error.message || "验证失败");
178
+ },
179
+ formatTime(timestamp) {
180
+ if (!timestamp) return "-";
181
+ const date = new Date(timestamp);
182
+ const year = date.getFullYear();
183
+ const month = String(date.getMonth() + 1).padStart(2, "0");
184
+ const day = String(date.getDate()).padStart(2, "0");
185
+ const hours = String(date.getHours()).padStart(2, "0");
186
+ const minutes = String(date.getMinutes()).padStart(2, "0");
187
+ const seconds = String(date.getSeconds()).padStart(2, "0");
188
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
178
189
  }
179
- }
180
-
181
- function formatTime(timestamp) {
182
- if (!timestamp) return "-";
183
- const date = new Date(timestamp);
184
- const year = date.getFullYear();
185
- const month = String(date.getMonth() + 1).padStart(2, "0");
186
- const day = String(date.getDate()).padStart(2, "0");
187
- const hours = String(date.getHours()).padStart(2, "0");
188
- const minutes = String(date.getMinutes()).padStart(2, "0");
189
- const seconds = String(date.getSeconds()).padStart(2, "0");
190
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
191
- }
190
+ };
192
191
  </script>
193
192
 
194
193
  <style scoped lang="scss">