@befly-addon/admin 1.0.10 → 1.0.11

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 (35) hide show
  1. package/apis/api/all.ts +6 -11
  2. package/apis/menu/all.ts +8 -14
  3. package/package.json +15 -4
  4. package/util.ts +1 -150
  5. package/views/403/index.vue +68 -0
  6. package/views/admin/components/edit.vue +150 -0
  7. package/views/admin/components/role.vue +138 -0
  8. package/views/admin/index.vue +179 -0
  9. package/views/dict/components/edit.vue +159 -0
  10. package/views/dict/index.vue +162 -0
  11. package/views/index/components/addonList.vue +127 -0
  12. package/views/index/components/environmentInfo.vue +99 -0
  13. package/views/index/components/operationLogs.vue +114 -0
  14. package/views/index/components/performanceMetrics.vue +150 -0
  15. package/views/index/components/quickActions.vue +27 -0
  16. package/views/index/components/serviceStatus.vue +183 -0
  17. package/views/index/components/systemNotifications.vue +132 -0
  18. package/views/index/components/systemOverview.vue +190 -0
  19. package/views/index/components/systemResources.vue +106 -0
  20. package/views/index/components/userInfo.vue +206 -0
  21. package/views/index/index.vue +29 -0
  22. package/views/login/components/emailLoginForm.vue +167 -0
  23. package/views/login/components/registerForm.vue +170 -0
  24. package/views/login/components/welcomePanel.vue +61 -0
  25. package/views/login/index.vue +191 -0
  26. package/views/menu/components/edit.vue +153 -0
  27. package/views/menu/index.vue +177 -0
  28. package/views/news/detail/index.vue +26 -0
  29. package/views/news/index.vue +26 -0
  30. package/views/role/components/api.vue +283 -0
  31. package/views/role/components/edit.vue +132 -0
  32. package/views/role/components/menu.vue +146 -0
  33. package/views/role/index.vue +179 -0
  34. package/views/user/index.vue +322 -0
  35. package/apis/dashboard/addonList.ts +0 -47
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <i-lucide:server style="width: 20px; height: 20px" />
5
+ <h2>运行环境</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="env-grid-compact">
9
+ <div class="env-compact-item">
10
+ <span class="env-label">操作系统</span>
11
+ <span class="env-value">{{ environmentInfo.os }}</span>
12
+ </div>
13
+ <div class="env-compact-item">
14
+ <span class="env-label">服务器</span>
15
+ <span class="env-value">{{ environmentInfo.server }}</span>
16
+ </div>
17
+ <div class="env-compact-item">
18
+ <span class="env-label">Node版本</span>
19
+ <span class="env-value">{{ environmentInfo.nodeVersion }}</span>
20
+ </div>
21
+ <div class="env-compact-item">
22
+ <span class="env-label">数据库</span>
23
+ <span class="env-value">{{ environmentInfo.database }}</span>
24
+ </div>
25
+ <div class="env-compact-item">
26
+ <span class="env-label">缓存</span>
27
+ <span class="env-value">{{ environmentInfo.cache }}</span>
28
+ </div>
29
+ <div class="env-compact-item">
30
+ <span class="env-label">时区</span>
31
+ <span class="env-value">{{ environmentInfo.timezone }}</span>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup>
39
+ import { ref } from 'vue';
40
+
41
+ // 组件内部数据
42
+ const environmentInfo = $ref({
43
+ os: '',
44
+ server: '',
45
+ nodeVersion: '',
46
+ database: '',
47
+ cache: '',
48
+ timezone: ''
49
+ });
50
+
51
+ // 获取数据
52
+ const fetchData = async () => {
53
+ try {
54
+ const { data } = await $Http('/addon/admin/dashboard/environmentInfo');
55
+ Object.assign(environmentInfo, data);
56
+ } catch (error) {
57
+ console.error('获取运行环境信息失败:', error);
58
+ }
59
+ };
60
+
61
+ fetchData();
62
+ </script>
63
+
64
+ <style scoped lang="scss">
65
+ .env-grid-compact {
66
+ display: grid;
67
+ grid-template-columns: repeat(4, 1fr);
68
+ gap: 10px;
69
+
70
+ .env-compact-item {
71
+ display: flex;
72
+ justify-content: space-between;
73
+ align-items: center;
74
+ padding: 10px 12px;
75
+ background: $bg-color-container;
76
+ border-radius: 6px;
77
+ border: 1px solid $border-color;
78
+ transition: all 0.2s ease;
79
+
80
+ &:hover {
81
+ background: rgba($primary-color, 0.03);
82
+ border-color: rgba($primary-color, 0.2);
83
+ }
84
+
85
+ .env-label {
86
+ font-size: 14px;
87
+ color: $text-secondary;
88
+ font-weight: 500;
89
+ }
90
+
91
+ .env-value {
92
+ font-size: 14px;
93
+ color: $text-primary;
94
+ font-weight: 600;
95
+ text-align: right;
96
+ }
97
+ }
98
+ }
99
+ </style>
@@ -0,0 +1,114 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <i-lucide:list style="width: 20px; height: 20px" />
5
+ <h2>操作日志</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="operation-table">
9
+ <div class="operation-header">
10
+ <span class="col-time">时间</span>
11
+ <span class="col-user">操作人</span>
12
+ <span class="col-action">操作</span>
13
+ <span class="col-module">模块</span>
14
+ <span class="col-ip">IP地址</span>
15
+ <span class="col-status">状态</span>
16
+ </div>
17
+ <div class="operation-body">
18
+ <div v-for="log in operationLogs" :key="log.id" class="operation-row">
19
+ <span class="col-time">{{ formatTime(log.createdAt) }}</span>
20
+ <span class="col-user">{{ log.userName }}</span>
21
+ <span class="col-action">{{ log.action }}</span>
22
+ <span class="col-module">{{ log.module }}</span>
23
+ <span class="col-ip">{{ log.ip }}</span>
24
+ <span class="col-status">
25
+ <tiny-tag :type="log.status === 'success' ? 'success' : 'danger'" size="small">
26
+ {{ log.status === 'success' ? '成功' : '失败' }}
27
+ </tiny-tag>
28
+ </span>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup>
37
+ import { ref } from 'vue';
38
+
39
+ // 组件内部数据
40
+ const operationLogs = $ref([
41
+ { id: 1, userName: '管理员', action: '创建角色', module: '权限管理', ip: '192.168.1.100', status: 'success', createdAt: Date.now() - 120000 },
42
+ { id: 2, userName: '张三', action: '修改菜单', module: '系统设置', ip: '192.168.1.101', status: 'success', createdAt: Date.now() - 900000 },
43
+ { id: 3, userName: '李四', action: '删除接口', module: '接口管理', ip: '192.168.1.102', status: 'failed', createdAt: Date.now() - 3600000 },
44
+ { id: 4, userName: '管理员', action: '同步数据库', module: '数据库', ip: '192.168.1.100', status: 'success', createdAt: Date.now() - 7200000 },
45
+ { id: 5, userName: '王五', action: '登录系统', module: '系统', ip: '192.168.1.103', status: 'success', createdAt: Date.now() - 10800000 }
46
+ ]);
47
+
48
+ const formatTime = (timestamp) => {
49
+ const date = new Date(timestamp);
50
+ const month = String(date.getMonth() + 1).padStart(2, '0');
51
+ const day = String(date.getDate()).padStart(2, '0');
52
+ const hours = String(date.getHours()).padStart(2, '0');
53
+ const minutes = String(date.getMinutes()).padStart(2, '0');
54
+ return `${month}-${day} ${hours}:${minutes}`;
55
+ };
56
+ </script>
57
+
58
+ <style scoped lang="scss">
59
+ .operation-table {
60
+ .operation-header,
61
+ .operation-row {
62
+ display: grid;
63
+ grid-template-columns: 100px 100px 1fr 120px 120px 80px;
64
+ gap: 12px;
65
+ align-items: center;
66
+ }
67
+
68
+ .operation-header {
69
+ padding: 10px 12px;
70
+ background: linear-gradient(135deg, rgba($primary-color, 0.05) 0%, rgba($primary-color, 0.02) 100%);
71
+ border-radius: 6px;
72
+ font-size: 14px;
73
+ font-weight: 600;
74
+ color: $text-secondary;
75
+ margin-bottom: 6px;
76
+ }
77
+
78
+ .operation-body {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 4px;
82
+ }
83
+
84
+ .operation-row {
85
+ padding: 10px 12px;
86
+ background: $bg-color-container;
87
+ border-radius: 6px;
88
+ border: 1px solid $border-color;
89
+ font-size: 14px;
90
+ transition: all 0.2s ease;
91
+
92
+ &:hover {
93
+ background: rgba($primary-color, 0.02);
94
+ border-color: rgba($primary-color, 0.2);
95
+ }
96
+
97
+ .col-time {
98
+ color: $text-secondary;
99
+ font-size: 14px;
100
+ }
101
+
102
+ .col-user,
103
+ .col-action,
104
+ .col-module,
105
+ .col-ip {
106
+ color: $text-primary;
107
+ }
108
+
109
+ .col-action {
110
+ font-weight: 600;
111
+ }
112
+ }
113
+ }
114
+ </style>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <i-lucide:zap style="width: 20px; height: 20px" />
5
+ <h2>性能指标</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="performance-grid">
9
+ <div class="perf-metric">
10
+ <div class="perf-icon">
11
+ <i-lucide:clock style="width: 18px; height: 18px" />
12
+ </div>
13
+ <div class="perf-info">
14
+ <div class="perf-label">平均响应</div>
15
+ <div class="perf-value">{{ performanceMetrics.avgResponseTime }}ms</div>
16
+ </div>
17
+ </div>
18
+ <div class="perf-metric">
19
+ <div class="perf-icon">
20
+ <i-lucide:trending-up style="width: 18px; height: 18px" />
21
+ </div>
22
+ <div class="perf-info">
23
+ <div class="perf-label">QPS</div>
24
+ <div class="perf-value">{{ performanceMetrics.qps }}/s</div>
25
+ </div>
26
+ </div>
27
+ <div class="perf-metric">
28
+ <div class="perf-icon">
29
+ <i-lucide:alert-circle style="width: 18px; height: 18px" />
30
+ </div>
31
+ <div class="perf-info">
32
+ <div class="perf-label">错误率</div>
33
+ <div class="perf-value">{{ performanceMetrics.errorRate }}%</div>
34
+ </div>
35
+ </div>
36
+ <div class="perf-metric">
37
+ <div class="perf-icon">
38
+ <i-lucide:activity style="width: 18px; height: 18px" />
39
+ </div>
40
+ <div class="perf-info">
41
+ <div class="perf-label">活跃连接</div>
42
+ <div class="perf-value">{{ performanceMetrics.activeConnections }}</div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ <!-- 最慢接口提示 -->
47
+ <div v-if="performanceMetrics.slowestApi" class="perf-slowest">
48
+ <i-lucide:alert-triangle style="width: 14px; height: 14px" />
49
+ <span>最慢接口: {{ performanceMetrics.slowestApi.path }} ({{ performanceMetrics.slowestApi.time }}ms)</span>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </template>
54
+
55
+ <script setup>
56
+ import { ref } from 'vue';
57
+
58
+ // 组件内部数据
59
+ const performanceMetrics = $ref({
60
+ avgResponseTime: 0,
61
+ qps: 0,
62
+ errorRate: 0,
63
+ activeConnections: 0,
64
+ slowestApi: null
65
+ });
66
+
67
+ // 获取数据
68
+ const fetchData = async () => {
69
+ try {
70
+ const { data } = await $Http('/addon/admin/dashboard/performanceMetrics');
71
+ Object.assign(performanceMetrics, data);
72
+ } catch (error) {
73
+ console.error('获取性能指标失败:', error);
74
+ }
75
+ };
76
+
77
+ fetchData();
78
+ </script>
79
+
80
+ <style scoped lang="scss">
81
+ .performance-grid {
82
+ display: grid;
83
+ grid-template-columns: repeat(4, 1fr);
84
+ gap: $spacing-sm;
85
+ margin-bottom: $spacing-sm;
86
+
87
+ .perf-metric {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: $spacing-sm;
91
+ padding: $spacing-sm $spacing-md;
92
+ background: rgba($primary-color, 0.02);
93
+ border-radius: $border-radius;
94
+ border: 1px solid $border-color;
95
+ transition: all 0.2s ease;
96
+
97
+ &:hover {
98
+ background: rgba($primary-color, 0.05);
99
+ border-color: $primary-color;
100
+ }
101
+
102
+ .perf-icon {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ width: 36px;
107
+ height: 36px;
108
+ border-radius: $border-radius-small;
109
+ background: linear-gradient(135deg, rgba($success-color, 0.1) 0%, rgba($success-color, 0.05) 100%);
110
+ color: $success-color;
111
+ flex-shrink: 0;
112
+ }
113
+
114
+ .perf-info {
115
+ flex: 1;
116
+
117
+ .perf-label {
118
+ font-size: 14px;
119
+ color: $text-secondary;
120
+ margin-bottom: 2px;
121
+ }
122
+
123
+ .perf-value {
124
+ font-size: 16px;
125
+ font-weight: 700;
126
+ color: $primary-color;
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ .perf-slowest {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 6px;
136
+ padding: $spacing-sm $spacing-md;
137
+ background: rgba($warning-color, 0.05);
138
+ border-radius: $border-radius-small;
139
+ border: 1px solid rgba($warning-color, 0.2);
140
+ font-size: 14px;
141
+ color: $warning-color;
142
+
143
+ span {
144
+ flex: 1;
145
+ overflow: hidden;
146
+ text-overflow: ellipsis;
147
+ white-space: nowrap;
148
+ }
149
+ }
150
+ </style>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-content">
4
+ <tiny-button type="primary" size="large" @click="handleClearCache">
5
+ <template #prefix>
6
+ <i-lucide:rotate-cw style="width: 18px; height: 18px" />
7
+ </template>
8
+ 刷新缓存
9
+ </tiny-button>
10
+ </div>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup>
15
+ const handleClearCache = () => {
16
+ console.log('刷新缓存');
17
+ };
18
+ </script>
19
+
20
+ <style scoped lang="scss">
21
+ .section-block {
22
+ .section-content {
23
+ display: flex;
24
+ justify-content: center;
25
+ }
26
+ }
27
+ </style>
@@ -0,0 +1,183 @@
1
+ <template>
2
+ <div class="section-block">
3
+ <div class="section-header">
4
+ <i-lucide:settings style="width: 20px; height: 20px" />
5
+ <h2>服务状态</h2>
6
+ </div>
7
+ <div class="section-content">
8
+ <div class="config-grid">
9
+ <div v-for="service in services" :key="service.name" class="config-card" :class="`config-${service.status}`">
10
+ <div class="config-icon">
11
+ <i-lucide:database v-if="service.name === '数据库'" style="width: 20px; height: 20px" />
12
+ <i-lucide:zap v-else-if="service.name === 'Redis'" style="width: 20px; height: 20px" />
13
+ <i-lucide:hard-drive v-else-if="service.name === '文件系统'" style="width: 20px; height: 20px" />
14
+ <i-lucide:mail v-else-if="service.name === '邮件服务'" style="width: 20px; height: 20px" />
15
+ <i-lucide:cloud v-else-if="service.name === 'OSS存储'" style="width: 20px; height: 20px" />
16
+ <i-lucide:circle v-else style="width: 20px; height: 20px" />
17
+ </div>
18
+ <div class="config-info">
19
+ <div class="config-name">{{ service.name }}</div>
20
+ <div class="config-status">
21
+ {{ getStatusText(service.status) }}
22
+ <span v-if="service.responseTime && service.responseTime !== '-'" class="latency">{{ service.responseTime }}</span>
23
+ </div>
24
+ </div>
25
+ <div class="config-badge">
26
+ <i-lucide:check-circle v-if="service.status === 'running'" style="width: 32px; height: 32px" />
27
+ <i-lucide:x-circle v-else-if="service.status === 'stopped'" style="width: 32px; height: 32px" />
28
+ <i-lucide:alert-circle v-else-if="service.status === 'unconfigured'" style="width: 32px; height: 32px" />
29
+ <i-lucide:circle v-else style="width: 32px; height: 32px" />
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </template>
36
+
37
+ <script setup>
38
+ import { ref } from 'vue';
39
+
40
+ // 组件内部数据
41
+ const services = $ref([]);
42
+
43
+ // 获取数据
44
+ const fetchData = async () => {
45
+ try {
46
+ const { data } = await $Http('/addon/admin/dashboard/serviceStatus');
47
+ services.splice(0, services.length, ...data.services);
48
+ } catch (error) {
49
+ console.error('获取服务状态失败:', error);
50
+ }
51
+ };
52
+
53
+ fetchData();
54
+
55
+ // 工具函数
56
+ const getStatusColor = (status) => {
57
+ const colors = {
58
+ running: 'success',
59
+ stopped: 'error',
60
+ unconfigured: 'warning'
61
+ };
62
+ return colors[status] || 'default';
63
+ };
64
+
65
+ const getStatusText = (status) => {
66
+ const texts = {
67
+ running: '正常',
68
+ stopped: '停止',
69
+ unconfigured: '未配置'
70
+ };
71
+ return texts[status] || status;
72
+ };
73
+ </script>
74
+
75
+ <style scoped lang="scss">
76
+ .config-grid {
77
+ display: grid;
78
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
79
+ gap: 10px;
80
+
81
+ .config-card {
82
+ background: rgba($primary-color, 0.02);
83
+ border: 1px solid $border-color;
84
+ border-radius: 6px;
85
+ padding: 12px;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 10px;
89
+ position: relative;
90
+ overflow: hidden;
91
+ transition: all 0.3s;
92
+
93
+ &:hover {
94
+ background: rgba($primary-color, 0.05);
95
+ border-color: $primary-color;
96
+ transform: translateY(-2px);
97
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
98
+ }
99
+
100
+ .config-icon {
101
+ width: 40px;
102
+ height: 40px;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ border-radius: 6px;
107
+ flex-shrink: 0;
108
+ }
109
+
110
+ .config-info {
111
+ flex: 1;
112
+ min-width: 0;
113
+
114
+ .config-name {
115
+ font-size: 14px;
116
+ font-weight: 600;
117
+ margin-bottom: 2px;
118
+ }
119
+
120
+ .config-status {
121
+ font-size: 14px;
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 4px;
125
+
126
+ .latency {
127
+ margin-left: 4px;
128
+ color: $text-placeholder;
129
+ }
130
+ }
131
+ }
132
+
133
+ .config-badge {
134
+ position: absolute;
135
+ top: 6px;
136
+ right: 6px;
137
+ opacity: 0.2;
138
+ }
139
+
140
+ &.config-running {
141
+ border-color: $success-color;
142
+ background: linear-gradient(135deg, rgba(82, 196, 26, 0.05), white);
143
+
144
+ .config-icon {
145
+ background: rgba(82, 196, 26, 0.1);
146
+ color: $success-color;
147
+ }
148
+
149
+ .config-name {
150
+ color: $success-color;
151
+ }
152
+ }
153
+
154
+ &.config-unconfigured {
155
+ border-color: $warning-color;
156
+ background: linear-gradient(135deg, rgba(250, 173, 20, 0.05), white);
157
+
158
+ .config-icon {
159
+ background: rgba(250, 173, 20, 0.1);
160
+ color: $warning-color;
161
+ }
162
+
163
+ .config-name {
164
+ color: $warning-color;
165
+ }
166
+ }
167
+
168
+ &.config-stopped {
169
+ border-color: $error-color;
170
+ background: linear-gradient(135deg, rgba(255, 77, 79, 0.05), white);
171
+
172
+ .config-icon {
173
+ background: rgba(255, 77, 79, 0.1);
174
+ color: $error-color;
175
+ }
176
+
177
+ .config-name {
178
+ color: $error-color;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ </style>