vue3-admin-gpt 1.0.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.
Files changed (118) hide show
  1. package/.env.development +14 -0
  2. package/.env.production +14 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +106 -0
  5. package/README.md +104 -0
  6. package/build-zip.cjs +53 -0
  7. package/cli.js +110 -0
  8. package/jsconfig.json +9 -0
  9. package/package.json +92 -0
  10. package/public/index.html +20 -0
  11. package/public/robots.txt +2 -0
  12. package/rspack.config.js +282 -0
  13. package/rspack.js +162 -0
  14. package/src/App.vue +9 -0
  15. package/src/api/icon.js +9 -0
  16. package/src/api/router.js +9 -0
  17. package/src/api/table.js +25 -0
  18. package/src/api/tree.js +9 -0
  19. package/src/api/user.js +34 -0
  20. package/src/assets/error_images/401.png +0 -0
  21. package/src/assets/error_images/404.png +0 -0
  22. package/src/assets/error_images/cloud.png +0 -0
  23. package/src/assets/login_images/background.jpg +0 -0
  24. package/src/assets/logo.png +0 -0
  25. package/src/assets/qr_logo/lqr_logo.png +0 -0
  26. package/src/assets/vuejs-fill.svg +4 -0
  27. package/src/components/VabPageHeader/index.vue +133 -0
  28. package/src/config/index.js +7 -0
  29. package/src/config/net.config.js +20 -0
  30. package/src/config/permission.js +136 -0
  31. package/src/config/setting.config.js +62 -0
  32. package/src/config/settings.js +6 -0
  33. package/src/config/theme.config.js +14 -0
  34. package/src/layouts/EmptyLayout.vue +3 -0
  35. package/src/layouts/components/VabAppMain/index.vue +109 -0
  36. package/src/layouts/components/VabAvatar/index.vue +255 -0
  37. package/src/layouts/components/VabBreadcrumb/index.vue +61 -0
  38. package/src/layouts/components/VabFullScreen/index.vue +61 -0
  39. package/src/layouts/components/VabLogo/index.vue +94 -0
  40. package/src/layouts/components/VabNav/index.vue +176 -0
  41. package/src/layouts/components/VabSide/components/VabMenuItem.vue +80 -0
  42. package/src/layouts/components/VabSide/components/VabSideItem.vue +100 -0
  43. package/src/layouts/components/VabSide/components/VabSubmenu.vue +56 -0
  44. package/src/layouts/components/VabSide/index.vue +123 -0
  45. package/src/layouts/components/VabTabs/index.vue +500 -0
  46. package/src/layouts/components/VabTheme/index.vue +603 -0
  47. package/src/layouts/components/VabTop/index.vue +286 -0
  48. package/src/layouts/export.js +29 -0
  49. package/src/layouts/index.vue +339 -0
  50. package/src/main.js +40 -0
  51. package/src/plugins/echarts.js +4 -0
  52. package/src/plugins/index.js +44 -0
  53. package/src/plugins/support.js +16 -0
  54. package/src/router/index.js +400 -0
  55. package/src/store/index.js +26 -0
  56. package/src/store/modules/errorLog.js +27 -0
  57. package/src/store/modules/routes.js +60 -0
  58. package/src/store/modules/settings.js +73 -0
  59. package/src/store/modules/table.js +22 -0
  60. package/src/store/modules/tabsBar.js +109 -0
  61. package/src/store/modules/user.js +131 -0
  62. package/src/styles/element-variables.scss +13 -0
  63. package/src/styles/loading.scss +345 -0
  64. package/src/styles/nav-icons.scss +52 -0
  65. package/src/styles/normalize.scss +353 -0
  66. package/src/styles/spinner/dots.css +68 -0
  67. package/src/styles/spinner/gauge.css +104 -0
  68. package/src/styles/spinner/inner-circles.css +51 -0
  69. package/src/styles/spinner/plus.css +341 -0
  70. package/src/styles/themes/default.scss +1 -0
  71. package/src/styles/transition.scss +18 -0
  72. package/src/styles/vab.scss +476 -0
  73. package/src/styles/variables.scss +69 -0
  74. package/src/utils/accessToken.js +56 -0
  75. package/src/utils/eventBus.js +8 -0
  76. package/src/utils/handleRoutes.js +100 -0
  77. package/src/utils/index.js +231 -0
  78. package/src/utils/message.js +67 -0
  79. package/src/utils/pageTitle.js +11 -0
  80. package/src/utils/password.js +43 -0
  81. package/src/utils/permission.js +19 -0
  82. package/src/utils/request.js +187 -0
  83. package/src/utils/static.js +81 -0
  84. package/src/utils/vab.js +218 -0
  85. package/src/utils/validate.js +48 -0
  86. package/src/views/401.vue +302 -0
  87. package/src/views/404.vue +302 -0
  88. package/src/views/demo/index.vue +591 -0
  89. package/src/views/index/index.vue +1489 -0
  90. package/src/views/login/index.vue +456 -0
  91. package/src/views/register/index.vue +524 -0
  92. package/src/views/vab/calendar.vue +488 -0
  93. package/src/views/vab/campaign.vue +1006 -0
  94. package/src/views/vab/chart.vue +189 -0
  95. package/src/views/vab/customer.vue +666 -0
  96. package/src/views/vab/editor.vue +84 -0
  97. package/src/views/vab/form.vue +151 -0
  98. package/src/views/vab/help.vue +390 -0
  99. package/src/views/vab/icon.vue +113 -0
  100. package/src/views/vab/knowledge.vue +820 -0
  101. package/src/views/vab/nested/menu1/menu2/menu3.vue +29 -0
  102. package/src/views/vab/nested/menu1/menu2.vue +33 -0
  103. package/src/views/vab/nested/menu1.vue +33 -0
  104. package/src/views/vab/nested.vue +97 -0
  105. package/src/views/vab/notification.vue +416 -0
  106. package/src/views/vab/order.vue +507 -0
  107. package/src/views/vab/permissions.vue +214 -0
  108. package/src/views/vab/product.vue +724 -0
  109. package/src/views/vab/project.vue +559 -0
  110. package/src/views/vab/settings.vue +319 -0
  111. package/src/views/vab/statistics.vue +431 -0
  112. package/src/views/vab/table.vue +110 -0
  113. package/src/views/vab/task.vue +613 -0
  114. package/src/views/vab/team.vue +662 -0
  115. package/src/views/vab/tree.vue +44 -0
  116. package/src/views/vab/upload.vue +180 -0
  117. package/src/views/vab/vue3Demo/index.vue +103 -0
  118. package/src/views/vab/workflow.vue +863 -0
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <span :title="isFullscreen ? '退出全屏' : '进入全屏'">
3
+ <!-- 直接使用图标组件,不再包裹在el-icon中 -->
4
+ <component
5
+ :is="isFullscreen ? 'FullScreen' : 'FullScreen'"
6
+ class="nav-icon"
7
+ @click="click"
8
+ />
9
+ </span>
10
+ </template>
11
+
12
+ <script>
13
+ import screenfull from "screenfull";
14
+ import { FullScreen, ScaleToOriginal } from "@element-plus/icons-vue";
15
+
16
+ export default {
17
+ name: "VabFullScreen",
18
+ components: {
19
+ FullScreen,
20
+ ScaleToOriginal,
21
+ },
22
+ data() {
23
+ return {
24
+ isFullscreen: false,
25
+ };
26
+ },
27
+ mounted() {
28
+ this.init();
29
+ },
30
+ beforeDestroy() {
31
+ this.destroy();
32
+ },
33
+ methods: {
34
+ click() {
35
+ if (!screenfull.isEnabled) {
36
+ this.$vab.message("开启全屏失败", "error");
37
+ return false;
38
+ }
39
+ screenfull.toggle();
40
+ this.$emit("refresh");
41
+ },
42
+ change() {
43
+ this.isFullscreen = screenfull.isFullscreen;
44
+ },
45
+ init() {
46
+ if (screenfull.isEnabled) {
47
+ screenfull.on("change", this.change);
48
+ }
49
+ },
50
+ destroy() {
51
+ if (screenfull.isEnabled) {
52
+ screenfull.off("change", this.change);
53
+ }
54
+ },
55
+ },
56
+ };
57
+ </script>
58
+
59
+ <style lang="scss" scoped>
60
+ /* 采用VabNav中定义的统一样式 */
61
+ </style>
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <div :class="'logo-container-' + layout">
3
+ <router-link to="/">
4
+ <!-- 这里是logo变更的位置 -->
5
+ <img v-if="logo" src="@/assets/logo.png" alt="logo" class="logo">
6
+ <span
7
+ :class="{ 'hidden-xs-only': layout === 'horizontal' }"
8
+ :title="title"
9
+ class="title"
10
+ >
11
+ {{ title }}
12
+ </span>
13
+ </router-link>
14
+ </div>
15
+ </template>
16
+ <script>
17
+ import { mapGetters } from "vuex";
18
+
19
+ export default {
20
+ name: "VabLogo",
21
+ data() {
22
+ return {
23
+ title: this.$baseTitle,
24
+ };
25
+ },
26
+ computed: {
27
+ ...mapGetters({
28
+ logo: "settings/logo",
29
+ layout: "settings/layout",
30
+ }),
31
+ },
32
+ };
33
+ </script>
34
+ <style lang="scss" scoped>
35
+ @mixin container {
36
+ position: relative;
37
+ height: $base-top-bar-height;
38
+ overflow: hidden;
39
+ line-height: $base-top-bar-height;
40
+ background: $base-menu-background;
41
+ }
42
+
43
+ @mixin logo {
44
+ display: inline-block;
45
+ width: 34px;
46
+ height: 34px;
47
+ margin-right: 3px;
48
+ color: #fff !important;
49
+ fill: #fff !important;
50
+ vertical-align: middle;
51
+ }
52
+
53
+ @mixin title {
54
+ display: inline-block;
55
+ overflow: hidden;
56
+ font-size: 20px;
57
+ line-height: 55px;
58
+ color: $base-title-color;
59
+ text-overflow: ellipsis;
60
+ white-space: nowrap;
61
+ vertical-align: middle;
62
+ }
63
+
64
+ .logo-container-horizontal {
65
+ @include container;
66
+
67
+ .logo {
68
+ @include logo;
69
+ }
70
+
71
+ .title {
72
+ @include title;
73
+ }
74
+ }
75
+
76
+ .logo-container-vertical {
77
+ @include container;
78
+
79
+ height: $base-logo-height;
80
+ line-height: $base-logo-height;
81
+ text-align: center;
82
+
83
+ .logo {
84
+ @include logo;
85
+ fill: #fff !important;
86
+ }
87
+
88
+ .title {
89
+ @include title;
90
+
91
+ max-width: calc(#{$base-left-menu-width} - 60px);
92
+ }
93
+ }
94
+ </style>
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <div class="nav-container">
3
+ <el-row :gutter="15">
4
+ <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="4">
5
+ <div class="left-panel">
6
+ <component
7
+ :is="collapse ? 'DArrowRight' : 'DArrowLeft'"
8
+ class="nav-icon"
9
+ @click="handleCollapse"
10
+ />
11
+ <vab-breadcrumb class="hidden-xs-only" />
12
+ </div>
13
+ </el-col>
14
+ <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="20">
15
+ <div class="right-panel">
16
+ <vab-full-screen @refresh="refreshRoute" />
17
+ <vab-theme class="hidden-xs-only" />
18
+ <Refresh
19
+ :class="{ 'is-pulsing': pulse }"
20
+ title="重载所有路由"
21
+ @click="refreshRoute"
22
+ class="nav-icon"
23
+ />
24
+ <vab-avatar />
25
+ </div>
26
+ </el-col>
27
+ </el-row>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { ref, computed, onBeforeUnmount } from "vue";
33
+ import { useStore } from "vuex";
34
+ import { Refresh } from "@element-plus/icons-vue";
35
+
36
+ defineOptions({
37
+ name: "VabNav",
38
+ });
39
+
40
+ const store = useStore();
41
+ const pulse = ref(false);
42
+ let timeOutID = null;
43
+
44
+ const collapse = computed(() => store.getters["settings/collapse"]);
45
+
46
+ const handleCollapse = () => {
47
+ store.dispatch("settings/changeCollapse");
48
+ };
49
+
50
+ const refreshRoute = async () => {
51
+ window.$eventBus.emit("reload-router-view");
52
+ pulse.value = true;
53
+ timeOutID = setTimeout(() => {
54
+ pulse.value = false;
55
+ }, 1000);
56
+ };
57
+
58
+ onBeforeUnmount(() => {
59
+ clearTimeout(timeOutID);
60
+ });
61
+ </script>
62
+
63
+ <style lang="scss" scoped>
64
+ .nav-container {
65
+ position: relative;
66
+ height: $base-nav-bar-height;
67
+ padding-right: $base-padding;
68
+ padding-left: $base-padding;
69
+ overflow: hidden;
70
+ user-select: none;
71
+ background: rgba(255, 255, 255, 0.8);
72
+ backdrop-filter: blur(20px) saturate(180%);
73
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
74
+ border-bottom: 1px solid rgba(255, 255, 255, 0.3);
75
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
76
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.6);
77
+
78
+ .left-panel {
79
+ position: relative;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-items: center;
83
+ height: $base-nav-bar-height;
84
+ gap: 8px; /* 折叠按钮与面包屑之间的间距 */
85
+ }
86
+
87
+ .right-panel {
88
+ position: relative;
89
+ display: flex;
90
+ align-content: center;
91
+ align-items: center;
92
+ justify-content: flex-end;
93
+ height: $base-nav-bar-height;
94
+ gap: 4px; /* 适当调整图标间距 */
95
+
96
+ .is-pulsing {
97
+ animation: pulse 1s infinite;
98
+ }
99
+
100
+ @keyframes pulse {
101
+ 0% {
102
+ transform: scale(1);
103
+ }
104
+ 50% {
105
+ transform: scale(1.2);
106
+ }
107
+ 100% {
108
+ transform: scale(1);
109
+ }
110
+ }
111
+ }
112
+
113
+ :deep() {
114
+ svg {
115
+ width: 1.2em;
116
+ height: 1.2em;
117
+ color: rgba(0, 0, 0, 0.7);
118
+ fill: rgba(0, 0, 0, 0.7) !important;
119
+ padding: 6px;
120
+ border-radius: 6px;
121
+ transition: all 0.3s ease;
122
+ }
123
+
124
+ svg:hover {
125
+ background: rgba(0, 0, 0, 0.05);
126
+ color: rgba(0, 0, 0, 0.9);
127
+ fill: rgba(0, 0, 0, 0.9) !important;
128
+ transform: scale(1.05);
129
+ }
130
+
131
+ button {
132
+ svg {
133
+ margin-right: 0;
134
+ color: rgba(255, 255, 255, 0.9);
135
+ background: rgba(0, 122, 255, 0.8);
136
+ border-color: rgba(0, 122, 255, 0.9);
137
+ cursor: pointer;
138
+ fill: rgba(255, 255, 255, 0.9);
139
+
140
+ &:hover {
141
+ background: rgba(0, 122, 255, 0.9);
142
+ border-color: rgba(0, 122, 255, 1);
143
+ }
144
+ }
145
+ }
146
+
147
+ .el-badge {
148
+ margin-right: 0;
149
+
150
+ .el-button {
151
+ background: rgba(255, 255, 255, 0.6);
152
+ border: 1px solid rgba(255, 255, 255, 0.8);
153
+ border-radius: 12px;
154
+ backdrop-filter: blur(10px);
155
+ -webkit-backdrop-filter: blur(10px);
156
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
157
+
158
+ &:hover {
159
+ background: rgba(255, 255, 255, 0.8);
160
+ border-color: rgba(255, 255, 255, 1);
161
+ transform: translateY(-1px);
162
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1),
163
+ 0 2px 4px rgba(0, 0, 0, 0.05);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ // 响应式设计
171
+ @media (max-width: 768px) {
172
+ .nav-container {
173
+ padding: 0 12px;
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <el-menu-item :index="handlePath(routeChildren.path)" @click="handleLink">
3
+ <el-icon
4
+ v-if="routeChildren.meta && routeChildren.meta.icon"
5
+ class="vab-fas-icon"
6
+ >
7
+ <component :is="getIconComponent(routeChildren.meta.icon)" />
8
+ </el-icon>
9
+ <span>{{ routeChildren.meta.title }}</span>
10
+ </el-menu-item>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { isExternal } from "@/utils/validate";
15
+ import path from "path";
16
+ import { faToElIcon } from "@/utils/vab";
17
+ import { useRouter, useRoute } from "vue-router";
18
+
19
+ defineOptions({
20
+ name: "VabMenuItem",
21
+ });
22
+
23
+ const router = useRouter();
24
+ const route = useRoute();
25
+
26
+ const props = defineProps({
27
+ routeChildren: {
28
+ type: Object,
29
+ default: () => null,
30
+ },
31
+ item: {
32
+ type: Object,
33
+ default: () => null,
34
+ },
35
+ fullPath: {
36
+ type: String,
37
+ default: "",
38
+ },
39
+ });
40
+
41
+ const handlePath = (routePath) => {
42
+ if (isExternal(routePath)) {
43
+ return routePath;
44
+ }
45
+ if (isExternal(props.fullPath)) {
46
+ return props.fullPath;
47
+ }
48
+ return path.resolve(props.fullPath, routePath);
49
+ };
50
+
51
+ const handleLink = () => {
52
+ const routePath = props.routeChildren.path;
53
+ const target = props.routeChildren.meta.target;
54
+
55
+ if (target === "_blank") {
56
+ if (isExternal(routePath)) {
57
+ window.open(routePath);
58
+ } else if (isExternal(props.fullPath)) {
59
+ window.open(props.fullPath);
60
+ } else if (route.path !== path.resolve(props.fullPath, routePath)) {
61
+ let routeData = router.resolve(path.resolve(props.fullPath, routePath));
62
+ window.open(routeData.href);
63
+ }
64
+ } else {
65
+ if (isExternal(routePath)) {
66
+ window.location.href = routePath;
67
+ } else if (isExternal(props.fullPath)) {
68
+ window.location.href = props.fullPath;
69
+ } else if (route.path !== path.resolve(props.fullPath, routePath)) {
70
+ router.push(path.resolve(props.fullPath, routePath));
71
+ }
72
+ }
73
+ };
74
+
75
+ // 将路由中的icon名称转换为Element Plus图标组件
76
+ const getIconComponent = (iconName) => {
77
+ // 直接使用导入的faToElIcon函数
78
+ return faToElIcon(iconName);
79
+ };
80
+ </script>
@@ -0,0 +1,100 @@
1
+ <template>
2
+ <component
3
+ :is="menuComponent"
4
+ v-if="!item.hidden"
5
+ :full-path="fullPath"
6
+ :item="item"
7
+ :route-children="routeChildren"
8
+ >
9
+ <template v-if="item.children && item.children.length">
10
+ <vab-side-item
11
+ v-for="route in item.children"
12
+ :key="route.path"
13
+ :full-path="handlePath(route.path)"
14
+ :item="route"
15
+ />
16
+ </template>
17
+ </component>
18
+ </template>
19
+
20
+ <script setup>
21
+ import { ref, computed } from "vue";
22
+ import { isExternal } from "@/utils/validate";
23
+ import path from "path";
24
+
25
+ defineOptions({
26
+ name: "VabSideItem",
27
+ });
28
+
29
+ // Props 定义
30
+ const props = defineProps({
31
+ item: {
32
+ type: Object,
33
+ required: true,
34
+ },
35
+ fullPath: {
36
+ type: String,
37
+ default: "",
38
+ },
39
+ });
40
+
41
+ // 数据
42
+ const routeChildren = ref(null);
43
+ let onlyOneChild = null;
44
+
45
+ // 方法
46
+ const handleChildren = (children = [], parent) => {
47
+ if (children === null) children = [];
48
+ const showChildren = children.filter((item) => {
49
+ if (item.hidden) {
50
+ return false;
51
+ } else {
52
+ routeChildren.value = item;
53
+ return true;
54
+ }
55
+ });
56
+
57
+ if (showChildren.length === 1) {
58
+ return true;
59
+ }
60
+
61
+ if (showChildren.length === 0) {
62
+ routeChildren.value = {
63
+ ...parent,
64
+ path: "",
65
+ notShowChildren: true,
66
+ };
67
+ return true;
68
+ }
69
+ return false;
70
+ };
71
+
72
+ const handlePath = (routePath) => {
73
+ if (isExternal(routePath)) {
74
+ return routePath;
75
+ }
76
+ if (isExternal(props.fullPath)) {
77
+ return props.fullPath;
78
+ }
79
+ return path.resolve(props.fullPath, routePath);
80
+ };
81
+
82
+ // 计算属性
83
+ const menuComponent = computed(() => {
84
+ if (
85
+ handleChildren(props.item.children, props.item) &&
86
+ (!routeChildren.value.children || routeChildren.value.notShowChildren) &&
87
+ !props.item.alwaysShow
88
+ ) {
89
+ return "VabMenuItem";
90
+ } else {
91
+ return "VabSubmenu";
92
+ }
93
+ });
94
+ </script>
95
+
96
+ <style lang="scss" scoped>
97
+ .vab-nav-icon {
98
+ margin-right: 4px;
99
+ }
100
+ </style>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <el-sub-menu
3
+ ref="subMenu"
4
+ :index="handlePath(item.path)"
5
+ :popper-append-to-body="false"
6
+ >
7
+ <template #title>
8
+ <el-icon v-if="item.meta && item.meta.icon" class="vab-fas-icon">
9
+ <component :is="getIconComponent(item.meta.icon)" />
10
+ </el-icon>
11
+ <span>{{ item.meta.title }}</span>
12
+ </template>
13
+ <slot />
14
+ </el-sub-menu>
15
+ </template>
16
+
17
+ <script setup>
18
+ import { isExternal } from "@/utils/validate";
19
+ import path from "path";
20
+ import { faToElIcon } from "@/utils/vab";
21
+
22
+ defineOptions({
23
+ name: "VabSubmenu",
24
+ });
25
+
26
+ const props = defineProps({
27
+ routeChildren: {
28
+ type: Object,
29
+ default: () => null,
30
+ },
31
+ item: {
32
+ type: Object,
33
+ default: () => null,
34
+ },
35
+ fullPath: {
36
+ type: String,
37
+ default: "",
38
+ },
39
+ });
40
+
41
+ const handlePath = (routePath) => {
42
+ if (isExternal(routePath)) {
43
+ return routePath;
44
+ }
45
+ if (isExternal(props.fullPath)) {
46
+ return props.fullPath;
47
+ }
48
+ return path.resolve(props.fullPath, routePath);
49
+ };
50
+
51
+ // 将路由中的icon名称转换为Element Plus图标组件
52
+ const getIconComponent = (iconName) => {
53
+ // 直接使用导入的faToElIcon函数
54
+ return faToElIcon(iconName);
55
+ };
56
+ </script>
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <el-scrollbar :class="{ 'is-collapse': collapse }" class="side-container">
3
+ <vab-logo />
4
+ <el-menu
5
+ active-text-color=" hsla(0, 0%, 100%, 0.95)"
6
+ background-color="#21252b"
7
+ :collapse="collapse"
8
+ :collapse-transition="false"
9
+ :default-active="activeMenu"
10
+ :default-openeds="defaultOpens"
11
+ text-color=" hsla(0, 0%, 100%, 0.95)"
12
+ mode="vertical"
13
+ >
14
+ <template v-for="route in routes">
15
+ <vab-side-item :full-path="route.path" :item="route" />
16
+ </template>
17
+ </el-menu>
18
+ </el-scrollbar>
19
+ </template>
20
+
21
+ <script setup>
22
+ import variables from "@/styles/variables.scss";
23
+ import { computed } from "vue";
24
+ import { useStore } from "vuex";
25
+ import { useRoute } from "vue-router";
26
+ import { defaultOopeneds } from "@/config";
27
+
28
+ defineOptions({
29
+ name: "VabSide",
30
+ });
31
+
32
+ const store = useStore();
33
+ const route = useRoute();
34
+
35
+ const collapse = computed(() => store.getters["settings/collapse"]);
36
+ const routes = computed(() => store.getters["routes/routes"]);
37
+
38
+ const defaultOpens = computed(() => {
39
+ // 使用配置文件中的defaultOopeneds
40
+ return defaultOopeneds;
41
+ });
42
+
43
+ const activeMenu = computed(() => {
44
+ const { meta, path } = route;
45
+ if (meta.activeMenu) {
46
+ return meta.activeMenu;
47
+ }
48
+ return path;
49
+ });
50
+ </script>
51
+
52
+ <style lang="scss" scoped>
53
+ @mixin active {
54
+ &:hover {
55
+ color: $base-color-white;
56
+ background-color: $base-menu-background-active !important;
57
+ }
58
+
59
+ &.is-active {
60
+ color: $base-color-white;
61
+ background-color: $base-menu-background-active !important;
62
+ }
63
+ }
64
+
65
+ .side-container {
66
+ position: fixed;
67
+ top: 0;
68
+ bottom: 0;
69
+ left: 0;
70
+ z-index: $base-z-index;
71
+ width: $base-left-menu-width;
72
+ height: 100vh;
73
+ overflow: hidden;
74
+ background: $base-menu-background;
75
+ box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
76
+ transition: width $base-transition-time;
77
+
78
+ &.is-collapse {
79
+ width: $base-left-menu-width-min;
80
+ border-right: 0;
81
+
82
+ :deep() {
83
+ .el-menu {
84
+ transition: width $base-transition-time;
85
+ }
86
+
87
+ .el-menu--collapse {
88
+ border-right: 0;
89
+
90
+ .el-submenu__icon-arrow {
91
+ right: 10px;
92
+ margin-top: -3px;
93
+ }
94
+
95
+ .el-menu-item,
96
+ .el-submenu {
97
+ text-align: center;
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ :deep() {
104
+ .el-scrollbar__wrap {
105
+ overflow-x: hidden;
106
+ }
107
+
108
+ .el-menu {
109
+ border: 0;
110
+ }
111
+
112
+ .el-menu-item,
113
+ .el-submenu__title {
114
+ height: $base-menu-item-height;
115
+ line-height: $base-menu-item-height;
116
+ }
117
+
118
+ .el-menu-item {
119
+ @include active;
120
+ }
121
+ }
122
+ }
123
+ </style>