befly-admin 3.12.17 → 3.13.0
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 +12 -14
- package/index.html +1 -1
- package/package.json +10 -11
- package/src/App.vue +1 -1
- package/src/components/detailPanel.vue +15 -27
- package/src/components/pageDialog.vue +39 -63
- package/src/components/pagedTableDetail.vue +79 -132
- package/src/layouts/1.vue +1 -1
- package/src/layouts/2.vue +1 -1
- package/src/layouts/default.vue +20 -18
- package/src/{main.ts → main.js} +2 -2
- package/src/plugins/{config.ts → config.js} +3 -14
- package/src/plugins/{global.ts → global.js} +2 -2
- package/src/plugins/{http.ts → http.js} +22 -44
- package/src/plugins/{router.ts → router.js} +2 -2
- package/src/plugins/{storage.ts → storage.js} +26 -45
- package/src/utils/is.js +6 -0
- package/src/views/index2.vue +15 -2
- package/vite.config.js +1 -2
- package/src/types/auto-imports.d.ts +0 -191
- package/src/types/components.d.ts +0 -20
- package/src/types/typed-router.d.ts +0 -290
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Befly Admin
|
|
2
2
|
|
|
3
|
-
基于 Vue3 + TDesign Vue Next +
|
|
3
|
+
基于 Vue3 + TDesign Vue Next + JavaScript + Vite 的后台管理系统。
|
|
4
4
|
|
|
5
5
|
## 技术栈
|
|
6
6
|
|
|
7
7
|
- **Vue 3** - 渐进式 JavaScript 框架
|
|
8
8
|
- **TDesign Vue Next** - 企业级设计体系
|
|
9
|
-
- **
|
|
9
|
+
- **JavaScript** - 运行时语言
|
|
10
10
|
- **Vite** - 下一代前端构建工具
|
|
11
11
|
- **SCSS** - CSS 预处理器
|
|
12
12
|
- **Vue Router** - 路由管理
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- ✅ 自动导入 Vue3 API(ref、reactive、computed 等)
|
|
20
20
|
- ✅ 自动导入 TDesign 组件(无需手动注册)
|
|
21
21
|
- ✅ 自动导入 Vue Router 和 Pinia API
|
|
22
|
-
- ✅
|
|
22
|
+
- ✅ JavaScript
|
|
23
23
|
- ✅ SCSS 支持(全局变量 + Mixins)
|
|
24
24
|
- ✅ 响应式布局
|
|
25
25
|
- ✅ 路由权限控制
|
|
@@ -41,7 +41,7 @@ bun run build
|
|
|
41
41
|
bun run preview
|
|
42
42
|
|
|
43
43
|
# 类型检查
|
|
44
|
-
|
|
44
|
+
(已移除类型检查相关步骤)
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
## 项目结构
|
|
@@ -55,12 +55,12 @@ admin/
|
|
|
55
55
|
│ │ └── dashboard/ # 仪表盘
|
|
56
56
|
│ ├── router/ # 路由配置
|
|
57
57
|
│ ├── styles/ # 全局样式
|
|
58
|
-
│ ├── types/ #
|
|
58
|
+
│ ├── types/ # 类型定义(如有)
|
|
59
59
|
│ ├── App.vue # 根组件
|
|
60
|
-
│ └── main.
|
|
60
|
+
│ └── main.js # 入口文件
|
|
61
61
|
├── index.html
|
|
62
|
-
├── vite.config.
|
|
63
|
-
├──
|
|
62
|
+
├── vite.config.js # Vite 配置
|
|
63
|
+
├── jsconfig.json # JavaScript 配置
|
|
64
64
|
└── package.json
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -70,7 +70,7 @@ admin/
|
|
|
70
70
|
|
|
71
71
|
无需手动导入以下 API,直接使用即可:
|
|
72
72
|
|
|
73
|
-
```
|
|
73
|
+
```javascript
|
|
74
74
|
// ❌ 不需要这样写
|
|
75
75
|
import { ref, computed, onMounted } from "vue";
|
|
76
76
|
import { useRouter, useRoute } from "vue-router";
|
|
@@ -94,7 +94,7 @@ TDesign 组件无需手动导入和注册:
|
|
|
94
94
|
<t-table :data="tableData" />
|
|
95
95
|
</template>
|
|
96
96
|
|
|
97
|
-
<script setup
|
|
97
|
+
<script setup>
|
|
98
98
|
// ❌ 不需要这样写
|
|
99
99
|
// import { Button, Input, Table } from 'tdesign-vue-next';
|
|
100
100
|
</script>
|
|
@@ -109,11 +109,9 @@ TDesign 组件无需手动导入和注册:
|
|
|
109
109
|
- **开发服务器**: 端口 5173,自动打开浏览器
|
|
110
110
|
- **代理配置**: `/api` 代理到 `http://localhost:3000`
|
|
111
111
|
|
|
112
|
-
###
|
|
112
|
+
### JavaScript 配置
|
|
113
113
|
|
|
114
|
-
- **严格模式**: 启用所有严格类型检查
|
|
115
114
|
- **路径映射**: `@/*` 映射到 `src/*`
|
|
116
|
-
- **自动生成类型**: `auto-imports.d.ts` 和 `components.d.ts`
|
|
117
115
|
|
|
118
116
|
## 构建
|
|
119
117
|
|
|
@@ -133,7 +131,7 @@ bun run build
|
|
|
133
131
|
|
|
134
132
|
## 注意事项
|
|
135
133
|
|
|
136
|
-
1.
|
|
134
|
+
1. **自动导入**:按需配置自动导入范围
|
|
137
135
|
2. **登录功能**:当前为模拟登录,需要对接真实 API
|
|
138
136
|
3. **Token 存储**:使用 localStorage,生产环境建议使用更安全的方式
|
|
139
137
|
4. **API 代理**:开发环境 `/api` 代理到 `http://localhost:3000`
|
package/index.html
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "3.13.0",
|
|
4
|
+
"gitHead": "0e5130c904374bdb3104dbdb422e62e3bae77902",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly Admin - 基于 Vue3 + TDesign Vue Next 的后台管理系统",
|
|
7
7
|
"files": [
|
|
@@ -28,16 +28,15 @@
|
|
|
28
28
|
"preview": "bunx --bun vite preview"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"befly-shared": "^1.4.5",
|
|
35
|
-
"befly-vite": "^1.4.14",
|
|
31
|
+
"axios": "^1.13.5",
|
|
32
|
+
"befly-admin-ui": "1.8.14",
|
|
33
|
+
"befly-vite": "^1.5.0",
|
|
36
34
|
"pinia": "^3.0.4",
|
|
37
|
-
"tdesign-vue-next": "^
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"vue
|
|
35
|
+
"tdesign-icons-vue-next": "^0.4.0",
|
|
36
|
+
"tdesign-vue-next": "^1.18.2",
|
|
37
|
+
"vite": "^8.0.0-beta.15",
|
|
38
|
+
"vue": "^3.5.28",
|
|
39
|
+
"vue-router": "^5.0.3"
|
|
41
40
|
},
|
|
42
41
|
"engines": {
|
|
43
42
|
"bun": ">=1.3.0",
|
package/src/App.vue
CHANGED
|
@@ -30,20 +30,8 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
|
-
<script setup
|
|
34
|
-
|
|
35
|
-
colKey?: string;
|
|
36
|
-
title?: string;
|
|
37
|
-
default?: string;
|
|
38
|
-
formatter?: (value: unknown) => unknown;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
type DetailPanelFieldNormalized = {
|
|
42
|
-
colKey: string;
|
|
43
|
-
title: string;
|
|
44
|
-
default?: string;
|
|
45
|
-
formatter?: (value: unknown) => unknown;
|
|
46
|
-
};
|
|
33
|
+
<script setup>
|
|
34
|
+
import { isString } from "../utils/is.js";
|
|
47
35
|
|
|
48
36
|
const props = defineProps({
|
|
49
37
|
/**
|
|
@@ -83,19 +71,19 @@ const props = defineProps({
|
|
|
83
71
|
* 所以这里必须暴露为顶层 computed,避免 v-for 误迭代 computed 对象本身。
|
|
84
72
|
*/
|
|
85
73
|
const normalizedFields = computed(() => {
|
|
86
|
-
const row = props.data
|
|
74
|
+
const row = props.data && typeof props.data === "object" ? props.data : null;
|
|
87
75
|
const dataId = row && Object.hasOwn(row, "id") ? row.id : undefined;
|
|
88
76
|
|
|
89
|
-
const rawFields = props.fields
|
|
77
|
+
const rawFields = props.fields;
|
|
90
78
|
const inputFields = Array.isArray(rawFields) ? rawFields : [];
|
|
91
|
-
const excludeKeys = props.excludeKeys
|
|
79
|
+
const excludeKeys = Array.isArray(props.excludeKeys) ? props.excludeKeys : [];
|
|
92
80
|
|
|
93
81
|
const fields = inputFields
|
|
94
|
-
.filter((item)
|
|
82
|
+
.filter((item) => {
|
|
95
83
|
return Boolean(item) && typeof item === "object";
|
|
96
84
|
})
|
|
97
|
-
.map((item)
|
|
98
|
-
const colKey =
|
|
85
|
+
.map((item) => {
|
|
86
|
+
const colKey = isString(item.colKey) ? item.colKey : "";
|
|
99
87
|
if (colKey.length === 0) {
|
|
100
88
|
return null;
|
|
101
89
|
}
|
|
@@ -104,7 +92,7 @@ const normalizedFields = computed(() => {
|
|
|
104
92
|
}
|
|
105
93
|
|
|
106
94
|
const titleRaw = item.title;
|
|
107
|
-
const title =
|
|
95
|
+
const title = isString(titleRaw) && titleRaw.length > 0 ? titleRaw : colKey;
|
|
108
96
|
|
|
109
97
|
return {
|
|
110
98
|
colKey: colKey,
|
|
@@ -113,7 +101,7 @@ const normalizedFields = computed(() => {
|
|
|
113
101
|
formatter: item.formatter
|
|
114
102
|
};
|
|
115
103
|
})
|
|
116
|
-
.filter((item)
|
|
104
|
+
.filter((item) => {
|
|
117
105
|
return Boolean(item);
|
|
118
106
|
});
|
|
119
107
|
|
|
@@ -131,23 +119,23 @@ const normalizedFields = computed(() => {
|
|
|
131
119
|
if (!item || typeof item !== "object") {
|
|
132
120
|
return false;
|
|
133
121
|
}
|
|
134
|
-
const record = item
|
|
122
|
+
const record = item;
|
|
135
123
|
const colKey = record["colKey"];
|
|
136
|
-
return
|
|
124
|
+
return isString(colKey) && colKey.length > 0;
|
|
137
125
|
});
|
|
138
126
|
|
|
139
127
|
return safeFields;
|
|
140
128
|
});
|
|
141
129
|
|
|
142
|
-
function formatValue(value
|
|
130
|
+
function formatValue(value, field) {
|
|
143
131
|
if (value === null || value === undefined || value === "") {
|
|
144
132
|
return field.default || "-";
|
|
145
133
|
}
|
|
146
134
|
if (field.formatter) {
|
|
147
135
|
const result = field.formatter(value);
|
|
148
|
-
return
|
|
136
|
+
return isString(result) ? result : String(result);
|
|
149
137
|
}
|
|
150
|
-
return
|
|
138
|
+
return isString(value) ? value : String(value);
|
|
151
139
|
}
|
|
152
140
|
</script>
|
|
153
141
|
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<TDialog
|
|
3
|
-
v-model:visible="innerVisible"
|
|
4
|
-
:header="props.title === '' ? true : props.title"
|
|
5
|
-
:width="props.width"
|
|
6
|
-
placement="center"
|
|
7
|
-
attach="body"
|
|
8
|
-
:close-btn="false"
|
|
9
|
-
:footer="true"
|
|
10
|
-
:confirm-loading="props.confirmLoading"
|
|
11
|
-
:close-on-overlay-click="false"
|
|
12
|
-
:close-on-esc-keydown="false"
|
|
13
|
-
@close="onDialogClose"
|
|
14
|
-
@confirm="onDialogConfirm"
|
|
15
|
-
@cancel="onDialogCancel"
|
|
16
|
-
>
|
|
2
|
+
<TDialog v-model:visible="innerVisible" :header="props.title === '' ? true : props.title" :width="props.width" placement="center" attach="body" :close-btn="false" :footer="true" :confirm-loading="props.confirmLoading" :close-on-overlay-click="false" :close-on-esc-keydown="false" @close="onDialogClose" @confirm="onDialogConfirm" @cancel="onDialogCancel">
|
|
17
3
|
<template #footer>
|
|
18
4
|
<div class="dialog-footer">
|
|
19
5
|
<TButton variant="outline" @click="onFooterClose">关闭</TButton>
|
|
@@ -28,7 +14,7 @@
|
|
|
28
14
|
</TDialog>
|
|
29
15
|
</template>
|
|
30
16
|
|
|
31
|
-
<script setup
|
|
17
|
+
<script setup>
|
|
32
18
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
33
19
|
|
|
34
20
|
import { Button as TButton, Dialog as TDialog, Popconfirm as TPopconfirm } from "tdesign-vue-next";
|
|
@@ -37,48 +23,38 @@ defineOptions({
|
|
|
37
23
|
inheritAttrs: false
|
|
38
24
|
});
|
|
39
25
|
|
|
40
|
-
type DialogTrigger = "close" | "confirm" | "cancel";
|
|
41
|
-
|
|
42
|
-
type PageDialogEventContext = {
|
|
43
|
-
trigger: DialogTrigger;
|
|
44
|
-
visible: boolean;
|
|
45
|
-
open: () => void;
|
|
46
|
-
close: () => void;
|
|
47
|
-
toggle: () => void;
|
|
48
|
-
getVisible: () => boolean;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
26
|
const openDelay = 100;
|
|
52
27
|
const closeDelay = 300;
|
|
53
28
|
|
|
54
|
-
const props =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
modelValue: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: undefined
|
|
33
|
+
},
|
|
34
|
+
title: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: ""
|
|
37
|
+
},
|
|
38
|
+
width: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: "600px"
|
|
41
|
+
},
|
|
42
|
+
confirmLoading: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
default: false
|
|
45
|
+
},
|
|
46
|
+
isConfirm: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: true
|
|
68
49
|
}
|
|
69
|
-
);
|
|
50
|
+
});
|
|
70
51
|
|
|
71
|
-
const emit = defineEmits
|
|
72
|
-
(e: "update:modelValue", value: boolean): void;
|
|
73
|
-
(e: "close", context: PageDialogEventContext): void;
|
|
74
|
-
(e: "confirm", context: PageDialogEventContext): void;
|
|
75
|
-
(e: "cancel", context: PageDialogEventContext): void;
|
|
76
|
-
}>();
|
|
52
|
+
const emit = defineEmits(["update:modelValue", "close", "confirm", "cancel"]);
|
|
77
53
|
|
|
78
|
-
let openDelayTimer
|
|
79
|
-
let closeDelayTimer
|
|
54
|
+
let openDelayTimer = null;
|
|
55
|
+
let closeDelayTimer = null;
|
|
80
56
|
|
|
81
|
-
function clearTimer(timer
|
|
57
|
+
function clearTimer(timer) {
|
|
82
58
|
if (timer) {
|
|
83
59
|
clearTimeout(timer);
|
|
84
60
|
}
|
|
@@ -86,13 +62,13 @@ function clearTimer(timer: ReturnType<typeof setTimeout> | null): null {
|
|
|
86
62
|
}
|
|
87
63
|
|
|
88
64
|
// 所有弹框约定:始终使用 v-if + v-model(modelValue)
|
|
89
|
-
const innerVisible = ref
|
|
65
|
+
const innerVisible = ref(false);
|
|
90
66
|
|
|
91
|
-
function open()
|
|
67
|
+
function open() {
|
|
92
68
|
emit("update:modelValue", true);
|
|
93
69
|
}
|
|
94
70
|
|
|
95
|
-
function close()
|
|
71
|
+
function close() {
|
|
96
72
|
openDelayTimer = clearTimer(openDelayTimer);
|
|
97
73
|
closeDelayTimer = clearTimer(closeDelayTimer);
|
|
98
74
|
|
|
@@ -104,7 +80,7 @@ function close(): void {
|
|
|
104
80
|
}, closeDelay);
|
|
105
81
|
}
|
|
106
82
|
|
|
107
|
-
function toggle()
|
|
83
|
+
function toggle() {
|
|
108
84
|
if (innerVisible.value) {
|
|
109
85
|
close();
|
|
110
86
|
return;
|
|
@@ -112,7 +88,7 @@ function toggle(): void {
|
|
|
112
88
|
open();
|
|
113
89
|
}
|
|
114
90
|
|
|
115
|
-
function createEventContext(trigger
|
|
91
|
+
function createEventContext(trigger) {
|
|
116
92
|
return {
|
|
117
93
|
trigger: trigger,
|
|
118
94
|
visible: innerVisible.value,
|
|
@@ -123,7 +99,7 @@ function createEventContext(trigger: DialogTrigger): PageDialogEventContext {
|
|
|
123
99
|
};
|
|
124
100
|
}
|
|
125
101
|
|
|
126
|
-
function applyModelValue(value
|
|
102
|
+
function applyModelValue(value) {
|
|
127
103
|
if (value) {
|
|
128
104
|
// 关键点:先渲染为 false,再延迟 true,保证 v-if + 初次 visible=true 时也能触发进入动画
|
|
129
105
|
openDelayTimer = clearTimer(openDelayTimer);
|
|
@@ -162,7 +138,7 @@ watch(
|
|
|
162
138
|
{ immediate: false }
|
|
163
139
|
);
|
|
164
140
|
|
|
165
|
-
function closeByTrigger(trigger
|
|
141
|
+
function closeByTrigger(trigger) {
|
|
166
142
|
// 固定交互:confirm 不自动关闭(等待异步提交成功后由调用侧 context.close() 决定关闭)
|
|
167
143
|
if (trigger === "confirm") {
|
|
168
144
|
return;
|
|
@@ -171,31 +147,31 @@ function closeByTrigger(trigger: DialogTrigger): void {
|
|
|
171
147
|
close();
|
|
172
148
|
}
|
|
173
149
|
|
|
174
|
-
function onDialogClose()
|
|
150
|
+
function onDialogClose() {
|
|
175
151
|
const context = createEventContext("close");
|
|
176
152
|
closeByTrigger("close");
|
|
177
153
|
emit("close", context);
|
|
178
154
|
}
|
|
179
155
|
|
|
180
|
-
function onDialogConfirm()
|
|
156
|
+
function onDialogConfirm() {
|
|
181
157
|
const context = createEventContext("confirm");
|
|
182
158
|
emit("confirm", context);
|
|
183
159
|
closeByTrigger("confirm");
|
|
184
160
|
}
|
|
185
161
|
|
|
186
|
-
function onDialogCancel()
|
|
162
|
+
function onDialogCancel() {
|
|
187
163
|
const context = createEventContext("cancel");
|
|
188
164
|
emit("cancel", context);
|
|
189
165
|
closeByTrigger("cancel");
|
|
190
166
|
}
|
|
191
167
|
|
|
192
|
-
function onFooterConfirm()
|
|
168
|
+
function onFooterConfirm() {
|
|
193
169
|
const context = createEventContext("confirm");
|
|
194
170
|
emit("confirm", context);
|
|
195
171
|
closeByTrigger("confirm");
|
|
196
172
|
}
|
|
197
173
|
|
|
198
|
-
function onFooterClose()
|
|
174
|
+
function onFooterClose() {
|
|
199
175
|
const context = createEventContext("cancel");
|
|
200
176
|
emit("cancel", context);
|
|
201
177
|
closeByTrigger("cancel");
|