befly-admin 3.5.32 → 3.5.34
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.
- package/README.md +32 -32
- package/package.json +20 -20
- package/src/App.vue +3 -3
- package/src/components/DetailPanel.vue +6 -6
- package/src/layouts/default.vue +139 -24
- package/src/main.js +13 -21
- package/src/plugins/config.js +29 -0
- package/src/plugins/global.js +1 -1
- package/src/plugins/http.js +11 -19
- package/src/plugins/router.js +45 -0
- package/src/plugins/storage.js +5 -12
- package/src/styles/global.scss +1 -1
- package/src/types/auto-imports.d.ts +5 -10
- package/src/types/components.d.ts +0 -27
- package/src/types/typed-router.d.ts +75 -23
- package/src/types/unplugin-vue-router-client.d.ts +3 -0
- package/src/views/dashboard/index.vue +117 -0
- package/src/views/index2.vue +9 -6
- package/vite.config.js +6 -5
- package/src/config/index.js +0 -20
- package/src/router/index.js +0 -86
- package/src/utils/index.js +0 -130
- package/src/views/index3.vue +0 -707
package/README.md
CHANGED
|
@@ -4,26 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
## 技术栈
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
7
|
+
- **Vue 3** - 渐进式 JavaScript 框架
|
|
8
|
+
- **TDesign Vue Next** - 企业级设计体系
|
|
9
|
+
- **TypeScript** - 类型安全
|
|
10
|
+
- **Vite** - 下一代前端构建工具
|
|
11
|
+
- **SCSS** - CSS 预处理器
|
|
12
|
+
- **Vue Router** - 路由管理
|
|
13
|
+
- **Pinia** - 状态管理
|
|
14
|
+
- **unplugin-auto-import** - API 自动导入
|
|
15
|
+
- **unplugin-vue-components** - 组件自动导入
|
|
16
16
|
|
|
17
17
|
## 特性
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
19
|
+
- ✅ 自动导入 Vue3 API(ref、reactive、computed 等)
|
|
20
|
+
- ✅ 自动导入 TDesign 组件(无需手动注册)
|
|
21
|
+
- ✅ 自动导入 Vue Router 和 Pinia API
|
|
22
|
+
- ✅ TypeScript 类型安全
|
|
23
|
+
- ✅ SCSS 支持(全局变量 + Mixins)
|
|
24
|
+
- ✅ 响应式布局
|
|
25
|
+
- ✅ 路由权限控制
|
|
26
|
+
- ✅ 代理配置(开发环境)
|
|
27
27
|
|
|
28
28
|
## 开发
|
|
29
29
|
|
|
@@ -72,14 +72,14 @@ admin/
|
|
|
72
72
|
|
|
73
73
|
```typescript
|
|
74
74
|
// ❌ 不需要这样写
|
|
75
|
-
import { ref, computed, onMounted } from
|
|
76
|
-
import { useRouter, useRoute } from
|
|
77
|
-
import { defineStore } from
|
|
75
|
+
import { ref, computed, onMounted } from "vue";
|
|
76
|
+
import { useRouter, useRoute } from "vue-router";
|
|
77
|
+
import { defineStore } from "pinia";
|
|
78
78
|
|
|
79
79
|
// ✅ 直接使用
|
|
80
80
|
const count = ref(0);
|
|
81
81
|
const router = useRouter();
|
|
82
|
-
const store = defineStore(
|
|
82
|
+
const store = defineStore("main", {});
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
### 组件自动导入
|
|
@@ -104,16 +104,16 @@ TDesign 组件无需手动导入和注册:
|
|
|
104
104
|
|
|
105
105
|
### Vite 配置
|
|
106
106
|
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
107
|
+
- **自动导入配置**: `unplugin-auto-import` 和 `unplugin-vue-components`
|
|
108
|
+
- **路径别名**: `@` 指向 `src` 目录
|
|
109
|
+
- **开发服务器**: 端口 5173,自动打开浏览器
|
|
110
|
+
- **代理配置**: `/api` 代理到 `http://localhost:3000`
|
|
111
111
|
|
|
112
112
|
### TypeScript 配置
|
|
113
113
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
114
|
+
- **严格模式**: 启用所有严格类型检查
|
|
115
|
+
- **路径映射**: `@/*` 映射到 `src/*`
|
|
116
|
+
- **自动生成类型**: `auto-imports.d.ts` 和 `components.d.ts`
|
|
117
117
|
|
|
118
118
|
## 构建
|
|
119
119
|
|
|
@@ -126,10 +126,10 @@ bun run build
|
|
|
126
126
|
|
|
127
127
|
构建优化:
|
|
128
128
|
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
129
|
+
- 代码分割(Vue、TDesign 单独打包)
|
|
130
|
+
- Tree-shaking
|
|
131
|
+
- 压缩优化
|
|
132
|
+
- Chunk size 限制:1500KB
|
|
133
133
|
|
|
134
134
|
## 注意事项
|
|
135
135
|
|
package/package.json
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.34",
|
|
4
|
+
"private": false,
|
|
4
5
|
"description": "Befly Admin - 基于 Vue3 + TDesign Vue Next 的后台管理系统",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"private": false,
|
|
7
|
-
"publishConfig": {
|
|
8
|
-
"access": "public",
|
|
9
|
-
"registry": "https://registry.npmjs.org"
|
|
10
|
-
},
|
|
11
7
|
"files": [
|
|
12
|
-
"src",
|
|
13
|
-
"public",
|
|
14
|
-
"index.html",
|
|
15
|
-
"vite.config.js",
|
|
16
8
|
".env",
|
|
17
9
|
".env.development",
|
|
18
10
|
".env.production",
|
|
19
11
|
".gitignore",
|
|
12
|
+
"README.md",
|
|
20
13
|
"bunfig.toml",
|
|
21
|
-
"
|
|
14
|
+
"index.html",
|
|
15
|
+
"public",
|
|
16
|
+
"src",
|
|
17
|
+
"vite.config.js"
|
|
22
18
|
],
|
|
23
19
|
"scripts": {
|
|
24
20
|
"dev": "vite",
|
|
@@ -26,21 +22,25 @@
|
|
|
26
22
|
"preview": "vite preview"
|
|
27
23
|
},
|
|
28
24
|
"dependencies": {
|
|
29
|
-
"@befly-addon/admin": "^1.1.
|
|
25
|
+
"@befly-addon/admin": "^1.1.32",
|
|
30
26
|
"@iconify-json/lucide": "^1.2.80",
|
|
31
27
|
"axios": "^1.13.2",
|
|
32
|
-
"befly-
|
|
33
|
-
"befly-vite": "^1.1.11",
|
|
28
|
+
"befly-vite": "^1.1.12",
|
|
34
29
|
"pinia": "^3.0.4",
|
|
35
|
-
"tdesign-vue-next": "^1.17.
|
|
36
|
-
"
|
|
37
|
-
"
|
|
30
|
+
"tdesign-vue-next": "^1.17.7",
|
|
31
|
+
"unplugin-vue-router": "^0.19.0",
|
|
32
|
+
"vite": "^8.0.0-beta.3",
|
|
33
|
+
"vue": "^3.5.26",
|
|
38
34
|
"vue-router": "^4.6.4"
|
|
39
35
|
},
|
|
40
36
|
"engines": {
|
|
37
|
+
"bun": ">=1.3.0",
|
|
41
38
|
"node": ">=24.0.0",
|
|
42
|
-
"pnpm": ">=10.0.0"
|
|
43
|
-
|
|
39
|
+
"pnpm": ">=10.0.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"registry": "https://registry.npmjs.org"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "0ba53d2bbdc6a7f2659ef60256cda7302fe585b0"
|
|
46
46
|
}
|
package/src/App.vue
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script setup>
|
|
10
|
-
import { ConfigProvider } from
|
|
10
|
+
import { ConfigProvider } from "tdesign-vue-next";
|
|
11
11
|
|
|
12
12
|
const globalConfig = {
|
|
13
13
|
dialog: {
|
|
@@ -15,12 +15,12 @@ const globalConfig = {
|
|
|
15
15
|
},
|
|
16
16
|
table: {
|
|
17
17
|
// 表格默认配置
|
|
18
|
-
size:
|
|
18
|
+
size: "medium",
|
|
19
19
|
bordered: false,
|
|
20
20
|
stripe: false,
|
|
21
21
|
showHeader: true,
|
|
22
22
|
// 列默认配置(需要在每个列上单独设置)
|
|
23
|
-
cellEmptyContent:
|
|
23
|
+
cellEmptyContent: "-"
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
</script>
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
33
|
<script setup>
|
|
34
|
-
import { computed } from
|
|
35
|
-
import { Tag as TTag } from
|
|
34
|
+
import { computed } from "vue";
|
|
35
|
+
import { Tag as TTag } from "tdesign-vue-next";
|
|
36
36
|
|
|
37
37
|
const props = defineProps({
|
|
38
38
|
/**
|
|
@@ -57,14 +57,14 @@ const props = defineProps({
|
|
|
57
57
|
*/
|
|
58
58
|
excludeKeys: {
|
|
59
59
|
type: Array,
|
|
60
|
-
default: () => [
|
|
60
|
+
default: () => ["row-select", "operation", "index"]
|
|
61
61
|
},
|
|
62
62
|
/**
|
|
63
63
|
* 空数据时的提示文字
|
|
64
64
|
*/
|
|
65
65
|
emptyText: {
|
|
66
66
|
type: String,
|
|
67
|
-
default:
|
|
67
|
+
default: "暂无数据"
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -92,8 +92,8 @@ const normalizedFields = computed(() => {
|
|
|
92
92
|
* @returns {string} 格式化后的值
|
|
93
93
|
*/
|
|
94
94
|
function formatValue(value, field) {
|
|
95
|
-
if (value === null || value === undefined || value ===
|
|
96
|
-
return field.default ||
|
|
95
|
+
if (value === null || value === undefined || value === "") {
|
|
96
|
+
return field.default || "-";
|
|
97
97
|
}
|
|
98
98
|
if (field.formatter) {
|
|
99
99
|
return field.formatter(value);
|
package/src/layouts/default.vue
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<!-- 无子菜单 -->
|
|
18
18
|
<t-menu-item v-if="!menu.children || menu.children.length === 0" :value="menu.path">
|
|
19
19
|
<template #icon>
|
|
20
|
-
<i-lucide:home v-if="menu.path === '/addon/admin/'" style="margin-right: 8px" />
|
|
20
|
+
<i-lucide:home v-if="menu.path === '/dashboard' || menu.path === '/addon/admin/' || menu.path === '/addon/admin'" style="margin-right: 8px" />
|
|
21
21
|
<i-lucide:file-text v-else style="margin-right: 8px" />
|
|
22
22
|
</template>
|
|
23
23
|
{{ menu.name }}
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
</t-upload>
|
|
57
57
|
<div class="user-info">
|
|
58
|
-
<span class="user-name">{{ $Data.userInfo.nickname ||
|
|
59
|
-
<span class="user-role">{{ $Data.userInfo.role ||
|
|
58
|
+
<span class="user-name">{{ $Data.userInfo.nickname || "管理员" }}</span>
|
|
59
|
+
<span class="user-role">{{ $Data.userInfo.role || "超级管理员" }}</span>
|
|
60
60
|
</div>
|
|
61
61
|
<t-button theme="default" variant="text" size="small" @click="$Method.handleLogout">
|
|
62
62
|
<template #icon>
|
|
@@ -75,12 +75,25 @@
|
|
|
75
75
|
</template>
|
|
76
76
|
|
|
77
77
|
<script setup>
|
|
78
|
-
import { arrayToTree } from
|
|
78
|
+
import { arrayToTree } from "befly-vite/utils/arrayToTree";
|
|
79
|
+
|
|
80
|
+
import { DialogPlugin } from "tdesign-vue-next";
|
|
79
81
|
|
|
80
82
|
const router = useRouter();
|
|
81
83
|
const route = useRoute();
|
|
82
84
|
const global = useGlobal();
|
|
83
85
|
|
|
86
|
+
const loginPath = "/addon/admin/login";
|
|
87
|
+
|
|
88
|
+
const normalizePath = (path) => {
|
|
89
|
+
if (typeof path !== "string") {
|
|
90
|
+
return path;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const normalized = path.replace(/\/+$/, "");
|
|
94
|
+
return normalized.length === 0 ? "/" : normalized;
|
|
95
|
+
};
|
|
96
|
+
|
|
84
97
|
const $From = {
|
|
85
98
|
treeMenuRef: null
|
|
86
99
|
};
|
|
@@ -90,11 +103,11 @@ const $Data = $ref({
|
|
|
90
103
|
userMenus: [],
|
|
91
104
|
userMenusFlat: [], // 一维菜单数据
|
|
92
105
|
expandedKeys: [],
|
|
93
|
-
currentMenuKey:
|
|
106
|
+
currentMenuKey: "",
|
|
94
107
|
userInfo: {
|
|
95
|
-
nickname:
|
|
96
|
-
role:
|
|
97
|
-
avatar:
|
|
108
|
+
nickname: "管理员",
|
|
109
|
+
role: "超级管理员",
|
|
110
|
+
avatar: "" // 用户头像
|
|
98
111
|
}
|
|
99
112
|
});
|
|
100
113
|
|
|
@@ -103,22 +116,89 @@ const $Method = {
|
|
|
103
116
|
// 获取用户菜单权限
|
|
104
117
|
async fetchUserMenus() {
|
|
105
118
|
try {
|
|
106
|
-
const { data } = await $Http(
|
|
119
|
+
const { data } = await $Http("/addon/admin/menu/all");
|
|
120
|
+
const lists = Array.isArray(data?.lists) ? data.lists : [];
|
|
121
|
+
|
|
122
|
+
const bizMenus = [];
|
|
123
|
+
const routeRecords = router.getRoutes();
|
|
124
|
+
|
|
125
|
+
for (const record of routeRecords) {
|
|
126
|
+
const path = normalizePath(record.path);
|
|
127
|
+
|
|
128
|
+
if (typeof path !== "string") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (path === "/") {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (path === normalizePath(loginPath)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (path.startsWith("/addon/")) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const title = typeof record.meta?.title === "string" ? record.meta.title : "";
|
|
145
|
+
if (title.length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (bizMenus.some((m) => m.path === path)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
bizMenus.push({
|
|
154
|
+
id: `biz_${path.replace(/[^a-zA-Z0-9]+/g, "_")}`,
|
|
155
|
+
pid: "biz",
|
|
156
|
+
name: title,
|
|
157
|
+
path: path
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
bizMenus.sort((a, b) => String(a.path).localeCompare(String(b.path)));
|
|
162
|
+
|
|
163
|
+
const bizMenusFlat =
|
|
164
|
+
bizMenus.length > 0
|
|
165
|
+
? [
|
|
166
|
+
{
|
|
167
|
+
id: "biz",
|
|
168
|
+
pid: 0,
|
|
169
|
+
name: "业务",
|
|
170
|
+
path: ""
|
|
171
|
+
}
|
|
172
|
+
].concat(bizMenus)
|
|
173
|
+
: [];
|
|
174
|
+
|
|
175
|
+
const normalizedLists = lists.map((menu) => {
|
|
176
|
+
const menuPath = normalizePath(menu?.path);
|
|
177
|
+
return Object.assign({}, menu, { path: menuPath });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const mergedLists = bizMenusFlat.concat(normalizedLists);
|
|
181
|
+
|
|
107
182
|
// 保存一维数据(data 是 { lists: [] } 格式)
|
|
108
|
-
$Data.userMenusFlat =
|
|
109
|
-
$Data.userMenus = arrayToTree(
|
|
183
|
+
$Data.userMenusFlat = mergedLists;
|
|
184
|
+
$Data.userMenus = arrayToTree(mergedLists);
|
|
110
185
|
$Method.setActiveMenu();
|
|
111
186
|
} catch (error) {
|
|
112
|
-
|
|
187
|
+
MessagePlugin.error("获取用户菜单失败");
|
|
113
188
|
}
|
|
114
189
|
},
|
|
115
190
|
|
|
116
191
|
// 设置当前激活的菜单(从一维数据查找并构建父级链)
|
|
117
192
|
setActiveMenu() {
|
|
118
193
|
const currentPath = route.path;
|
|
194
|
+
const normalizedCurrentPath = normalizePath(currentPath);
|
|
119
195
|
|
|
120
196
|
// 在一维数据中查找当前路径对应的菜单
|
|
121
|
-
const currentMenu = $Data.userMenusFlat.find((menu) =>
|
|
197
|
+
const currentMenu = $Data.userMenusFlat.find((menu) => {
|
|
198
|
+
const menuPath = menu.path;
|
|
199
|
+
const normalizedMenuPath = normalizePath(menuPath);
|
|
200
|
+
return normalizedMenuPath === normalizedCurrentPath;
|
|
201
|
+
});
|
|
122
202
|
|
|
123
203
|
if (!currentMenu) {
|
|
124
204
|
return;
|
|
@@ -146,41 +226,76 @@ const $Method = {
|
|
|
146
226
|
|
|
147
227
|
// 处理菜单点击
|
|
148
228
|
onMenuClick(path) {
|
|
149
|
-
if (path) {
|
|
229
|
+
if (typeof path === "string" && path.startsWith("/")) {
|
|
150
230
|
router.push(path);
|
|
151
231
|
}
|
|
152
232
|
},
|
|
153
233
|
|
|
154
234
|
// 处理退出登录
|
|
155
235
|
handleLogout() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
236
|
+
let dialog = null;
|
|
237
|
+
let destroyed = false;
|
|
238
|
+
|
|
239
|
+
const destroy = () => {
|
|
240
|
+
if (destroyed) return;
|
|
241
|
+
destroyed = true;
|
|
242
|
+
if (dialog && typeof dialog.destroy === "function") {
|
|
160
243
|
dialog.destroy();
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
dialog = DialogPlugin.confirm({
|
|
248
|
+
header: "确认退出登录",
|
|
249
|
+
body: "确定要退出登录吗?",
|
|
250
|
+
status: "warning",
|
|
251
|
+
onConfirm: async () => {
|
|
252
|
+
if (dialog && typeof dialog.setConfirmLoading === "function") {
|
|
253
|
+
dialog.setConfirmLoading(true);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
$Storage.local.remove("token");
|
|
258
|
+
await router.push(loginPath);
|
|
259
|
+
MessagePlugin.success("退出成功");
|
|
260
|
+
destroy();
|
|
261
|
+
} catch (error) {
|
|
262
|
+
MessagePlugin.error("退出失败");
|
|
263
|
+
destroy();
|
|
264
|
+
} finally {
|
|
265
|
+
if (dialog && typeof dialog.setConfirmLoading === "function") {
|
|
266
|
+
dialog.setConfirmLoading(false);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
onClose: () => {
|
|
271
|
+
destroy();
|
|
164
272
|
}
|
|
165
273
|
});
|
|
166
274
|
},
|
|
167
275
|
|
|
168
276
|
// 处理系统设置
|
|
169
277
|
handleSettings() {
|
|
170
|
-
router.push(
|
|
278
|
+
router.push("/addon/admin/settings");
|
|
171
279
|
},
|
|
172
280
|
|
|
173
281
|
// 头像上传成功
|
|
174
282
|
onAvatarUploadSuccess(res) {
|
|
175
283
|
if (res.response?.code === 0 && res.response?.data?.url) {
|
|
176
284
|
$Data.userInfo.avatar = res.response.data.url;
|
|
177
|
-
MessagePlugin.success(
|
|
285
|
+
MessagePlugin.success("头像上传成功");
|
|
178
286
|
// TODO: 可以调用接口保存用户头像
|
|
179
287
|
}
|
|
180
288
|
}
|
|
181
289
|
};
|
|
182
290
|
|
|
183
291
|
$Method.fetchUserMenus();
|
|
292
|
+
|
|
293
|
+
watch(
|
|
294
|
+
() => route.path,
|
|
295
|
+
() => {
|
|
296
|
+
$Method.setActiveMenu();
|
|
297
|
+
}
|
|
298
|
+
);
|
|
184
299
|
</script>
|
|
185
300
|
|
|
186
301
|
<style scoped lang="scss">
|
|
@@ -260,7 +375,7 @@ $Method.fetchUserMenus();
|
|
|
260
375
|
font-weight: var(--font-weight-medium);
|
|
261
376
|
|
|
262
377
|
&::before {
|
|
263
|
-
content:
|
|
378
|
+
content: "";
|
|
264
379
|
position: absolute;
|
|
265
380
|
left: 0;
|
|
266
381
|
top: 50%;
|
package/src/main.js
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// 引入 TDesign 组件
|
|
2
|
+
import { Table as TTable } from "tdesign-vue-next";
|
|
3
3
|
// 引入 TDesign 样式
|
|
4
|
-
import
|
|
5
|
-
|
|
4
|
+
import "tdesign-vue-next/es/style/index.css";
|
|
6
5
|
// 引入 UnoCSS 样式
|
|
7
|
-
import
|
|
8
|
-
|
|
6
|
+
import "virtual:uno.css";
|
|
9
7
|
// 引入 addonAdmin 的 CSS 变量
|
|
10
|
-
import
|
|
11
|
-
|
|
8
|
+
import "@befly-addon/admin/styles/variables.scss";
|
|
12
9
|
// 引入全局基础样式(reset、通用类、滚动条等)
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
// 引入路由实例
|
|
16
|
-
import { router } from './router';
|
|
17
|
-
|
|
18
|
-
// 引入 TDesign 组件
|
|
19
|
-
import { Table as TTable } from 'tdesign-vue-next';
|
|
10
|
+
import "@/styles/global.scss";
|
|
11
|
+
import App from "./App.vue";
|
|
20
12
|
|
|
21
13
|
const app = createApp(App);
|
|
22
14
|
|
|
@@ -27,21 +19,21 @@ app.use(createPinia());
|
|
|
27
19
|
app.use(router);
|
|
28
20
|
|
|
29
21
|
// 全局配置 TTable 默认属性
|
|
30
|
-
app.component(
|
|
22
|
+
app.component("TTable", {
|
|
31
23
|
...TTable,
|
|
32
24
|
props: {
|
|
33
25
|
...TTable.props,
|
|
34
26
|
bordered: {
|
|
35
27
|
type: [Boolean, Object],
|
|
36
|
-
default: () => ({ cell:
|
|
28
|
+
default: () => ({ cell: "horizontal" })
|
|
37
29
|
},
|
|
38
30
|
size: {
|
|
39
31
|
type: String,
|
|
40
|
-
default:
|
|
32
|
+
default: "small"
|
|
41
33
|
},
|
|
42
34
|
height: {
|
|
43
35
|
type: [String, Number],
|
|
44
|
-
default:
|
|
36
|
+
default: "100%"
|
|
45
37
|
},
|
|
46
38
|
selectOnRowClick: {
|
|
47
39
|
type: Boolean,
|
|
@@ -49,9 +41,9 @@ app.component('TTable', {
|
|
|
49
41
|
},
|
|
50
42
|
activeRowType: {
|
|
51
43
|
type: String,
|
|
52
|
-
default:
|
|
44
|
+
default: "single"
|
|
53
45
|
}
|
|
54
46
|
}
|
|
55
47
|
});
|
|
56
48
|
|
|
57
|
-
app.mount(
|
|
49
|
+
app.mount("#app");
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内部配置
|
|
3
|
+
* 存放框架内置的配置变量,不建议修改
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 内置配置对象
|
|
8
|
+
*/
|
|
9
|
+
export const $Config = {
|
|
10
|
+
/** 应用标题 */
|
|
11
|
+
appTitle: import.meta.env.VITE_APP_TITLE || "野蜂飞舞",
|
|
12
|
+
/** API 基础地址 */
|
|
13
|
+
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || "",
|
|
14
|
+
|
|
15
|
+
/** 上传地址(优先使用 VITE_UPLOAD_URL;否则基于 VITE_API_BASE_URL 拼接 /upload) */
|
|
16
|
+
uploadUrl: import.meta.env.VITE_UPLOAD_URL || ((import.meta.env.VITE_API_BASE_URL || "").length > 0 ? `${import.meta.env.VITE_API_BASE_URL}/upload` : "/upload"),
|
|
17
|
+
/** 存储命名空间 */
|
|
18
|
+
storageNamespace: import.meta.env.VITE_STORAGE_NAMESPACE || "befly_admin",
|
|
19
|
+
|
|
20
|
+
/** 登录页路径(可通过 VITE_LOGIN_PATH 覆盖) */
|
|
21
|
+
loginPath: import.meta.env.VITE_LOGIN_PATH || "/addon/admin/login",
|
|
22
|
+
|
|
23
|
+
/** 首页路径(可通过 VITE_HOME_PATH 覆盖) */
|
|
24
|
+
homePath: import.meta.env.VITE_HOME_PATH || "/dashboard",
|
|
25
|
+
/** 是否开发环境 */
|
|
26
|
+
isDev: import.meta.env.DEV,
|
|
27
|
+
/** 是否生产环境 */
|
|
28
|
+
isProd: import.meta.env.PROD
|
|
29
|
+
};
|
package/src/plugins/global.js
CHANGED
package/src/plugins/http.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import axios from
|
|
2
|
-
import { MessagePlugin } from
|
|
3
|
-
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { MessagePlugin } from "tdesign-vue-next";
|
|
3
|
+
|
|
4
|
+
import { $Storage } from "./storage";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {Object} ApiResponse
|
|
@@ -14,7 +15,7 @@ const request = axios.create({
|
|
|
14
15
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
15
16
|
timeout: 10000,
|
|
16
17
|
headers: {
|
|
17
|
-
|
|
18
|
+
"Content-Type": "application/json"
|
|
18
19
|
}
|
|
19
20
|
});
|
|
20
21
|
|
|
@@ -22,22 +23,13 @@ const request = axios.create({
|
|
|
22
23
|
request.interceptors.request.use(
|
|
23
24
|
(config) => {
|
|
24
25
|
// 添加 token
|
|
25
|
-
const token = $Storage.local.get(
|
|
26
|
+
const token = $Storage.local.get("token");
|
|
26
27
|
if (token) {
|
|
27
28
|
config.headers.Authorization = `Bearer ${token}`;
|
|
28
|
-
// 开发环境下打印 token 信息
|
|
29
|
-
if (import.meta.env.DEV) {
|
|
30
|
-
console.log('[HTTP] 请求携带 token:', token.substring(0, 20) + '...');
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
if (import.meta.env.DEV) {
|
|
34
|
-
console.log('[HTTP] 请求未携带 token');
|
|
35
|
-
}
|
|
36
29
|
}
|
|
37
30
|
return config;
|
|
38
31
|
},
|
|
39
32
|
(error) => {
|
|
40
|
-
console.error('[Request] 请求错误:', error);
|
|
41
33
|
return Promise.reject(error);
|
|
42
34
|
}
|
|
43
35
|
);
|
|
@@ -51,7 +43,7 @@ request.interceptors.response.use(
|
|
|
51
43
|
if (res.code !== 0) {
|
|
52
44
|
return Promise.reject({
|
|
53
45
|
code: res.code,
|
|
54
|
-
msg: res.msg ||
|
|
46
|
+
msg: res.msg || "请求失败",
|
|
55
47
|
data: res.data
|
|
56
48
|
});
|
|
57
49
|
}
|
|
@@ -60,10 +52,10 @@ request.interceptors.response.use(
|
|
|
60
52
|
return res;
|
|
61
53
|
},
|
|
62
54
|
(error) => {
|
|
63
|
-
MessagePlugin.error(
|
|
55
|
+
MessagePlugin.error("网络连接失败");
|
|
64
56
|
return Promise.reject({
|
|
65
57
|
code: -1,
|
|
66
|
-
msg:
|
|
58
|
+
msg: "网络连接失败",
|
|
67
59
|
error: error
|
|
68
60
|
});
|
|
69
61
|
}
|
|
@@ -78,11 +70,11 @@ request.interceptors.response.use(
|
|
|
78
70
|
* @param {import('axios').AxiosRequestConfig} [config] - axios 请求配置
|
|
79
71
|
* @returns {Promise<any>} 成功返回 data,失败抛出 {code, msg, data} 对象
|
|
80
72
|
*/
|
|
81
|
-
export function $Http(url, data = {}, method =
|
|
73
|
+
export function $Http(url, data = {}, method = "post", config) {
|
|
82
74
|
const methodLower = method.toLowerCase();
|
|
83
75
|
|
|
84
76
|
// GET 请求将 data 作为 params
|
|
85
|
-
if (methodLower ===
|
|
77
|
+
if (methodLower === "get") {
|
|
86
78
|
return request.get(url, {
|
|
87
79
|
...config,
|
|
88
80
|
params: data
|