befly-admin 3.0.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.
Files changed (57) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +4 -0
  3. package/LICENSE +201 -0
  4. package/README.md +143 -0
  5. package/index.html +13 -0
  6. package/libs/auto-routes-template.js +104 -0
  7. package/libs/autoRouter.ts +67 -0
  8. package/libs/icons.ts +543 -0
  9. package/package.json +42 -0
  10. package/public/logo.svg +106 -0
  11. package/src/App.vue +17 -0
  12. package/src/api/auth.ts +60 -0
  13. package/src/components/Icon.vue +41 -0
  14. package/src/env.d.ts +9 -0
  15. package/src/layouts/0.vue +207 -0
  16. package/src/layouts/1.vue +22 -0
  17. package/src/layouts/2.vue +166 -0
  18. package/src/main.ts +19 -0
  19. package/src/plugins/http.ts +94 -0
  20. package/src/plugins/router.ts +47 -0
  21. package/src/plugins/store.ts +19 -0
  22. package/src/styles/index.scss +198 -0
  23. package/src/styles/mixins.scss +98 -0
  24. package/src/styles/variables.scss +75 -0
  25. package/src/types/env.d.ts +23 -0
  26. package/src/util.ts +28 -0
  27. package/src/views/403/403.vue +33 -0
  28. package/src/views/admin/components/edit.vue +147 -0
  29. package/src/views/admin/components/role.vue +135 -0
  30. package/src/views/admin/index.vue +169 -0
  31. package/src/views/dict/components/edit.vue +156 -0
  32. package/src/views/dict/index.vue +159 -0
  33. package/src/views/index/components/AddonList.vue +125 -0
  34. package/src/views/index/components/EnvironmentInfo.vue +97 -0
  35. package/src/views/index/components/OperationLogs.vue +112 -0
  36. package/src/views/index/components/PerformanceMetrics.vue +148 -0
  37. package/src/views/index/components/QuickActions.vue +27 -0
  38. package/src/views/index/components/ServiceStatus.vue +193 -0
  39. package/src/views/index/components/SystemNotifications.vue +136 -0
  40. package/src/views/index/components/SystemOverview.vue +188 -0
  41. package/src/views/index/components/SystemResources.vue +104 -0
  42. package/src/views/index/components/UserInfo.vue +136 -0
  43. package/src/views/index/index.vue +62 -0
  44. package/src/views/login/index_1.vue +694 -0
  45. package/src/views/menu/components/edit.vue +150 -0
  46. package/src/views/menu/index.vue +168 -0
  47. package/src/views/news/detail/detail_2.vue +26 -0
  48. package/src/views/news/detail/index.vue +26 -0
  49. package/src/views/news/news.vue +26 -0
  50. package/src/views/role/components/api.vue +280 -0
  51. package/src/views/role/components/edit.vue +129 -0
  52. package/src/views/role/components/menu.vue +143 -0
  53. package/src/views/role/index.vue +179 -0
  54. package/src/views/user/user.vue +320 -0
  55. package/temp/router.js +71 -0
  56. package/tsconfig.json +34 -0
  57. package/vite.config.ts +100 -0
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <Icon name="Bell" :size="20" />
5
+ <h2>系统通知</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="notification-compact-list">
9
+ <div v-for="notification in notifications" :key="notification.id" class="notification-compact-item">
10
+ <div class="notification-icon" :class="`type-${notification.type}`">
11
+ <Icon :name="getNotificationIcon(notification.type)" :size="16" />
12
+ </div>
13
+ <div class="notification-content">
14
+ <span class="notification-title">{{ notification.title }}</span>
15
+ <span class="notification-time">{{ formatTime(notification.createdAt) }}</span>
16
+ </div>
17
+ <t-tag v-if="!notification.isRead" theme="primary" size="small" variant="light">新</t-tag>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup>
25
+ // 组件内部数据
26
+ const notifications = $ref([
27
+ { id: 1, type: 'warning', title: '系统更新提醒 - v1.1.0 版本已发布', isRead: false, createdAt: Date.now() - 3600000 },
28
+ { id: 2, type: 'info', title: '数据备份完成 - 今日凌晨自动备份成功', isRead: true, createdAt: Date.now() - 21600000 },
29
+ { id: 3, type: 'error', title: 'SSL证书即将过期 - 请及时更新证书', isRead: false, createdAt: Date.now() - 86400000 },
30
+ { id: 4, type: 'success', title: '性能优化完成 - 响应速度提升30%', isRead: true, createdAt: Date.now() - 172800000 }
31
+ ]);
32
+
33
+ const getNotificationIcon = (type) => {
34
+ const iconMap = {
35
+ info: 'Info',
36
+ success: 'CheckCircle',
37
+ warning: 'AlertTriangle',
38
+ error: 'XCircle'
39
+ };
40
+ return iconMap[type] || 'Bell';
41
+ };
42
+
43
+ const formatTime = (timestamp) => {
44
+ const date = new Date(timestamp);
45
+ const now = Date.now();
46
+ const diff = now - timestamp;
47
+
48
+ if (diff < 3600000) {
49
+ return `${Math.floor(diff / 60000)}分钟前`;
50
+ } else if (diff < 86400000) {
51
+ return `${Math.floor(diff / 3600000)}小时前`;
52
+ } else {
53
+ const month = String(date.getMonth() + 1).padStart(2, '0');
54
+ const day = String(date.getDate()).padStart(2, '0');
55
+ return `${month}-${day}`;
56
+ }
57
+ };
58
+ </script>
59
+
60
+ <style scoped lang="scss">
61
+ .notification-compact-list {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: $spacing-xs;
65
+
66
+ .notification-compact-item {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: $spacing-sm;
70
+ padding: $spacing-sm $spacing-md;
71
+ background: rgba($primary-color, 0.02);
72
+ border-radius: $border-radius-small;
73
+ border: 1px solid $border-color;
74
+ transition: all 0.2s ease;
75
+
76
+ &:hover {
77
+ background: rgba($primary-color, 0.05);
78
+ border-color: $primary-color;
79
+ }
80
+
81
+ .notification-icon {
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ width: 32px;
86
+ height: 32px;
87
+ border-radius: $border-radius-small;
88
+ flex-shrink: 0;
89
+
90
+ &.type-info {
91
+ background: rgba($primary-color, 0.1);
92
+ color: $primary-color;
93
+ }
94
+
95
+ &.type-success {
96
+ background: rgba($success-color, 0.1);
97
+ color: $success-color;
98
+ }
99
+
100
+ &.type-warning {
101
+ background: rgba($warning-color, 0.1);
102
+ color: $warning-color;
103
+ }
104
+
105
+ &.type-error {
106
+ background: rgba($error-color, 0.1);
107
+ color: $error-color;
108
+ }
109
+ }
110
+
111
+ .notification-content {
112
+ display: flex;
113
+ align-items: center;
114
+ gap: $spacing-sm;
115
+ flex: 1;
116
+ min-width: 0;
117
+
118
+ .notification-title {
119
+ font-size: 14px;
120
+ color: $text-primary;
121
+ font-weight: 500;
122
+ overflow: hidden;
123
+ text-overflow: ellipsis;
124
+ white-space: nowrap;
125
+ flex: 1;
126
+ }
127
+
128
+ .notification-time {
129
+ font-size: 14px;
130
+ color: $text-placeholder;
131
+ flex-shrink: 0;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ </style>
@@ -0,0 +1,188 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <Icon name="Info" :size="20" />
5
+ <h2>系统概览</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <t-row :gutter="12">
9
+ <t-col :xs="24" :sm="12" :md="12" :lg="12">
10
+ <div class="info-block">
11
+ <div class="stats-grid">
12
+ <div class="stat-box stat-primary">
13
+ <Icon name="Menu" :size="24" />
14
+ <div class="stat-content">
15
+ <div class="stat-value">{{ permissionStats.menuCount }}</div>
16
+ <div class="stat-label">菜单总数</div>
17
+ </div>
18
+ </div>
19
+ <div class="stat-box stat-success">
20
+ <Icon name="Webhook" :size="24" />
21
+ <div class="stat-content">
22
+ <div class="stat-value">{{ permissionStats.apiCount }}</div>
23
+ <div class="stat-label">接口总数</div>
24
+ </div>
25
+ </div>
26
+ <div class="stat-box stat-warning">
27
+ <Icon name="Users" :size="24" />
28
+ <div class="stat-content">
29
+ <div class="stat-value">{{ permissionStats.roleCount }}</div>
30
+ <div class="stat-label">角色总数</div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </t-col>
36
+ </t-row>
37
+ </div>
38
+ </div>
39
+ </template>
40
+
41
+ <script setup>
42
+ // 组件内部数据
43
+ const permissionStats = $ref({
44
+ menuCount: 0,
45
+ apiCount: 0,
46
+ roleCount: 0
47
+ });
48
+
49
+ // 获取数据
50
+ const fetchData = async () => {
51
+ try {
52
+ const { data } = await $Http('/addon/admin/dashboardSystemOverview');
53
+ Object.assign(permissionStats, data);
54
+ } catch (error) {
55
+ console.error('获取系统概览失败:', error);
56
+ }
57
+ };
58
+
59
+ fetchData();
60
+ </script>
61
+
62
+ <style scoped lang="scss">
63
+ .info-block {
64
+ background: transparent;
65
+ border: none;
66
+ padding: 0;
67
+ height: 100%;
68
+
69
+ .info-header {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 6px;
73
+ padding-bottom: 8px;
74
+ margin-bottom: 12px;
75
+ border-bottom: 2px solid $primary-color;
76
+
77
+ .info-title {
78
+ font-size: 14px;
79
+ font-weight: 600;
80
+ color: $text-primary;
81
+ }
82
+ }
83
+
84
+ .info-grid-compact {
85
+ display: grid;
86
+ grid-template-columns: repeat(3, 1fr);
87
+ gap: 10px;
88
+
89
+ .info-grid-item {
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ padding: 10px 12px;
94
+ background: rgba($primary-color, 0.02);
95
+ border-radius: $border-radius-small;
96
+ border: 1px solid $border-color;
97
+ transition: all 0.2s ease;
98
+
99
+ &:hover {
100
+ background: rgba($primary-color, 0.05);
101
+ border-color: $primary-color;
102
+ }
103
+
104
+ .label {
105
+ font-size: 14px;
106
+ color: $text-secondary;
107
+ font-weight: 500;
108
+ }
109
+
110
+ .value {
111
+ font-size: 14px;
112
+ color: $text-primary;
113
+ font-weight: 600;
114
+
115
+ &.highlight {
116
+ color: $primary-color;
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ .stats-grid {
124
+ display: grid;
125
+ grid-template-columns: repeat(3, 1fr);
126
+ gap: 10px;
127
+
128
+ .stat-box {
129
+ background: rgba($primary-color, 0.02);
130
+ border: 1px solid $border-color;
131
+ border-radius: 6px;
132
+ padding: 12px;
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 10px;
136
+ transition: all 0.3s;
137
+
138
+ &:hover {
139
+ background: rgba($primary-color, 0.05);
140
+ border-color: $primary-color;
141
+ transform: translateY(-2px);
142
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
143
+ }
144
+
145
+ .stat-content {
146
+ flex: 1;
147
+
148
+ .stat-value {
149
+ font-size: 20px;
150
+ font-weight: 700;
151
+ margin-bottom: 2px;
152
+ }
153
+
154
+ .stat-label {
155
+ font-size: 14px;
156
+ color: $text-secondary;
157
+ }
158
+ }
159
+
160
+ &.stat-primary {
161
+ border-color: $primary-color;
162
+ background: linear-gradient(135deg, rgba(0, 82, 217, 0.05), white);
163
+
164
+ .stat-value {
165
+ color: $primary-color;
166
+ }
167
+ }
168
+
169
+ &.stat-success {
170
+ border-color: $success-color;
171
+ background: linear-gradient(135deg, rgba(82, 196, 26, 0.05), white);
172
+
173
+ .stat-value {
174
+ color: $success-color;
175
+ }
176
+ }
177
+
178
+ &.stat-warning {
179
+ border-color: $warning-color;
180
+ background: linear-gradient(135deg, rgba(250, 173, 20, 0.05), white);
181
+
182
+ .stat-value {
183
+ color: $warning-color;
184
+ }
185
+ }
186
+ }
187
+ }
188
+ </style>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <Icon name="Activity" :size="20" />
5
+ <h2>系统资源</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="resource-compact-list">
9
+ <div class="resource-compact-item">
10
+ <div class="resource-compact-header">
11
+ <Icon name="Cpu" :size="16" />
12
+ <span class="resource-label">CPU</span>
13
+ <span class="resource-value">{{ systemResources.cpu.usage }}%</span>
14
+ <span class="resource-desc">{{ systemResources.cpu.cores }}核心</span>
15
+ </div>
16
+ <t-progress :percentage="systemResources.cpu.usage" :theme="getProgressColor(systemResources.cpu.usage)" size="small" />
17
+ </div>
18
+ <div class="resource-compact-item">
19
+ <div class="resource-compact-header">
20
+ <Icon name="HardDrive" :size="16" />
21
+ <span class="resource-label">内存</span>
22
+ <span class="resource-value">{{ systemResources.memory.percentage }}%</span>
23
+ <span class="resource-desc">{{ systemResources.memory.used }}GB / {{ systemResources.memory.total }}GB</span>
24
+ </div>
25
+ <t-progress :percentage="systemResources.memory.percentage" :theme="getProgressColor(systemResources.memory.percentage)" size="small" />
26
+ </div>
27
+ <div class="resource-compact-item">
28
+ <div class="resource-compact-header">
29
+ <Icon name="Disc" :size="16" />
30
+ <span class="resource-label">磁盘</span>
31
+ <span class="resource-value">{{ systemResources.disk.percentage }}%</span>
32
+ <span class="resource-desc">{{ systemResources.disk.used }}GB / {{ systemResources.disk.total }}GB</span>
33
+ </div>
34
+ <t-progress :percentage="systemResources.disk.percentage" :theme="getProgressColor(systemResources.disk.percentage)" size="small" />
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </template>
40
+
41
+ <script setup>
42
+ // 组件内部数据
43
+ const systemResources = $ref({
44
+ cpu: { usage: 0, cores: 0 },
45
+ memory: { used: 0, total: 0, percentage: 0 },
46
+ disk: { used: 0, total: 0, percentage: 0 }
47
+ });
48
+
49
+ // 获取数据
50
+ const fetchData = async () => {
51
+ try {
52
+ const { data } = await $Http('/addon/admin/dashboardSystemResources');
53
+ Object.assign(systemResources, data);
54
+ } catch (error) {
55
+ console.error('获取系统资源失败:', error);
56
+ }
57
+ };
58
+
59
+ fetchData();
60
+
61
+ // 工具函数
62
+ const getProgressColor = (percentage) => {
63
+ if (percentage < 50) return 'success';
64
+ if (percentage < 80) return 'warning';
65
+ return 'danger';
66
+ };
67
+ </script>
68
+
69
+ <style scoped lang="scss">
70
+ .resource-compact-list {
71
+ display: grid;
72
+ grid-template-columns: repeat(3, 1fr);
73
+ gap: $spacing-md;
74
+
75
+ .resource-compact-item {
76
+ .resource-compact-header {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 10px;
80
+ margin-bottom: 8px;
81
+
82
+ .resource-label {
83
+ font-size: 14px;
84
+ font-weight: 600;
85
+ color: $text-secondary;
86
+ min-width: 50px;
87
+ }
88
+
89
+ .resource-value {
90
+ font-size: 16px;
91
+ font-weight: 700;
92
+ color: $primary-color;
93
+ min-width: 60px;
94
+ }
95
+
96
+ .resource-desc {
97
+ font-size: 14px;
98
+ color: $text-placeholder;
99
+ flex: 1;
100
+ }
101
+ }
102
+ }
103
+ }
104
+ </style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div class="section-block user-info-card">
3
+ <div class="user-header">
4
+ <div class="user-avatar">
5
+ <Icon name="User" :size="32" />
6
+ </div>
7
+ <div class="user-basic">
8
+ <div class="user-name">{{ userInfo.nickname || userInfo.name || userInfo.username || '未设置' }}</div>
9
+ <div class="user-role">{{ userInfo.role?.name || '普通用户' }}</div>
10
+ </div>
11
+ </div>
12
+ <div class="user-details">
13
+ <div class="detail-item">
14
+ <Icon name="Mail" :size="14" />
15
+ <span>{{ userInfo.email || '未设置' }}</span>
16
+ </div>
17
+ <div v-if="userInfo.phone" class="detail-item">
18
+ <Icon name="Phone" :size="14" />
19
+ <span>{{ userInfo.phone }}</span>
20
+ </div>
21
+ <div v-if="userInfo.lastLoginTime" class="detail-item">
22
+ <Icon name="Clock" :size="14" />
23
+ <span>{{ formatTime(userInfo.lastLoginTime) }}</span>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup>
30
+ // 组件内部数据
31
+ const userInfo = $ref({});
32
+
33
+ // 获取数据
34
+ const fetchData = async () => {
35
+ try {
36
+ const { data } = await $Http('/addon/admin/adminInfo');
37
+ Object.assign(userInfo, data);
38
+ } catch (error) {
39
+ console.error('获取用户信息失败:', error);
40
+ }
41
+ };
42
+
43
+ // 格式化时间
44
+ const formatTime = (timestamp) => {
45
+ if (!timestamp) return '';
46
+ const date = new Date(Number(timestamp));
47
+ const now = new Date();
48
+ const diff = now - date;
49
+
50
+ // 小于1分钟
51
+ if (diff < 60000) {
52
+ return '刚刚';
53
+ }
54
+ // 小于1小时
55
+ if (diff < 3600000) {
56
+ return `${Math.floor(diff / 60000)}分钟前`;
57
+ }
58
+ // 小于24小时
59
+ if (diff < 86400000) {
60
+ return `${Math.floor(diff / 3600000)}小时前`;
61
+ }
62
+ // 小于7天
63
+ if (diff < 604800000) {
64
+ return `${Math.floor(diff / 86400000)}天前`;
65
+ }
66
+ // 超过7天显示具体日期
67
+ return `${date.getMonth() + 1}月${date.getDate()}日`;
68
+ };
69
+
70
+ fetchData();
71
+ </script>
72
+
73
+ <style scoped lang="scss">
74
+ .user-info-card {
75
+ .user-header {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 12px;
79
+ padding-bottom: 12px;
80
+ border-bottom: 1px solid $border-color;
81
+
82
+ .user-avatar {
83
+ width: 48px;
84
+ height: 48px;
85
+ background: linear-gradient(135deg, $primary-color, #764ba2);
86
+ border-radius: 50%;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ color: white;
91
+ flex-shrink: 0;
92
+ }
93
+
94
+ .user-basic {
95
+ flex: 1;
96
+ min-width: 0;
97
+
98
+ .user-name {
99
+ font-size: 16px;
100
+ font-weight: 600;
101
+ color: $text-primary;
102
+ margin-bottom: 4px;
103
+ overflow: hidden;
104
+ text-overflow: ellipsis;
105
+ white-space: nowrap;
106
+ }
107
+
108
+ .user-role {
109
+ font-size: 12px;
110
+ color: $text-secondary;
111
+ }
112
+ }
113
+ }
114
+
115
+ .user-details {
116
+ display: flex;
117
+ flex-direction: column;
118
+ gap: 8px;
119
+ margin-top: 12px;
120
+
121
+ .detail-item {
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 8px;
125
+ font-size: 12px;
126
+ color: $text-secondary;
127
+
128
+ span {
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ white-space: nowrap;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ </style>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <div class="dashboard-container">
3
+ <div class="dashboard-left">
4
+ <SystemOverview />
5
+ <ServiceStatus />
6
+ <SystemResources />
7
+ <PerformanceMetrics />
8
+ <EnvironmentInfo />
9
+ </div>
10
+ <div class="dashboard-right">
11
+ <UserInfo />
12
+ <AddonList />
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup>
18
+ import SystemOverview from './components/SystemOverview.vue';
19
+ import ServiceStatus from './components/ServiceStatus.vue';
20
+ import UserInfo from './components/UserInfo.vue';
21
+ import AddonList from './components/AddonList.vue';
22
+ import SystemResources from './components/SystemResources.vue';
23
+ import PerformanceMetrics from './components/PerformanceMetrics.vue';
24
+ import EnvironmentInfo from './components/EnvironmentInfo.vue';
25
+ </script>
26
+
27
+ <style scoped lang="scss">
28
+ .dashboard-container {
29
+ display: flex;
30
+ gap: 12px;
31
+ overflow: hidden;
32
+ height: 100%;
33
+
34
+ .dashboard-left {
35
+ flex: 1;
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 12px;
39
+ overflow-y: auto;
40
+ min-width: 0;
41
+ }
42
+
43
+ .dashboard-right {
44
+ width: 400px;
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: 12px;
48
+ overflow-y: auto;
49
+ flex-shrink: 0;
50
+ }
51
+
52
+ @media (max-width: 768px) {
53
+ flex-direction: column;
54
+ padding: 10px;
55
+ gap: 10px;
56
+
57
+ .dashboard-right {
58
+ width: 100%;
59
+ }
60
+ }
61
+ }
62
+ </style>