befly-admin 3.4.5 → 3.4.6
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/bunfig.toml +1 -8
- package/package.json +10 -6
- package/src/config/internal/index.ts +20 -0
- package/src/layouts/internal/0.vue +3 -2
- package/src/plugins/internal/global.ts +27 -0
- package/src/plugins/internal/router.ts +2 -2
- package/src/types/auto-imports.d.ts +111 -17
- package/src/types/components.d.ts +27 -0
- package/src/views/internal/admin/index.vue +13 -6
- package/src/views/internal/dict/index.vue +4 -4
- package/src/views/internal/index/components/addonList.vue +2 -2
- package/src/views/internal/index/components/environmentInfo.vue +1 -1
- package/src/views/internal/index/components/operationLogs.vue +1 -1
- package/src/views/internal/index/components/performanceMetrics.vue +6 -6
- package/src/views/internal/index/components/quickActions.vue +1 -1
- package/src/views/internal/index/components/serviceStatus.vue +11 -23
- package/src/views/internal/index/components/systemNotifications.vue +6 -12
- package/src/views/internal/index/components/systemOverview.vue +4 -4
- package/src/views/internal/index/components/systemResources.vue +4 -4
- package/src/views/internal/index/components/userInfo.vue +5 -5
- package/src/views/internal/login/components/emailLoginForm.vue +2 -2
- package/src/views/internal/login/components/registerForm.vue +4 -4
- package/src/views/internal/menu/index.vue +5 -5
- package/src/views/internal/role/index.vue +6 -6
- package/src/views/internal/user/user.vue +3 -3
- package/vite.config.ts +37 -42
- package/src/components/internal/Icon.vue +0 -41
- package/src/plugins/internal/store.ts +0 -19
package/bunfig.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.6",
|
|
4
4
|
"description": "Befly Admin - 基于 Vue3 + OpenTiny Vue 的后台管理系统",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -31,26 +31,30 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@opentiny/vue": "^3.27.0",
|
|
33
33
|
"axios": "^1.13.2",
|
|
34
|
-
"befly-auto-routes": "^1.1.0",
|
|
35
|
-
"lucide-vue-next": "^0.552.0",
|
|
36
34
|
"pinia": "^3.0.4",
|
|
37
35
|
"vue": "^3.5.22",
|
|
38
36
|
"vue-router": "^4.6.3"
|
|
39
37
|
},
|
|
40
38
|
"devDependencies": {
|
|
39
|
+
"@iconify-json/lucide": "^1.2.72",
|
|
40
|
+
"@iconify/json": "^2.2.280",
|
|
41
41
|
"@opentiny/unplugin-tiny-vue": "^1.0.0",
|
|
42
42
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
43
43
|
"@vue-macros/reactivity-transform": "^3.1.1",
|
|
44
|
+
"@vue/compiler-sfc": "^3.5.22",
|
|
45
|
+
"befly-auto-routes": "1.1.0",
|
|
46
|
+
"merge-anything": "^6.0.6",
|
|
44
47
|
"sass": "^1.93.3",
|
|
45
48
|
"typescript": "^5.9.3",
|
|
46
49
|
"unplugin-auto-import": "^20.2.0",
|
|
50
|
+
"unplugin-icons": "^0.22.0",
|
|
47
51
|
"unplugin-vue-components": "^30.0.0",
|
|
48
52
|
"vite": "^7.2.0",
|
|
49
53
|
"vue-tsc": "^3.1.3"
|
|
50
54
|
},
|
|
51
55
|
"engines": {
|
|
52
|
-
"node": ">=
|
|
53
|
-
"pnpm": ">=
|
|
56
|
+
"node": ">=24.0.0",
|
|
57
|
+
"pnpm": ">=10.0.0"
|
|
54
58
|
},
|
|
55
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "c0844203f8fee734d2a8742f3b77b67bf0874681"
|
|
56
60
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内部配置
|
|
3
|
+
* 存放框架内置的配置变量,不建议修改
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 内置配置对象
|
|
8
|
+
*/
|
|
9
|
+
export const $ConfigInternal = {
|
|
10
|
+
/** 应用标题 */
|
|
11
|
+
appTitle: import.meta.env.VITE_APP_TITLE || '管理后台',
|
|
12
|
+
/** API 基础地址 */
|
|
13
|
+
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || '',
|
|
14
|
+
/** 存储命名空间 */
|
|
15
|
+
storageNamespace: import.meta.env.VITE_STORAGE_NAMESPACE || 'befly_admin',
|
|
16
|
+
/** 是否开发环境 */
|
|
17
|
+
isDev: import.meta.env.DEV,
|
|
18
|
+
/** 是否生产环境 */
|
|
19
|
+
isProd: import.meta.env.PROD
|
|
20
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<!-- 顶部导航栏 -->
|
|
4
4
|
<div class="layout-header">
|
|
5
5
|
<div class="logo">
|
|
6
|
-
<h2>{{
|
|
6
|
+
<h2>{{ global.data.appTitle }}</h2>
|
|
7
7
|
</div>
|
|
8
8
|
<div class="header-right">
|
|
9
9
|
<tiny-dropdown title="管理员" trigger="click" border type="info" @item-click="$Method.handleUserMenu">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<tiny-tree-menu :data="$Data.userMenus" :props="{ label: 'name' }" node-key="id" :node-height="40" :show-filter="false" :default-expanded-keys="$Data.expandedKeys" :default-expanded-keys-highlight="$Data.currentNodeKey" style="height: 100%" only-check-children width-adapt @node-click="$Method.onMenuClick">
|
|
22
22
|
<template #default="{ data }">
|
|
23
23
|
<span class="menu-item">
|
|
24
|
-
<
|
|
24
|
+
<i-lucide:square style="width: 16px; height: 16px; margin-right: 8px; vertical-align: middle" />
|
|
25
25
|
<span>{{ data.name }}</span>
|
|
26
26
|
</span>
|
|
27
27
|
</template>
|
|
@@ -40,6 +40,7 @@ import { arrayToTree } from '@/utils';
|
|
|
40
40
|
|
|
41
41
|
const router = useRouter();
|
|
42
42
|
const route = useRoute();
|
|
43
|
+
const global = useGlobal();
|
|
43
44
|
|
|
44
45
|
// 响应式数据
|
|
45
46
|
const $Data = $ref({
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 全局状态管理
|
|
5
|
+
* 集中管理所有全局数据,避免分散到多个 store 文件
|
|
6
|
+
*/
|
|
7
|
+
export const useGlobal = defineStore('global', () => {
|
|
8
|
+
// ==================== 全局数据 ====================
|
|
9
|
+
const data = $ref({});
|
|
10
|
+
|
|
11
|
+
// ==================== 全局方法 ====================
|
|
12
|
+
const method = {
|
|
13
|
+
/**
|
|
14
|
+
* 设置页面标题
|
|
15
|
+
* @param title 标题文本
|
|
16
|
+
*/
|
|
17
|
+
setPageTitle: (title?: string) => {
|
|
18
|
+
document.title = title ? `${title} - ${$ConfigInternal.appTitle}` : $ConfigInternal.appTitle;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ==================== 返回 ====================
|
|
23
|
+
return {
|
|
24
|
+
data,
|
|
25
|
+
method
|
|
26
|
+
};
|
|
27
|
+
});
|
|
@@ -28,11 +28,11 @@ router.beforeEach(async (to, from, next) => {
|
|
|
28
28
|
|
|
29
29
|
// 1. 未登录且访问受保护路由 → 跳转登录
|
|
30
30
|
if (!token && isProtectedRoute) {
|
|
31
|
-
return next('/login');
|
|
31
|
+
return next('/internal/login');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// 2. 已登录访问登录页 → 跳转首页
|
|
35
|
-
if (token && to.path === '/login') {
|
|
35
|
+
if (token && to.path === '/internal/login') {
|
|
36
36
|
return next('/');
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -6,28 +6,29 @@
|
|
|
6
6
|
// biome-ignore lint: disable
|
|
7
7
|
export {}
|
|
8
8
|
declare global {
|
|
9
|
+
const $ConfigInternal: typeof import('../config/internal/index')['$ConfigInternal']
|
|
9
10
|
const $Http: typeof import('../plugins/internal/http')['$Http']
|
|
10
|
-
const $Session: typeof import('../plugins/storage')['$Session']
|
|
11
|
+
const $Session: (typeof import('../plugins/storage'))['$Session']
|
|
11
12
|
const $Storage: typeof import('../plugins/internal/storage')['$Storage']
|
|
12
|
-
const AppIcon: typeof import('tdesign-icons-vue-next')['AppIcon']
|
|
13
|
-
const DashboardIcon: typeof import('tdesign-icons-vue-next')['DashboardIcon']
|
|
14
|
-
const DialogPlugin: typeof import('tdesign-vue-next')['DialogPlugin']
|
|
13
|
+
const AppIcon: (typeof import('tdesign-icons-vue-next'))['AppIcon']
|
|
14
|
+
const DashboardIcon: (typeof import('tdesign-icons-vue-next'))['DashboardIcon']
|
|
15
|
+
const DialogPlugin: (typeof import('tdesign-vue-next'))['DialogPlugin']
|
|
15
16
|
const EffectScope: typeof import('vue')['EffectScope']
|
|
16
|
-
const FileIcon: typeof import('tdesign-icons-vue-next')['FileIcon']
|
|
17
|
-
const FolderOpenIcon: typeof import('tdesign-icons-vue-next')['FolderOpenIcon']
|
|
18
|
-
const HomeIcon: typeof import('tdesign-icons-vue-next')['HomeIcon']
|
|
17
|
+
const FileIcon: (typeof import('tdesign-icons-vue-next'))['FileIcon']
|
|
18
|
+
const FolderOpenIcon: (typeof import('tdesign-icons-vue-next'))['FolderOpenIcon']
|
|
19
|
+
const HomeIcon: (typeof import('tdesign-icons-vue-next'))['HomeIcon']
|
|
19
20
|
const Loading: typeof import('@opentiny/vue')['Loading']
|
|
20
|
-
const LoadingPlugin: typeof import('tdesign-vue-next')['LoadingPlugin']
|
|
21
|
+
const LoadingPlugin: (typeof import('tdesign-vue-next'))['LoadingPlugin']
|
|
21
22
|
const Message: typeof import('@opentiny/vue')['Message']
|
|
22
|
-
const MessagePlugin: typeof import('tdesign-vue-next')['MessagePlugin']
|
|
23
|
+
const MessagePlugin: (typeof import('tdesign-vue-next'))['MessagePlugin']
|
|
23
24
|
const Modal: typeof import('@opentiny/vue')['Modal']
|
|
24
25
|
const Notify: typeof import('@opentiny/vue')['Notify']
|
|
25
|
-
const NotifyPlugin: typeof import('tdesign-vue-next')['NotifyPlugin']
|
|
26
|
-
const RootListIcon: typeof import('tdesign-icons-vue-next')['RootListIcon']
|
|
27
|
-
const SettingIcon: typeof import('tdesign-icons-vue-next')['SettingIcon']
|
|
26
|
+
const NotifyPlugin: (typeof import('tdesign-vue-next'))['NotifyPlugin']
|
|
27
|
+
const RootListIcon: (typeof import('tdesign-icons-vue-next'))['RootListIcon']
|
|
28
|
+
const SettingIcon: (typeof import('tdesign-icons-vue-next'))['SettingIcon']
|
|
28
29
|
const TinyMessage: typeof import('@opentiny/vue-message')['default']
|
|
29
|
-
const UserIcon: typeof import('tdesign-icons-vue-next')['UserIcon']
|
|
30
|
-
const ViewListIcon: typeof import('tdesign-icons-vue-next')['ViewListIcon']
|
|
30
|
+
const UserIcon: (typeof import('tdesign-icons-vue-next'))['UserIcon']
|
|
31
|
+
const ViewListIcon: (typeof import('tdesign-icons-vue-next'))['ViewListIcon']
|
|
31
32
|
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
|
32
33
|
const computed: typeof import('vue')['computed']
|
|
33
34
|
const createApp: typeof import('vue')['createApp']
|
|
@@ -41,7 +42,7 @@ declare global {
|
|
|
41
42
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
|
42
43
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
|
43
44
|
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
|
|
44
|
-
const getIcon: typeof import('tdesign-icons-vue-next')['getIcon']
|
|
45
|
+
const getIcon: (typeof import('tdesign-icons-vue-next'))['getIcon']
|
|
45
46
|
const h: typeof import('vue')['h']
|
|
46
47
|
const iconAcceptance: typeof import('@opentiny/vue-icon').iconAcceptance
|
|
47
48
|
const iconActivation: typeof import('@opentiny/vue-icon').iconActivation
|
|
@@ -618,7 +619,7 @@ declare global {
|
|
|
618
619
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
|
619
620
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
|
620
621
|
const shallowRef: typeof import('vue')['shallowRef']
|
|
621
|
-
const storage: typeof import('../plugins/storage')['default']
|
|
622
|
+
const storage: (typeof import('../plugins/storage'))['default']
|
|
622
623
|
const storeToRefs: typeof import('pinia')['storeToRefs']
|
|
623
624
|
const toRaw: typeof import('vue')['toRaw']
|
|
624
625
|
const toRef: typeof import('vue')['toRef']
|
|
@@ -629,7 +630,7 @@ declare global {
|
|
|
629
630
|
const useAttrs: typeof import('vue')['useAttrs']
|
|
630
631
|
const useCssModule: typeof import('vue')['useCssModule']
|
|
631
632
|
const useCssVars: typeof import('vue')['useCssVars']
|
|
632
|
-
const useGlobal: typeof import('../plugins/internal/
|
|
633
|
+
const useGlobal: typeof import('../plugins/internal/global')['useGlobal']
|
|
633
634
|
const useGlobalStore: typeof import('../plugins/store').useGlobalStore
|
|
634
635
|
const useId: typeof import('vue')['useId']
|
|
635
636
|
const useLink: typeof import('vue-router')['useLink']
|
|
@@ -650,3 +651,96 @@ declare global {
|
|
|
650
651
|
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
|
651
652
|
import('vue')
|
|
652
653
|
}
|
|
654
|
+
|
|
655
|
+
// for vue template auto import
|
|
656
|
+
import { UnwrapRef } from 'vue'
|
|
657
|
+
declare module 'vue' {
|
|
658
|
+
interface GlobalComponents {}
|
|
659
|
+
interface ComponentCustomProperties {
|
|
660
|
+
readonly $ConfigInternal: UnwrapRef<typeof import('../config/internal/index')['$ConfigInternal']>
|
|
661
|
+
readonly $Http: UnwrapRef<typeof import('../plugins/internal/http')['$Http']>
|
|
662
|
+
readonly $Storage: UnwrapRef<typeof import('../plugins/internal/storage')['$Storage']>
|
|
663
|
+
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
|
664
|
+
readonly Loading: UnwrapRef<typeof import('@opentiny/vue')['Loading']>
|
|
665
|
+
readonly Message: UnwrapRef<typeof import('@opentiny/vue')['Message']>
|
|
666
|
+
readonly Modal: UnwrapRef<typeof import('@opentiny/vue')['Modal']>
|
|
667
|
+
readonly Notify: UnwrapRef<typeof import('@opentiny/vue')['Notify']>
|
|
668
|
+
readonly TinyMessage: UnwrapRef<typeof import('@opentiny/vue-message')['default']>
|
|
669
|
+
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
|
|
670
|
+
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
|
671
|
+
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
|
672
|
+
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
|
|
673
|
+
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
|
674
|
+
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
|
675
|
+
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
|
676
|
+
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
|
677
|
+
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
|
678
|
+
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
|
679
|
+
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
|
680
|
+
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
|
681
|
+
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
|
|
682
|
+
readonly h: UnwrapRef<typeof import('vue')['h']>
|
|
683
|
+
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
|
684
|
+
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
|
685
|
+
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
|
686
|
+
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
|
687
|
+
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
|
688
|
+
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
|
|
689
|
+
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
|
690
|
+
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
|
691
|
+
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
|
692
|
+
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
|
|
693
|
+
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
|
|
694
|
+
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
|
695
|
+
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
|
696
|
+
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
|
697
|
+
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
|
698
|
+
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
|
|
699
|
+
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
|
|
700
|
+
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
|
701
|
+
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
|
702
|
+
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
|
703
|
+
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
|
704
|
+
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
|
705
|
+
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
|
706
|
+
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
|
707
|
+
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
|
708
|
+
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
|
709
|
+
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
|
710
|
+
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
|
711
|
+
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
|
712
|
+
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
|
713
|
+
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
|
714
|
+
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
|
715
|
+
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
|
716
|
+
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
|
717
|
+
readonly router: UnwrapRef<typeof import('../plugins/internal/router')['router']>
|
|
718
|
+
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
|
719
|
+
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
|
720
|
+
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
|
721
|
+
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
|
722
|
+
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
|
723
|
+
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
|
|
724
|
+
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
|
725
|
+
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
|
726
|
+
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
|
727
|
+
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
|
728
|
+
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
|
729
|
+
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
|
730
|
+
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
|
731
|
+
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
|
732
|
+
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
|
733
|
+
readonly useGlobal: UnwrapRef<typeof import('../plugins/internal/global')['useGlobal']>
|
|
734
|
+
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
|
735
|
+
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
|
736
|
+
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
|
737
|
+
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
|
738
|
+
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
|
739
|
+
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
|
740
|
+
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
|
741
|
+
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
|
742
|
+
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
|
743
|
+
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
|
744
|
+
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
|
745
|
+
}
|
|
746
|
+
}
|
|
@@ -12,6 +12,33 @@ export {}
|
|
|
12
12
|
declare module 'vue' {
|
|
13
13
|
export interface GlobalComponents {
|
|
14
14
|
Icon: typeof import('./../components/internal/Icon.vue')['default']
|
|
15
|
+
'ILucide:activity': typeof import('~icons/lucide/activity')['default']
|
|
16
|
+
'ILucide:alertCircle': typeof import('~icons/lucide/alert-circle')['default']
|
|
17
|
+
'ILucide:alertTriangle': typeof import('~icons/lucide/alert-triangle')['default']
|
|
18
|
+
'ILucide:box': typeof import('~icons/lucide/box')['default']
|
|
19
|
+
'ILucide:checkCircle': typeof import('~icons/lucide/check-circle')['default']
|
|
20
|
+
'ILucide:circle': typeof import('~icons/lucide/circle')['default']
|
|
21
|
+
'ILucide:clock': typeof import('~icons/lucide/clock')['default']
|
|
22
|
+
'ILucide:cloud': typeof import('~icons/lucide/cloud')['default']
|
|
23
|
+
'ILucide:cpu': typeof import('~icons/lucide/cpu')['default']
|
|
24
|
+
'ILucide:database': typeof import('~icons/lucide/database')['default']
|
|
25
|
+
'ILucide:disc': typeof import('~icons/lucide/disc')['default']
|
|
26
|
+
'ILucide:hardDrive': typeof import('~icons/lucide/hard-drive')['default']
|
|
27
|
+
'ILucide:info': typeof import('~icons/lucide/info')['default']
|
|
28
|
+
'ILucide:mail': typeof import('~icons/lucide/mail')['default']
|
|
29
|
+
'ILucide:menu': typeof import('~icons/lucide/menu')['default']
|
|
30
|
+
'ILucide:package': typeof import('~icons/lucide/package')['default']
|
|
31
|
+
'ILucide:phone': typeof import('~icons/lucide/phone')['default']
|
|
32
|
+
'ILucide:rotateCw': typeof import('~icons/lucide/rotate-cw')['default']
|
|
33
|
+
'ILucide:server': typeof import('~icons/lucide/server')['default']
|
|
34
|
+
'ILucide:settings': typeof import('~icons/lucide/settings')['default']
|
|
35
|
+
'ILucide:square': typeof import('~icons/lucide/square')['default']
|
|
36
|
+
'ILucide:trendingUp': typeof import('~icons/lucide/trending-up')['default']
|
|
37
|
+
'ILucide:user': typeof import('~icons/lucide/user')['default']
|
|
38
|
+
'ILucide:users': typeof import('~icons/lucide/users')['default']
|
|
39
|
+
'ILucide:webhook': typeof import('~icons/lucide/webhook')['default']
|
|
40
|
+
'ILucide:xCircle': typeof import('~icons/lucide/x-circle')['default']
|
|
41
|
+
'ILucide:zap': typeof import('~icons/lucide/zap')['default']
|
|
15
42
|
RouterLink: typeof import('vue-router')['RouterLink']
|
|
16
43
|
RouterView: typeof import('vue-router')['RouterView']
|
|
17
44
|
TinyButton: typeof import('@opentiny/vue-button')['default']
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
<template>
|
|
1
|
+
<template>
|
|
2
2
|
<div class="page-admin page-table">
|
|
3
3
|
<div class="main-tool">
|
|
4
4
|
<div class="left">
|
|
5
5
|
<tiny-button type="primary" @click="$Method.onAction('add', {})">
|
|
6
6
|
<template #icon>
|
|
7
|
-
<
|
|
7
|
+
<i-lucide:plus style="width: 16px; height: 16px" />
|
|
8
8
|
</template>
|
|
9
9
|
添加管理员
|
|
10
10
|
</tiny-button>
|
|
@@ -12,7 +12,14 @@
|
|
|
12
12
|
<div class="right">
|
|
13
13
|
<tiny-button @click="$Method.handleRefresh">
|
|
14
14
|
<template #icon>
|
|
15
|
-
<
|
|
15
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
16
|
+
</template>
|
|
17
|
+
</tiny-button>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="right">
|
|
20
|
+
<tiny-button @click="$Method.handleRefresh">
|
|
21
|
+
<template #icon>
|
|
22
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
16
23
|
</template>
|
|
17
24
|
刷新
|
|
18
25
|
</tiny-button>
|
|
@@ -39,15 +46,15 @@
|
|
|
39
46
|
<template #dropdown>
|
|
40
47
|
<tiny-dropdown-menu>
|
|
41
48
|
<tiny-dropdown-item :item-data="{ command: 'role' }">
|
|
42
|
-
<
|
|
49
|
+
<i-lucide:user style="width: 14px; height: 14px; margin-right: 6px" />
|
|
43
50
|
分配角色
|
|
44
51
|
</tiny-dropdown-item>
|
|
45
52
|
<tiny-dropdown-item :item-data="{ command: 'upd' }">
|
|
46
|
-
<
|
|
53
|
+
<i-lucide:pencil style="width: 14px; height: 14px; margin-right: 6px" />
|
|
47
54
|
编辑
|
|
48
55
|
</tiny-dropdown-item>
|
|
49
56
|
<tiny-dropdown-item :item-data="{ command: 'del' }" divided>
|
|
50
|
-
<
|
|
57
|
+
<i-lucide:trash-2 style="width: 14px; height: 14px; margin-right: 6px" />
|
|
51
58
|
删除
|
|
52
59
|
</tiny-dropdown-item>
|
|
53
60
|
</tiny-dropdown-menu>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div class="left">
|
|
5
5
|
<tiny-button type="primary" @click="$Method.onAction('add', {})">
|
|
6
6
|
<template #icon>
|
|
7
|
-
<
|
|
7
|
+
<i-lucide:plus style="width: 16px; height: 16px" />
|
|
8
8
|
</template>
|
|
9
9
|
添加字典
|
|
10
10
|
</tiny-button>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="right">
|
|
13
13
|
<tiny-button @click="$Method.handleRefresh">
|
|
14
14
|
<template #icon>
|
|
15
|
-
<
|
|
15
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
16
16
|
</template>
|
|
17
17
|
刷新
|
|
18
18
|
</tiny-button>
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
<template #dropdown>
|
|
41
41
|
<tiny-dropdown-menu>
|
|
42
42
|
<tiny-dropdown-item :item-data="{ command: 'upd' }">
|
|
43
|
-
<
|
|
43
|
+
<i-lucide:pencil style="width: 14px; height: 14px; margin-right: 6px" />
|
|
44
44
|
编辑
|
|
45
45
|
</tiny-dropdown-item>
|
|
46
46
|
<tiny-dropdown-item :item-data="{ command: 'del' }" divided>
|
|
47
|
-
<
|
|
47
|
+
<i-lucide:trash-2 style="width: 14px; height: 14px; margin-right: 6px" />
|
|
48
48
|
删除
|
|
49
49
|
</tiny-dropdown-item>
|
|
50
50
|
</tiny-dropdown-menu>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:package style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>已安装插件</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<div v-for="addon in addonList" :key="addon.name" class="addon-item">
|
|
10
10
|
<div class="addon-status-badge" :class="{ active: addon.enabled }"></div>
|
|
11
11
|
<div class="addon-icon">
|
|
12
|
-
<
|
|
12
|
+
<i-lucide:box style="width: 20px; height: 20px" />
|
|
13
13
|
</div>
|
|
14
14
|
<div class="addon-info">
|
|
15
15
|
<div class="addon-title">
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:zap style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>性能指标</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
8
8
|
<div class="performance-grid">
|
|
9
9
|
<div class="perf-metric">
|
|
10
10
|
<div class="perf-icon">
|
|
11
|
-
<
|
|
11
|
+
<i-lucide:clock style="width: 18px; height: 18px" />
|
|
12
12
|
</div>
|
|
13
13
|
<div class="perf-info">
|
|
14
14
|
<div class="perf-label">平均响应</div>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</div>
|
|
18
18
|
<div class="perf-metric">
|
|
19
19
|
<div class="perf-icon">
|
|
20
|
-
<
|
|
20
|
+
<i-lucide:trending-up style="width: 18px; height: 18px" />
|
|
21
21
|
</div>
|
|
22
22
|
<div class="perf-info">
|
|
23
23
|
<div class="perf-label">QPS</div>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
<div class="perf-metric">
|
|
28
28
|
<div class="perf-icon">
|
|
29
|
-
<
|
|
29
|
+
<i-lucide:alert-circle style="width: 18px; height: 18px" />
|
|
30
30
|
</div>
|
|
31
31
|
<div class="perf-info">
|
|
32
32
|
<div class="perf-label">错误率</div>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
<div class="perf-metric">
|
|
37
37
|
<div class="perf-icon">
|
|
38
|
-
<
|
|
38
|
+
<i-lucide:activity style="width: 18px; height: 18px" />
|
|
39
39
|
</div>
|
|
40
40
|
<div class="perf-info">
|
|
41
41
|
<div class="perf-label">活跃连接</div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
</div>
|
|
46
46
|
<!-- 最慢接口提示 -->
|
|
47
47
|
<div v-if="performanceMetrics.slowestApi" class="perf-slowest">
|
|
48
|
-
<
|
|
48
|
+
<i-lucide:alert-triangle style="width: 14px; height: 14px" />
|
|
49
49
|
<span>最慢接口: {{ performanceMetrics.slowestApi.path }} ({{ performanceMetrics.slowestApi.time }}ms)</span>
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:settings style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>服务状态</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
8
8
|
<div class="config-grid">
|
|
9
9
|
<div v-for="service in services" :key="service.name" class="config-card" :class="`config-${service.status}`">
|
|
10
10
|
<div class="config-icon">
|
|
11
|
-
<
|
|
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" />
|
|
12
17
|
</div>
|
|
13
18
|
<div class="config-info">
|
|
14
19
|
<div class="config-name">{{ service.name }}</div>
|
|
@@ -18,7 +23,10 @@
|
|
|
18
23
|
</div>
|
|
19
24
|
</div>
|
|
20
25
|
<div class="config-badge">
|
|
21
|
-
<
|
|
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" />
|
|
22
30
|
</div>
|
|
23
31
|
</div>
|
|
24
32
|
</div>
|
|
@@ -60,26 +68,6 @@ const getStatusText = (status) => {
|
|
|
60
68
|
};
|
|
61
69
|
return texts[status] || status;
|
|
62
70
|
};
|
|
63
|
-
|
|
64
|
-
const getServiceIcon = (name) => {
|
|
65
|
-
const icons = {
|
|
66
|
-
数据库: 'Database',
|
|
67
|
-
Redis: 'Zap',
|
|
68
|
-
文件系统: 'HardDrive',
|
|
69
|
-
邮件服务: 'Mail',
|
|
70
|
-
OSS存储: 'Cloud'
|
|
71
|
-
};
|
|
72
|
-
return icons[name] || 'Circle';
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const getStatusIcon = (status) => {
|
|
76
|
-
const icons = {
|
|
77
|
-
running: 'CheckCircle',
|
|
78
|
-
stopped: 'XCircle',
|
|
79
|
-
unconfigured: 'AlertCircle'
|
|
80
|
-
};
|
|
81
|
-
return icons[status] || 'Circle';
|
|
82
|
-
};
|
|
83
71
|
</script>
|
|
84
72
|
|
|
85
73
|
<style scoped lang="scss">
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:bell style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>系统通知</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
8
8
|
<div class="notification-compact-list">
|
|
9
9
|
<div v-for="notification in notifications" :key="notification.id" class="notification-compact-item">
|
|
10
10
|
<div class="notification-icon" :class="`type-${notification.type}`">
|
|
11
|
-
<
|
|
11
|
+
<i-lucide:info v-if="notification.type === 'info'" style="width: 16px; height: 16px" />
|
|
12
|
+
<i-lucide:check-circle v-else-if="notification.type === 'success'" style="width: 16px; height: 16px" />
|
|
13
|
+
<i-lucide:alert-triangle v-else-if="notification.type === 'warning'" style="width: 16px; height: 16px" />
|
|
14
|
+
<i-lucide:x-circle v-else-if="notification.type === 'error'" style="width: 16px; height: 16px" />
|
|
15
|
+
<i-lucide:bell v-else style="width: 16px; height: 16px" />
|
|
12
16
|
</div>
|
|
13
17
|
<div class="notification-content">
|
|
14
18
|
<span class="notification-title">{{ notification.title }}</span>
|
|
@@ -30,16 +34,6 @@ const notifications = $ref([
|
|
|
30
34
|
{ id: 4, type: 'success', title: '性能优化完成 - 响应速度提升30%', isRead: true, createdAt: Date.now() - 172800000 }
|
|
31
35
|
]);
|
|
32
36
|
|
|
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
37
|
const formatTime = (timestamp) => {
|
|
44
38
|
const date = new Date(timestamp);
|
|
45
39
|
const now = Date.now();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:info style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>系统概览</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
<div class="info-block">
|
|
11
11
|
<div class="stats-grid">
|
|
12
12
|
<div class="stat-box stat-primary">
|
|
13
|
-
<
|
|
13
|
+
<i-lucide:menu style="width: 24px; height: 24px" />
|
|
14
14
|
<div class="stat-content">
|
|
15
15
|
<div class="stat-value">{{ permissionStats.menuCount }}</div>
|
|
16
16
|
<div class="stat-label">菜单总数</div>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
<div class="stat-box stat-success">
|
|
20
|
-
<
|
|
20
|
+
<i-lucide:webhook style="width: 24px; height: 24px" />
|
|
21
21
|
<div class="stat-content">
|
|
22
22
|
<div class="stat-value">{{ permissionStats.apiCount }}</div>
|
|
23
23
|
<div class="stat-label">接口总数</div>
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
26
26
|
<div class="stat-box stat-warning">
|
|
27
|
-
<
|
|
27
|
+
<i-lucide:users style="width: 24px; height: 24px" />
|
|
28
28
|
<div class="stat-content">
|
|
29
29
|
<div class="stat-value">{{ permissionStats.roleCount }}</div>
|
|
30
30
|
<div class="stat-label">角色总数</div>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="section-block">
|
|
3
3
|
<div class="section-header">
|
|
4
|
-
<
|
|
4
|
+
<i-lucide:activity style="width: 20px; height: 20px" />
|
|
5
5
|
<h2>系统资源</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="section-content">
|
|
8
8
|
<div class="resource-compact-list">
|
|
9
9
|
<div class="resource-compact-item">
|
|
10
10
|
<div class="resource-compact-header">
|
|
11
|
-
<
|
|
11
|
+
<i-lucide:cpu style="width: 16px; height: 16px" />
|
|
12
12
|
<span class="resource-label">CPU</span>
|
|
13
13
|
<span class="resource-value">{{ systemResources.cpu.usage }}%</span>
|
|
14
14
|
<span class="resource-desc">{{ systemResources.cpu.cores }}核心</span>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</div>
|
|
18
18
|
<div class="resource-compact-item">
|
|
19
19
|
<div class="resource-compact-header">
|
|
20
|
-
<
|
|
20
|
+
<i-lucide:hard-drive style="width: 16px; height: 16px" />
|
|
21
21
|
<span class="resource-label">内存</span>
|
|
22
22
|
<span class="resource-value">{{ systemResources.memory.percentage }}%</span>
|
|
23
23
|
<span class="resource-desc">{{ systemResources.memory.used }}GB / {{ systemResources.memory.total }}GB</span>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
<div class="resource-compact-item">
|
|
28
28
|
<div class="resource-compact-header">
|
|
29
|
-
<
|
|
29
|
+
<i-lucide:disc style="width: 16px; height: 16px" />
|
|
30
30
|
<span class="resource-label">磁盘</span>
|
|
31
31
|
<span class="resource-value">{{ systemResources.disk.percentage }}%</span>
|
|
32
32
|
<span class="resource-desc">{{ systemResources.disk.used }}GB / {{ systemResources.disk.total }}GB</span>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="section-block user-info-card">
|
|
3
3
|
<div class="user-header">
|
|
4
4
|
<div class="user-avatar">
|
|
5
|
-
<
|
|
5
|
+
<i-lucide:user style="width: 32px; height: 32px" />
|
|
6
6
|
</div>
|
|
7
7
|
<div class="user-basic">
|
|
8
8
|
<div class="user-name">{{ $Data.userInfo.nickname || $Data.userInfo.name || $Data.userInfo.username || '未设置' }}</div>
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
<div class="user-details">
|
|
13
13
|
<div class="detail-item">
|
|
14
|
-
<
|
|
14
|
+
<i-lucide:mail style="width: 14px; height: 14px" />
|
|
15
15
|
<span>{{ $Data.userInfo.email || '未设置' }}</span>
|
|
16
16
|
</div>
|
|
17
17
|
<div v-if="$Data.userInfo.phone" class="detail-item">
|
|
18
|
-
<
|
|
18
|
+
<i-lucide:phone style="width: 14px; height: 14px" />
|
|
19
19
|
<span>{{ $Data.userInfo.phone }}</span>
|
|
20
20
|
</div>
|
|
21
21
|
<div v-if="$Data.userInfo.lastLoginTime" class="detail-item">
|
|
22
|
-
<
|
|
22
|
+
<i-lucide:clock style="width: 14px; height: 14px" />
|
|
23
23
|
<span>{{ $Method.formatTime($Data.userInfo.lastLoginTime) }}</span>
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<div v-if="$Data.userInfo.roleCode === 'dev'" class="user-actions">
|
|
29
29
|
<tiny-button type="primary" size="mini" :loading="$Data.refreshing" @click="$Method.handleRefreshCache">
|
|
30
30
|
<template #icon>
|
|
31
|
-
<
|
|
31
|
+
<i-lucide:rotate-cw style="width: 14px; height: 14px" />
|
|
32
32
|
</template>
|
|
33
33
|
刷新缓存
|
|
34
34
|
</tiny-button>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<tiny-form-item prop="account" label="账号">
|
|
4
4
|
<tiny-input v-model="$Data.formData.account" placeholder="请输入用户名或邮箱" size="large" clearable>
|
|
5
5
|
<template #prefix-icon>
|
|
6
|
-
<
|
|
6
|
+
<i-lucide:user style="width: 18px; height: 18px" />
|
|
7
7
|
</template>
|
|
8
8
|
</tiny-input>
|
|
9
9
|
</tiny-form-item>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<tiny-form-item prop="password" label="密码">
|
|
12
12
|
<tiny-input v-model="$Data.formData.password" type="password" placeholder="请输入密码" size="large" clearable>
|
|
13
13
|
<template #prefix-icon>
|
|
14
|
-
<
|
|
14
|
+
<i-lucide:lock style="width: 18px; height: 18px" />
|
|
15
15
|
</template>
|
|
16
16
|
</tiny-input>
|
|
17
17
|
</tiny-form-item>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<tiny-form-item prop="username" label="用户名">
|
|
4
4
|
<tiny-input v-model="$Data.formData.username" placeholder="请输入用户名" size="large" clearable>
|
|
5
5
|
<template #prefix-icon>
|
|
6
|
-
<
|
|
6
|
+
<i-lucide:user style="width: 18px; height: 18px" />
|
|
7
7
|
</template>
|
|
8
8
|
</tiny-input>
|
|
9
9
|
</tiny-form-item>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<tiny-form-item prop="email" label="邮箱">
|
|
12
12
|
<tiny-input v-model="$Data.formData.email" placeholder="请输入邮箱" size="large" clearable>
|
|
13
13
|
<template #prefix-icon>
|
|
14
|
-
<
|
|
14
|
+
<i-lucide:mail style="width: 18px; height: 18px" />
|
|
15
15
|
</template>
|
|
16
16
|
</tiny-input>
|
|
17
17
|
</tiny-form-item>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<tiny-form-item prop="password" label="密码">
|
|
20
20
|
<tiny-input v-model="$Data.formData.password" type="password" placeholder="请输入密码" size="large" clearable>
|
|
21
21
|
<template #prefix-icon>
|
|
22
|
-
<
|
|
22
|
+
<i-lucide:lock style="width: 18px; height: 18px" />
|
|
23
23
|
</template>
|
|
24
24
|
</tiny-input>
|
|
25
25
|
</tiny-form-item>
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<tiny-form-item prop="nickname" label="昵称">
|
|
28
28
|
<tiny-input v-model="$Data.formData.nickname" placeholder="请输入昵称(选填)" size="large" clearable>
|
|
29
29
|
<template #prefix-icon>
|
|
30
|
-
<
|
|
30
|
+
<i-lucide:smile style="width: 18px; height: 18px" />
|
|
31
31
|
</template>
|
|
32
32
|
</tiny-input>
|
|
33
33
|
</tiny-form-item>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div class="left">
|
|
5
5
|
<tiny-button type="primary" @click="$Method.onAction('add', {})">
|
|
6
6
|
<template #icon>
|
|
7
|
-
<
|
|
7
|
+
<i-lucide:plus style="width: 16px; height: 16px" />
|
|
8
8
|
</template>
|
|
9
9
|
添加菜单
|
|
10
10
|
</tiny-button>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="right">
|
|
13
13
|
<tiny-button @click="$Method.handleRefresh">
|
|
14
14
|
<template #icon>
|
|
15
|
-
<
|
|
15
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
16
16
|
</template>
|
|
17
17
|
刷新
|
|
18
18
|
</tiny-button>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<tiny-grid-column field="path" title="路径" :width="200" />
|
|
26
26
|
<tiny-grid-column field="icon" title="图标" :width="100">
|
|
27
27
|
<template #default="{ row }">
|
|
28
|
-
<
|
|
28
|
+
<i-lucide:square v-if="row.icon" style="width: 16px; height: 16px" />
|
|
29
29
|
<span v-else>-</span>
|
|
30
30
|
</template>
|
|
31
31
|
</tiny-grid-column>
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
<template #dropdown>
|
|
50
50
|
<tiny-dropdown-menu>
|
|
51
51
|
<tiny-dropdown-item :item-data="{ command: 'upd' }">
|
|
52
|
-
<
|
|
52
|
+
<i-lucide:pencil style="width: 14px; height: 14px; margin-right: 6px" />
|
|
53
53
|
编辑
|
|
54
54
|
</tiny-dropdown-item>
|
|
55
55
|
<tiny-dropdown-item :item-data="{ command: 'del' }" divided>
|
|
56
|
-
<
|
|
56
|
+
<i-lucide:trash-2 style="width: 14px; height: 14px; margin-right: 6px" />
|
|
57
57
|
删除
|
|
58
58
|
</tiny-dropdown-item>
|
|
59
59
|
</tiny-dropdown-menu>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div class="left">
|
|
5
5
|
<tiny-button type="primary" @click="$Method.onAction('add', {})">
|
|
6
6
|
<template #icon>
|
|
7
|
-
<
|
|
7
|
+
<i-lucide:plus style="width: 16px; height: 16px" />
|
|
8
8
|
</template>
|
|
9
9
|
添加角色
|
|
10
10
|
</tiny-button>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="right">
|
|
13
13
|
<tiny-button @click="$Method.handleRefresh">
|
|
14
14
|
<template #icon>
|
|
15
|
-
<
|
|
15
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
16
16
|
</template>
|
|
17
17
|
刷新
|
|
18
18
|
</tiny-button>
|
|
@@ -38,19 +38,19 @@
|
|
|
38
38
|
<template #dropdown>
|
|
39
39
|
<tiny-dropdown-menu>
|
|
40
40
|
<tiny-dropdown-item :item-data="{ command: 'upd' }">
|
|
41
|
-
<
|
|
41
|
+
<i-lucide:pencil style="width: 14px; height: 14px; margin-right: 6px" />
|
|
42
42
|
编辑
|
|
43
43
|
</tiny-dropdown-item>
|
|
44
44
|
<tiny-dropdown-item :item-data="{ command: 'menu' }">
|
|
45
|
-
<
|
|
45
|
+
<i-lucide:settings style="width: 14px; height: 14px; margin-right: 6px" />
|
|
46
46
|
菜单权限
|
|
47
47
|
</tiny-dropdown-item>
|
|
48
48
|
<tiny-dropdown-item :item-data="{ command: 'api' }">
|
|
49
|
-
<
|
|
49
|
+
<i-lucide:code style="width: 14px; height: 14px; margin-right: 6px" />
|
|
50
50
|
接口权限
|
|
51
51
|
</tiny-dropdown-item>
|
|
52
52
|
<tiny-dropdown-item :item-data="{ command: 'del' }" divided>
|
|
53
|
-
<
|
|
53
|
+
<i-lucide:trash-2 style="width: 14px; height: 14px; margin-right: 6px" />
|
|
54
54
|
删除
|
|
55
55
|
</tiny-dropdown-item>
|
|
56
56
|
</tiny-dropdown-menu>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div class="toolbar-left">
|
|
6
6
|
<t-button theme="primary" @click="$Method.handleAdd">
|
|
7
7
|
<template #icon>
|
|
8
|
-
<
|
|
8
|
+
<i-lucide:plus style="width: 16px; height: 16px" />
|
|
9
9
|
</template>
|
|
10
10
|
添加管理员
|
|
11
11
|
</t-button>
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
<t-select v-model="$Data.searchState" placeholder="状态" clearable style="width: 120px" :options="$Data.stateOptions" @change="$Method.handleSearch" />
|
|
17
17
|
<t-button theme="default" @click="$Method.handleSearch">
|
|
18
18
|
<template #icon>
|
|
19
|
-
<search
|
|
19
|
+
<i-lucide:search style="width: 16px; height: 16px" />
|
|
20
20
|
</template>
|
|
21
21
|
搜索
|
|
22
22
|
</t-button>
|
|
23
23
|
<t-button theme="default" @click="$Method.handleReset">
|
|
24
24
|
<template #icon>
|
|
25
|
-
<
|
|
25
|
+
<i-lucide:rotate-cw style="width: 16px; height: 16px" />
|
|
26
26
|
</template>
|
|
27
27
|
重置
|
|
28
28
|
</t-button>
|
package/vite.config.ts
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import { fileURLToPath, URL } from 'node:url';
|
|
2
1
|
import { defineConfig } from 'vite';
|
|
3
2
|
import vue from '@vitejs/plugin-vue';
|
|
4
3
|
import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
|
|
5
4
|
import AutoImport from 'unplugin-auto-import/vite';
|
|
6
5
|
import Components from 'unplugin-vue-components/vite';
|
|
6
|
+
import Icons from 'unplugin-icons/vite';
|
|
7
|
+
import IconsResolver from 'unplugin-icons/resolver';
|
|
7
8
|
import { TinyVueSingleResolver } from '@opentiny/unplugin-tiny-vue';
|
|
8
9
|
import autoRoutes from 'befly-auto-routes';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
9
11
|
|
|
10
|
-
// https://vite.dev/config/
|
|
11
12
|
export default defineConfig({
|
|
13
|
+
root: process.cwd(),
|
|
14
|
+
base: './',
|
|
12
15
|
plugins: [
|
|
13
16
|
vue(),
|
|
14
|
-
// Vue 响应式语法糖
|
|
15
17
|
ReactivityTransform(),
|
|
16
|
-
// 自动路由插件
|
|
17
18
|
autoRoutes({ debug: true }),
|
|
18
|
-
|
|
19
|
+
Icons({
|
|
20
|
+
compiler: 'vue3',
|
|
21
|
+
autoInstall: true,
|
|
22
|
+
defaultClass: 'icon-befly',
|
|
23
|
+
defaultStyle: 'margin-right: 8px; vertical-align: middle;'
|
|
24
|
+
}),
|
|
19
25
|
AutoImport({
|
|
20
26
|
imports: [
|
|
21
27
|
'vue',
|
|
@@ -25,18 +31,22 @@ export default defineConfig({
|
|
|
25
31
|
'@opentiny/vue': ['Modal', 'Notify', 'Loading', 'Message']
|
|
26
32
|
}
|
|
27
33
|
],
|
|
28
|
-
resolvers: [TinyVueSingleResolver],
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
resolvers: [TinyVueSingleResolver, IconsResolver({})],
|
|
35
|
+
vueTemplate: true,
|
|
36
|
+
dirsScanOptions: {
|
|
37
|
+
filePatterns: ['*.ts'],
|
|
38
|
+
fileFilter: (file) => file.endsWith('.ts'),
|
|
39
|
+
types: true
|
|
40
|
+
},
|
|
41
|
+
dirs: ['./src/plugins/**', './src/config/**'],
|
|
31
42
|
dts: 'src/types/auto-imports.d.ts',
|
|
32
|
-
eslintrc: {
|
|
33
|
-
enabled: false
|
|
34
|
-
}
|
|
43
|
+
eslintrc: { enabled: false }
|
|
35
44
|
}),
|
|
36
|
-
// 自动导入 OpenTiny 组件
|
|
37
45
|
Components({
|
|
38
|
-
resolvers: [TinyVueSingleResolver],
|
|
39
|
-
dirs: ['src/components
|
|
46
|
+
resolvers: [TinyVueSingleResolver, IconsResolver({})],
|
|
47
|
+
dirs: ['src/components'],
|
|
48
|
+
deep: true,
|
|
49
|
+
version: 3,
|
|
40
50
|
dts: 'src/types/components.d.ts'
|
|
41
51
|
})
|
|
42
52
|
],
|
|
@@ -46,53 +56,38 @@ export default defineConfig({
|
|
|
46
56
|
}
|
|
47
57
|
},
|
|
48
58
|
define: {
|
|
49
|
-
|
|
59
|
+
__DEV__: JSON.stringify(process.env.NODE_ENV !== 'production')
|
|
50
60
|
},
|
|
51
61
|
css: {
|
|
52
62
|
preprocessorOptions: {
|
|
53
63
|
scss: {
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
additionalData: `@use "@/styles/internal/variables.scss" as *;`,
|
|
65
|
+
api: 'modern-compiler'
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
},
|
|
59
69
|
server: {
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
host: '0.0.0.0',
|
|
71
|
+
port: 5600,
|
|
72
|
+
strictPort: true,
|
|
62
73
|
open: false
|
|
63
74
|
},
|
|
64
75
|
logLevel: 'info',
|
|
65
|
-
customLogger: {
|
|
66
|
-
info: (msg) => {
|
|
67
|
-
// 过滤掉频繁的依赖优化信息
|
|
68
|
-
if (msg.includes('new dependencies optimized')) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
console.info(msg);
|
|
72
|
-
},
|
|
73
|
-
warn: console.warn,
|
|
74
|
-
error: console.error
|
|
75
|
-
},
|
|
76
76
|
optimizeDeps: {
|
|
77
|
-
include: ['vue', 'vue-router', 'pinia', '
|
|
78
|
-
exclude: [],
|
|
79
|
-
// 禁用自动发现,减少频繁优化提示
|
|
80
|
-
noDiscovery: true,
|
|
81
|
-
// 增加缓存时间,避免重复优化
|
|
82
|
-
force: false,
|
|
83
|
-
esbuildOptions: {
|
|
84
|
-
target: 'esnext'
|
|
85
|
-
}
|
|
77
|
+
include: ['vue', 'vue-router', 'pinia', '@opentiny/vue', 'axios']
|
|
86
78
|
},
|
|
87
79
|
build: {
|
|
80
|
+
target: 'esnext',
|
|
88
81
|
outDir: 'dist',
|
|
82
|
+
assetsDir: 'assets',
|
|
89
83
|
sourcemap: false,
|
|
90
|
-
|
|
84
|
+
minify: 'esbuild',
|
|
85
|
+
chunkSizeWarningLimit: 1000,
|
|
91
86
|
rollupOptions: {
|
|
92
87
|
output: {
|
|
93
88
|
manualChunks: {
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
vue: ['vue', 'vue-router', 'pinia'],
|
|
90
|
+
opentiny: ['@opentiny/vue']
|
|
96
91
|
}
|
|
97
92
|
}
|
|
98
93
|
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<component :is="iconComponent" :size="size" :color="color" :stroke-width="strokeWidth" style="margin-right: 8px; vertical-align: middle" v-bind="$attrs" />
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
|
-
<script setup>
|
|
6
|
-
import * as LucideIcons from 'lucide-vue-next';
|
|
7
|
-
import { computed, markRaw } from 'vue';
|
|
8
|
-
|
|
9
|
-
const props = defineProps({
|
|
10
|
-
name: {
|
|
11
|
-
type: String,
|
|
12
|
-
required: true
|
|
13
|
-
},
|
|
14
|
-
size: {
|
|
15
|
-
type: Number,
|
|
16
|
-
default: 16
|
|
17
|
-
},
|
|
18
|
-
color: {
|
|
19
|
-
type: String,
|
|
20
|
-
default: 'currentColor'
|
|
21
|
-
},
|
|
22
|
-
strokeWidth: {
|
|
23
|
-
type: Number,
|
|
24
|
-
default: 2
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// 动态获取图标组件
|
|
29
|
-
const iconComponent = computed(() => {
|
|
30
|
-
const iconName = props.name;
|
|
31
|
-
const icon = LucideIcons[iconName];
|
|
32
|
-
|
|
33
|
-
if (!icon) {
|
|
34
|
-
console.warn(`Icon "${props.name}" not found in lucide-vue-next`);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 使用 markRaw 避免响应式包装
|
|
39
|
-
return markRaw(icon);
|
|
40
|
-
});
|
|
41
|
-
</script>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineStore } from 'pinia';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 全局状态管理
|
|
5
|
-
* 集中管理所有全局数据,避免分散到多个 store 文件
|
|
6
|
-
*/
|
|
7
|
-
export const useGlobal = defineStore('global', () => {
|
|
8
|
-
// ==================== 全局状态 ====================
|
|
9
|
-
|
|
10
|
-
// 可以在这里添加全局状态,例如:
|
|
11
|
-
// - 用户信息
|
|
12
|
-
// - 主题配置
|
|
13
|
-
// - 应用设置
|
|
14
|
-
// - 等等
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
// 暂时没有状态和方法
|
|
18
|
-
};
|
|
19
|
-
});
|