befly-admin 3.4.6 → 3.4.7

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 (65) hide show
  1. package/.gitignore +2 -0
  2. package/index.html +2 -2
  3. package/package.json +8 -8
  4. package/src/config/{internal/index.ts → index.ts} +1 -1
  5. package/src/layouts/{internal/0.vue → default.vue} +49 -20
  6. package/src/main.ts +3 -5
  7. package/src/plugins/{internal/global.ts → global.ts} +1 -11
  8. package/src/plugins/{internal/http.ts → http.ts} +1 -1
  9. package/src/{plugins/internal/router.ts → router/index.ts} +22 -15
  10. package/src/styles/{internal/index.scss → global.scss} +29 -29
  11. package/src/types/auto-imports.d.ts +90 -645
  12. package/src/types/components.d.ts +5 -23
  13. package/src/types/typed-router.d.ts +32 -0
  14. package/src/utils/index.ts +21 -9
  15. package/src/views/index.vue +27 -0
  16. package/src/views/internal/login.vue +73 -0
  17. package/src/views/test.vue +60 -0
  18. package/tsconfig.json +4 -4
  19. package/vite.config.ts +123 -44
  20. package/src/components/internal/README.md +0 -27
  21. package/src/layouts/4.vue +0 -17
  22. package/src/layouts/internal/1.vue +0 -22
  23. package/src/layouts/internal/2.vue +0 -169
  24. package/src/layouts/internal/README.md +0 -27
  25. package/src/plugins/internal/README.md +0 -36
  26. package/src/styles/internal/README.md +0 -27
  27. package/src/styles/internal/mixins.scss +0 -98
  28. package/src/types/env.d.ts +0 -23
  29. package/src/utils/README.md +0 -37
  30. package/src/utils/internal/README.md +0 -21
  31. package/src/utils/internal/index.ts +0 -30
  32. package/src/views/internal/403/403.vue +0 -66
  33. package/src/views/internal/README.md +0 -27
  34. package/src/views/internal/admin/components/edit.vue +0 -147
  35. package/src/views/internal/admin/components/role.vue +0 -135
  36. package/src/views/internal/admin/index.vue +0 -176
  37. package/src/views/internal/dict/components/edit.vue +0 -156
  38. package/src/views/internal/dict/index.vue +0 -159
  39. package/src/views/internal/index/components/addonList.vue +0 -125
  40. package/src/views/internal/index/components/environmentInfo.vue +0 -97
  41. package/src/views/internal/index/components/operationLogs.vue +0 -112
  42. package/src/views/internal/index/components/performanceMetrics.vue +0 -148
  43. package/src/views/internal/index/components/quickActions.vue +0 -27
  44. package/src/views/internal/index/components/serviceStatus.vue +0 -181
  45. package/src/views/internal/index/components/systemNotifications.vue +0 -130
  46. package/src/views/internal/index/components/systemOverview.vue +0 -188
  47. package/src/views/internal/index/components/systemResources.vue +0 -104
  48. package/src/views/internal/index/components/userInfo.vue +0 -202
  49. package/src/views/internal/index/index.vue +0 -62
  50. package/src/views/internal/login/components/emailLoginForm.vue +0 -163
  51. package/src/views/internal/login/components/registerForm.vue +0 -168
  52. package/src/views/internal/login/components/welcomePanel.vue +0 -61
  53. package/src/views/internal/login/index_1.vue +0 -189
  54. package/src/views/internal/menu/components/edit.vue +0 -150
  55. package/src/views/internal/menu/index.vue +0 -168
  56. package/src/views/internal/news/detail/detail_2.vue +0 -26
  57. package/src/views/internal/news/detail/index.vue +0 -26
  58. package/src/views/internal/news/news.vue +0 -26
  59. package/src/views/internal/role/components/api.vue +0 -280
  60. package/src/views/internal/role/components/edit.vue +0 -129
  61. package/src/views/internal/role/components/menu.vue +0 -143
  62. package/src/views/internal/role/index.vue +0 -179
  63. package/src/views/internal/user/user.vue +0 -320
  64. /package/src/plugins/{internal/storage.ts → storage.ts} +0 -0
  65. /package/src/styles/{internal/variables.scss → variables.scss} +0 -0
@@ -1,202 +0,0 @@
1
- <template>
2
- <div class="section-block user-info-card">
3
- <div class="user-header">
4
- <div class="user-avatar">
5
- <i-lucide:user style="width: 32px; height: 32px" />
6
- </div>
7
- <div class="user-basic">
8
- <div class="user-name">{{ $Data.userInfo.nickname || $Data.userInfo.name || $Data.userInfo.username || '未设置' }}</div>
9
- <div class="user-role">{{ $Data.userInfo.role?.name || '普通用户' }}</div>
10
- </div>
11
- </div>
12
- <div class="user-details">
13
- <div class="detail-item">
14
- <i-lucide:mail style="width: 14px; height: 14px" />
15
- <span>{{ $Data.userInfo.email || '未设置' }}</span>
16
- </div>
17
- <div v-if="$Data.userInfo.phone" class="detail-item">
18
- <i-lucide:phone style="width: 14px; height: 14px" />
19
- <span>{{ $Data.userInfo.phone }}</span>
20
- </div>
21
- <div v-if="$Data.userInfo.lastLoginTime" class="detail-item">
22
- <i-lucide:clock style="width: 14px; height: 14px" />
23
- <span>{{ $Method.formatTime($Data.userInfo.lastLoginTime) }}</span>
24
- </div>
25
- </div>
26
-
27
- <!-- 仅 dev 角色显示刷新缓存按钮 -->
28
- <div v-if="$Data.userInfo.roleCode === 'dev'" class="user-actions">
29
- <tiny-button type="primary" size="mini" :loading="$Data.refreshing" @click="$Method.handleRefreshCache">
30
- <template #icon>
31
- <i-lucide:rotate-cw style="width: 14px; height: 14px" />
32
- </template>
33
- 刷新缓存
34
- </tiny-button>
35
- </div>
36
- </div>
37
- </template>
38
-
39
- <script setup>
40
- // 响应式数据
41
- const $Data = $ref({
42
- userInfo: {},
43
- refreshing: false
44
- });
45
-
46
- // 方法集合
47
- const $Method = {
48
- // 获取数据
49
- async fetchData() {
50
- try {
51
- const { data } = await $Http('/addon/admin/admin/info');
52
- Object.assign($Data.userInfo, data);
53
- } catch (error) {
54
- console.error('获取用户信息失败:', error);
55
- }
56
- },
57
-
58
- // 刷新缓存
59
- async handleRefreshCache() {
60
- try {
61
- $Data.refreshing = true;
62
- const result = await $Http('/addon/admin/admin/cacheRefresh');
63
-
64
- if (result.code === 0) {
65
- const { apis, menus, roles } = result.data;
66
- const messages = [];
67
-
68
- if (apis.success) {
69
- messages.push(`接口缓存: ${apis.count} 个`);
70
- }
71
- if (menus.success) {
72
- messages.push(`菜单缓存: ${menus.count} 个`);
73
- }
74
- if (roles.success) {
75
- messages.push(`角色缓存: ${roles.count} 个`);
76
- }
77
-
78
- TinyMessage.success({
79
- message: `缓存刷新成功!${messages.join(',')}`,
80
- duration: 3000
81
- });
82
- } else {
83
- TinyMessage.warning({
84
- message: result.msg || '部分缓存刷新失败',
85
- duration: 3000
86
- });
87
- }
88
- } catch (error) {
89
- console.error('刷新缓存失败:', error);
90
- TinyMessage.error({
91
- message: '刷新缓存失败,请稍后重试',
92
- duration: 3000
93
- });
94
- } finally {
95
- $Data.refreshing = false;
96
- }
97
- },
98
-
99
- // 格式化时间
100
- formatTime(timestamp) {
101
- if (!timestamp) return '';
102
- const date = new Date(Number(timestamp));
103
- const now = new Date();
104
- const diff = now - date;
105
-
106
- // 小于1分钟
107
- if (diff < 60000) {
108
- return '刚刚';
109
- }
110
- // 小于1小时
111
- if (diff < 3600000) {
112
- return `${Math.floor(diff / 60000)}分钟前`;
113
- }
114
- // 小于24小时
115
- if (diff < 86400000) {
116
- return `${Math.floor(diff / 3600000)}小时前`;
117
- }
118
- // 小于7天
119
- if (diff < 604800000) {
120
- return `${Math.floor(diff / 86400000)}天前`;
121
- }
122
- // 超过7天显示具体日期
123
- return `${date.getMonth() + 1}月${date.getDate()}日`;
124
- }
125
- };
126
-
127
- // 初始化
128
- $Method.fetchData();
129
- </script>
130
-
131
- <style scoped lang="scss">
132
- .user-info-card {
133
- .user-header {
134
- display: flex;
135
- align-items: center;
136
- gap: 12px;
137
- padding-bottom: 12px;
138
- border-bottom: 1px solid $border-color;
139
-
140
- .user-avatar {
141
- width: 48px;
142
- height: 48px;
143
- background: linear-gradient(135deg, $primary-color, #764ba2);
144
- border-radius: 50%;
145
- display: flex;
146
- align-items: center;
147
- justify-content: center;
148
- color: white;
149
- flex-shrink: 0;
150
- }
151
-
152
- .user-basic {
153
- flex: 1;
154
- min-width: 0;
155
-
156
- .user-name {
157
- font-size: 16px;
158
- font-weight: 600;
159
- color: $text-primary;
160
- margin-bottom: 4px;
161
- overflow: hidden;
162
- text-overflow: ellipsis;
163
- white-space: nowrap;
164
- }
165
-
166
- .user-role {
167
- font-size: 12px;
168
- color: $text-secondary;
169
- }
170
- }
171
- }
172
-
173
- .user-details {
174
- display: flex;
175
- flex-direction: column;
176
- gap: 8px;
177
- margin-top: 12px;
178
-
179
- .detail-item {
180
- display: flex;
181
- align-items: center;
182
- gap: 8px;
183
- font-size: 12px;
184
- color: $text-secondary;
185
-
186
- span {
187
- overflow: hidden;
188
- text-overflow: ellipsis;
189
- white-space: nowrap;
190
- }
191
- }
192
- }
193
-
194
- .user-actions {
195
- margin-top: 16px;
196
- padding-top: 12px;
197
- border-top: 1px solid $border-color;
198
- display: flex;
199
- justify-content: center;
200
- }
201
- }
202
- </style>
@@ -1,62 +0,0 @@
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>
@@ -1,163 +0,0 @@
1
- <template>
2
- <tiny-form :model="$Data.formData" :rules="$Data2.formRules" :ref="(el) => ($From.form = el)" class="login-form" label-width="90px" label-position="left" :show-message="false">
3
- <tiny-form-item prop="account" label="账号">
4
- <tiny-input v-model="$Data.formData.account" placeholder="请输入用户名或邮箱" size="large" clearable>
5
- <template #prefix-icon>
6
- <i-lucide:user style="width: 18px; height: 18px" />
7
- </template>
8
- </tiny-input>
9
- </tiny-form-item>
10
-
11
- <tiny-form-item prop="password" label="密码">
12
- <tiny-input v-model="$Data.formData.password" type="password" placeholder="请输入密码" size="large" clearable>
13
- <template #prefix-icon>
14
- <i-lucide:lock style="width: 18px; height: 18px" />
15
- </template>
16
- </tiny-input>
17
- </tiny-form-item>
18
-
19
- <div class="form-footer">
20
- <a href="#" class="forgot-password">忘记密码?</a>
21
- </div>
22
-
23
- <tiny-button theme="primary" class="auth-btn" size="large" :loading="$Data.loading" @click="$Method.apiLogin"> 登录 </tiny-button>
24
- </tiny-form>
25
- </template>
26
-
27
- <script setup>
28
- const router = useRouter();
29
-
30
- // 表单引用
31
- const $From = $shallowRef({
32
- form: null
33
- });
34
-
35
- // 数据定义
36
- const $Data = $ref({
37
- loading: false,
38
- formData: {
39
- account: '',
40
- password: ''
41
- }
42
- });
43
-
44
- const $Data2 = $shallowRef({
45
- formRules: {
46
- account: [{ required: true, message: '请输入用户名或邮箱', trigger: 'blur' }],
47
- password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
48
- }
49
- });
50
-
51
- // 方法定义
52
- const $Method = {
53
- async apiLogin() {
54
- try {
55
- const valid = await $From.form.validate();
56
-
57
- $Data.loading = true;
58
-
59
- const res = await $Http('/addon/admin/auth/login', {
60
- account: $Data.formData.account,
61
- password: $Data.formData.password
62
- });
63
-
64
- // 先保存 token
65
- $Storage.local.set('token', res.data.token);
66
-
67
- // 如果返回用户信息,也可以存储
68
- if (res.data.userInfo) {
69
- $Storage.local.set('userInfo', res.data.userInfo);
70
- }
71
-
72
- Modal.message({
73
- message: '登录成功',
74
- status: 'success'
75
- });
76
-
77
- // 跳转到首页,路由守卫会自动加载菜单
78
- await router.push('/');
79
- } catch (error) {
80
- console.log('🔥[ error ]-77', error);
81
- } finally {
82
- $Data.loading = false;
83
- }
84
- }
85
- };
86
- </script>
87
-
88
- <style scoped lang="scss">
89
- .login-form {
90
- width: 100%;
91
- max-width: 450px;
92
- }
93
-
94
- .tiny-form-item {
95
- width: 100%;
96
- margin-bottom: 1.2rem;
97
-
98
- :deep(.tiny-form__controls) {
99
- width: 100%;
100
- }
101
-
102
- :deep(.tiny-input) {
103
- width: 100%;
104
- background: #f8f9fa;
105
- border: 1px solid #e0e0e0;
106
- border-radius: 6px;
107
- transition: all 0.3s;
108
-
109
- &:hover {
110
- border-color: #48b19f;
111
- }
112
-
113
- &:focus-within {
114
- border-color: #48b19f;
115
- background: #fff;
116
- }
117
-
118
- input {
119
- padding: 0.75rem 1rem;
120
- }
121
- }
122
- }
123
-
124
- .form-footer {
125
- width: 100%;
126
- display: flex;
127
- justify-content: flex-end;
128
- margin-bottom: 1rem;
129
- }
130
-
131
- .forgot-password {
132
- font-size: 0.8rem;
133
- color: #888;
134
- text-decoration: none;
135
-
136
- &:hover {
137
- color: #48b19f;
138
- }
139
- }
140
-
141
- .auth-btn {
142
- width: 100% !important;
143
- max-width: 100%;
144
- height: 44px;
145
- border-radius: 6px;
146
- background: #48b19f;
147
- border: none;
148
- font-size: 0.95rem;
149
- font-weight: 600;
150
- margin-top: 0.5rem;
151
- transition: all 0.3s;
152
-
153
- &:hover {
154
- background: #3a9d8f;
155
- transform: translateY(-1px);
156
- box-shadow: 0 3px 10px rgba(72, 177, 159, 0.3);
157
- }
158
-
159
- :deep(.tiny-button__text) {
160
- color: #fff;
161
- }
162
- }
163
- </style>
@@ -1,168 +0,0 @@
1
- <template>
2
- <tiny-form :model="$Data.formData" :rules="$Data2.formRules" :ref="(el) => ($From.form = el)" class="login-form" label-width="70px" label-position="left">
3
- <tiny-form-item prop="username" label="用户名">
4
- <tiny-input v-model="$Data.formData.username" placeholder="请输入用户名" size="large" clearable>
5
- <template #prefix-icon>
6
- <i-lucide:user style="width: 18px; height: 18px" />
7
- </template>
8
- </tiny-input>
9
- </tiny-form-item>
10
-
11
- <tiny-form-item prop="email" label="邮箱">
12
- <tiny-input v-model="$Data.formData.email" placeholder="请输入邮箱" size="large" clearable>
13
- <template #prefix-icon>
14
- <i-lucide:mail style="width: 18px; height: 18px" />
15
- </template>
16
- </tiny-input>
17
- </tiny-form-item>
18
-
19
- <tiny-form-item prop="password" label="密码">
20
- <tiny-input v-model="$Data.formData.password" type="password" placeholder="请输入密码" size="large" clearable>
21
- <template #prefix-icon>
22
- <i-lucide:lock style="width: 18px; height: 18px" />
23
- </template>
24
- </tiny-input>
25
- </tiny-form-item>
26
-
27
- <tiny-form-item prop="nickname" label="昵称">
28
- <tiny-input v-model="$Data.formData.nickname" placeholder="请输入昵称(选填)" size="large" clearable>
29
- <template #prefix-icon>
30
- <i-lucide:smile style="width: 18px; height: 18px" />
31
- </template>
32
- </tiny-input>
33
- </tiny-form-item>
34
-
35
- <tiny-button theme="primary" class="auth-btn" size="large" :loading="$Data.loading" @click="$Method.handleSubmit"> 注册 </tiny-button>
36
- </tiny-form>
37
- </template>
38
-
39
- <script setup>
40
- const emit = defineEmits(['success']);
41
-
42
- // 表单引用
43
- const $From = $shallowRef({
44
- form: null
45
- });
46
-
47
- // 数据定义
48
- const $Data = $ref({
49
- loading: false,
50
- formData: {
51
- username: '',
52
- email: '',
53
- password: '',
54
- nickname: ''
55
- }
56
- });
57
-
58
- const $Data2 = $shallowRef({
59
- formRules: {
60
- username: [
61
- { required: true, message: '请输入用户名', trigger: 'blur' },
62
- { min: 3, max: 20, message: '用户名长度为 3-20 个字符', trigger: 'blur' }
63
- ],
64
- email: [
65
- { required: true, message: '请输入邮箱', trigger: 'blur' },
66
- { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
67
- ],
68
- password: [
69
- { required: true, message: '请输入密码', trigger: 'blur' },
70
- { min: 6, message: '密码长度至少 6 个字符', trigger: 'blur' }
71
- ]
72
- // nickname 是选填项,不需要验证规则
73
- }
74
- });
75
-
76
- // 方法定义
77
- const $Method = {
78
- async handleSubmit() {
79
- const valid = await $From.form.validate();
80
- if (!valid) return;
81
-
82
- $Data.loading = true;
83
-
84
- try {
85
- await $Http('/addon/admin/register', $Data.formData);
86
- MessagePlugin.success('注册成功,请登录');
87
-
88
- // 清空表单
89
- $Method.resetForm();
90
-
91
- // 通知父组件注册成功,切换到登录模式
92
- emit('success');
93
- } catch (error) {
94
- // 错误已经在 request 拦截器中处理
95
- } finally {
96
- $Data.loading = false;
97
- }
98
- },
99
-
100
- // 清空表单
101
- resetForm() {
102
- $Data.formData.username = '';
103
- $Data.formData.email = '';
104
- $Data.formData.password = '';
105
- $Data.formData.nickname = '';
106
- }
107
- };
108
- </script>
109
-
110
- <style scoped lang="scss">
111
- .login-form {
112
- width: 100%;
113
- max-width: 450px;
114
- }
115
-
116
- .tiny-form-item {
117
- width: 100%;
118
- margin-bottom: 1.2rem;
119
-
120
- :deep(.tiny-form__controls) {
121
- width: 100%;
122
- }
123
-
124
- :deep(.tiny-input) {
125
- width: 100%;
126
- background: #f8f9fa;
127
- border: 1px solid #e0e0e0;
128
- border-radius: 6px;
129
- transition: all 0.3s;
130
-
131
- &:hover {
132
- border-color: #48b19f;
133
- }
134
-
135
- &:focus-within {
136
- border-color: #48b19f;
137
- background: #fff;
138
- }
139
-
140
- input {
141
- padding: 0.75rem 1rem;
142
- }
143
- }
144
- }
145
-
146
- .auth-btn {
147
- width: 100% !important;
148
- max-width: 100%;
149
- height: 44px;
150
- border-radius: 6px;
151
- background: #48b19f;
152
- border: none;
153
- font-size: 0.95rem;
154
- font-weight: 600;
155
- margin-top: 0.5rem;
156
- transition: all 0.3s;
157
-
158
- &:hover {
159
- background: #3a9d8f;
160
- transform: translateY(-1px);
161
- box-shadow: 0 3px 10px rgba(72, 177, 159, 0.3);
162
- }
163
-
164
- :deep(.tiny-button__text) {
165
- color: #fff;
166
- }
167
- }
168
- </style>
@@ -1,61 +0,0 @@
1
- <template>
2
- <div class="panel-content" v-if="!isSignUp">
3
- <h2>你好,朋友!</h2>
4
- <p>填写个人信息,开始使用</p>
5
- <button class="toggle-btn" @click="$emit('toggle')">注册账号</button>
6
- </div>
7
- <div class="panel-content" v-else>
8
- <h2>欢迎回来!</h2>
9
- <p>使用您的账号登录</p>
10
- <button class="toggle-btn" @click="$emit('toggle')">立即登录</button>
11
- </div>
12
- </template>
13
-
14
- <script setup>
15
- const props = defineProps({
16
- isSignUp: {
17
- type: Boolean,
18
- default: false
19
- }
20
- });
21
-
22
- defineEmits(['toggle']);
23
- </script>
24
-
25
- <style scoped lang="scss">
26
- .panel-content {
27
- text-align: center;
28
- padding: 2rem;
29
- max-width: 400px;
30
-
31
- h2 {
32
- font-size: 2rem;
33
- font-weight: 600;
34
- margin-bottom: 1rem;
35
- }
36
-
37
- p {
38
- font-size: 1rem;
39
- line-height: 1.6;
40
- margin-bottom: 2rem;
41
- opacity: 0.9;
42
- }
43
-
44
- .toggle-btn {
45
- padding: 0.8rem 3rem;
46
- border: 2px solid #fff;
47
- background: transparent;
48
- color: #fff;
49
- border-radius: 25px;
50
- font-size: 0.9rem;
51
- font-weight: 600;
52
- cursor: pointer;
53
- transition: all 0.3s;
54
-
55
- &:hover {
56
- background: #fff;
57
- color: #48b19f;
58
- }
59
- }
60
- }
61
- </style>