hlq-cli 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 (88) hide show
  1. package/README.md +18 -0
  2. package/bin/index.js +16 -0
  3. package/lib/aesCreate.js +11 -0
  4. package/lib/axiosCreate.js +11 -0
  5. package/lib/create.js +172 -0
  6. package/lib/echartCreate.js +12 -0
  7. package/lib/jwtDecodeCreate.js +16 -0
  8. package/lib/rsaCreate.js +11 -0
  9. package/lib/stateCreate.js +34 -0
  10. package/lib/websocketCreate.js +16 -0
  11. package/package.json +21 -0
  12. package/templates/.env +1 -0
  13. package/templates/.env.dev +2 -0
  14. package/templates/.env.pro +2 -0
  15. package/templates/index.html +15 -0
  16. package/templates/package-lock.json +4058 -0
  17. package/templates/package.json +31 -0
  18. package/templates/public/config.js +1 -0
  19. package/templates/public/font/iconfont.css +579 -0
  20. package/templates/public/font/iconfont.js +1 -0
  21. package/templates/public/font/iconfont.ttf +0 -0
  22. package/templates/public/font/iconfont.woff +0 -0
  23. package/templates/public/font/iconfont.woff2 +0 -0
  24. package/templates/src/App.vue +35 -0
  25. package/templates/src/components/chart/barChart.vue +103 -0
  26. package/templates/src/components/chart/color.ts +43 -0
  27. package/templates/src/components/chart/lineChart.vue +114 -0
  28. package/templates/src/components/chart/mapChart.vue +135 -0
  29. package/templates/src/components/chart/mixedChart.vue +148 -0
  30. package/templates/src/components/chart/pieChart.vue +104 -0
  31. package/templates/src/components/chart/radarChart.vue +112 -0
  32. package/templates/src/components/chart/scatterChart.vue +144 -0
  33. package/templates/src/components/chart/sunburstChart.vue +183 -0
  34. package/templates/src/components/descript/index.vue +45 -0
  35. package/templates/src/components/dialog/index.vue +54 -0
  36. package/templates/src/components/drawer/index.vue +53 -0
  37. package/templates/src/components/form/component/cascader.vue +65 -0
  38. package/templates/src/components/form/component/checkbox.vue +31 -0
  39. package/templates/src/components/form/component/datePicker.vue +39 -0
  40. package/templates/src/components/form/component/dateRange.vue +36 -0
  41. package/templates/src/components/form/component/datetimePicker.vue +25 -0
  42. package/templates/src/components/form/component/fileUpload.vue +80 -0
  43. package/templates/src/components/form/component/formFun.ts +132 -0
  44. package/templates/src/components/form/component/imageUpload.vue +92 -0
  45. package/templates/src/components/form/component/input.vue +41 -0
  46. package/templates/src/components/form/component/location.vue +79 -0
  47. package/templates/src/components/form/component/radio.vue +31 -0
  48. package/templates/src/components/form/component/select.vue +66 -0
  49. package/templates/src/components/form/component/textarea.vue +26 -0
  50. package/templates/src/components/form/component/timePicker.vue +28 -0
  51. package/templates/src/components/form/component/upload.ts +20 -0
  52. package/templates/src/components/form/formInterface.ts +115 -0
  53. package/templates/src/components/form/index.vue +193 -0
  54. package/templates/src/components/form/item.vue +323 -0
  55. package/templates/src/components/groupForm/index.vue +91 -0
  56. package/templates/src/components/icon/index.vue +29 -0
  57. package/templates/src/components/layout/header.vue +238 -0
  58. package/templates/src/components/layout/index.vue +167 -0
  59. package/templates/src/components/layout/menu.vue +130 -0
  60. package/templates/src/components/layout/sideBarItem.vue +49 -0
  61. package/templates/src/components/searchBox/height.ts +9 -0
  62. package/templates/src/components/searchBox/index.vue +265 -0
  63. package/templates/src/components/table/index.vue +371 -0
  64. package/templates/src/components/table/table.ts +23 -0
  65. package/templates/src/components/tree/index.vue +222 -0
  66. package/templates/src/components/tree/lazyTree.vue +136 -0
  67. package/templates/src/data.d.ts +4 -0
  68. package/templates/src/main.ts +18 -0
  69. package/templates/src/router/index.ts +60 -0
  70. package/templates/src/store/menuInterface.ts +10 -0
  71. package/templates/src/store/permission.ts +59 -0
  72. package/templates/src/store/user.ts +24 -0
  73. package/templates/src/utils/alioss/index.ts +0 -0
  74. package/templates/src/utils/axios/http.ts +99 -0
  75. package/templates/src/utils/axios/index.ts +112 -0
  76. package/templates/src/utils/axios/service.ts +8 -0
  77. package/templates/src/utils/crypto/index.ts +28 -0
  78. package/templates/src/utils/rsa/index.ts +18 -0
  79. package/templates/src/utils/token/index.ts +6 -0
  80. package/templates/src/utils/tree/index.ts +74 -0
  81. package/templates/src/utils/websocket/index.ts +136 -0
  82. package/templates/src/views/login/index.vue +248 -0
  83. package/templates/src/views/templete/table.vue +122 -0
  84. package/templates/src/views/templete/tableConfig.ts +153 -0
  85. package/templates/tsconfig.app.json +19 -0
  86. package/templates/tsconfig.json +7 -0
  87. package/templates/tsconfig.node.json +23 -0
  88. package/templates/vite.config.ts +34 -0
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <!-- 表单页面 -->
3
+ <div class="formUpdata">
4
+ <template v-for="(item, index) in config" v-if="state.hasGroup">
5
+ <el-divider content-position="left">{{ item.title }}</el-divider>
6
+ <div>
7
+ <Form
8
+ :form-data="formData"
9
+ :form-item-config="item.config"
10
+ :onlyRead="onlyRead"
11
+ :ref="(el) => setItemRef(el, index as number)"
12
+ ></Form>
13
+ </div>
14
+ </template>
15
+ <template v-else>
16
+ <Form
17
+ :form-data="formData"
18
+ :form-item-config="config"
19
+ :onlyRead="onlyRead"
20
+ :ref="(el) => setItemRef(el, 0)"
21
+ ></Form>
22
+ </template>
23
+ </div>
24
+ </template>
25
+ <script lang="ts" setup>
26
+ import { onMounted, reactive, ref } from 'vue'
27
+ import Form from '@/components/form/index.vue'
28
+
29
+ interface Props {
30
+ onlyRead?: boolean
31
+ config: any
32
+ formData: Data
33
+ }
34
+ const props = withDefaults(defineProps<Props>(), {
35
+ onlyRead: false,
36
+ })
37
+ const formRef = ref<any[]>([])
38
+ interface stateInterface {
39
+ buttonList: any
40
+ hasGroup: boolean
41
+ }
42
+ const state = reactive<stateInterface>({
43
+ buttonList: [],
44
+ hasGroup: false,
45
+ })
46
+ const setItemRef = (el: any, index: number) => {
47
+ if (el) {
48
+ formRef.value[index] = el
49
+ }
50
+ }
51
+ const getFormData = async () => {
52
+ let valid = true
53
+ for (let item of formRef.value) {
54
+ const flag = await item.valid()
55
+ valid = valid && flag
56
+ }
57
+ if (!valid) {
58
+ return
59
+ }
60
+ let data = {}
61
+ for (let item of formRef.value) {
62
+ const formData = item.getFormData()
63
+ data = { ...data, ...formData }
64
+ }
65
+ return data
66
+ }
67
+ const clear = () => {
68
+ for (let item of formRef.value) {
69
+ item.clear()
70
+ }
71
+ }
72
+ onMounted(() => {
73
+ if (props.config.find((item: any) => item.type === 'group')) {
74
+ state.hasGroup = true
75
+ } else {
76
+ state.hasGroup = false
77
+ }
78
+ })
79
+ defineExpose({ getFormData, clear })
80
+ </script>
81
+ <style lang="scss" scoped>
82
+ .formUpdata {
83
+ // width: calc(100% - 2 * var(--el-main-padding-left));
84
+ // height: calc(100% - 2 * var(--el-main-padding-top) - 6px);
85
+ .buttonBox {
86
+ display: flex;
87
+ gap: 12px;
88
+ justify-content: center;
89
+ }
90
+ }
91
+ </style>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <svg class="icon" aria-hidden="true" v-if="name.indexOf('icon-') === 0">
3
+ <use :xlink:href="`#${name}`"></use>
4
+ </svg>
5
+ <img v-else :src="imagePath" alt="" class="icon" />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { computed } from "vue";
10
+
11
+ interface Props {
12
+ name: string;
13
+ }
14
+
15
+ const { name } = defineProps<Props>();
16
+
17
+ const imagePath = computed(() => {
18
+ try {
19
+ // 动态导入SVG文件
20
+ return new URL(`../../assets/svg/${name}.svg`, import.meta.url).href;
21
+ } catch (error) {
22
+ console.error(`Failed to load SVG: ${name}`, error);
23
+ return ''; // 返回空字符串或默认图片路径
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <style scoped>
29
+ </style>
@@ -0,0 +1,238 @@
1
+ <template>
2
+ <div class="header">
3
+ <div class="logoBox">
4
+ <div class="logoIcon"></div>
5
+ <div class="title">{title}</div>
6
+ </div>
7
+ <slot></slot>
8
+ <div class="otherBox">
9
+ <el-dropdown @command="handleCommand">
10
+ <div class="flexRow">
11
+ <div class="person">
12
+ <!-- <icon name="icon-changongxiao_gerenzhongxin"></icon> -->
13
+ </div>
14
+ <div style="padding: 12px; color: #fff">
15
+ <div>{{ userStore.otherMessage.name }}</div>
16
+ </div>
17
+ </div>
18
+ <template #dropdown>
19
+ <el-dropdown-menu>
20
+ <el-dropdown-item command="changePassword">
21
+ 修改密码
22
+ </el-dropdown-item>
23
+ <el-dropdown-item command="quit">退出</el-dropdown-item>
24
+ <!-- <el-dropdown-item command="personalCenter">
25
+ 个人中心
26
+ </el-dropdown-item> -->
27
+ </el-dropdown-menu>
28
+ </template>
29
+ </el-dropdown>
30
+ </div>
31
+ <Dialog
32
+ title="修改密码"
33
+ v-model:model-value="state.changePasswordShow"
34
+ width="460px"
35
+ @submit="passwordSubmit"
36
+ destroy-on-close
37
+ >
38
+ <div class="changePassword">
39
+ <el-form label-width="80px" class="changePasswordForm">
40
+ <el-form-item label="旧密码" prop="oldPassword">
41
+ <el-input
42
+ v-model="state.oldPassword"
43
+ type="password"
44
+ show-password
45
+ ></el-input>
46
+ </el-form-item>
47
+ <el-form-item label="新密码" prop="newPassword">
48
+ <el-input
49
+ v-model="state.newPassword"
50
+ type="password"
51
+ show-password
52
+ autocomplete="new-password"
53
+ ></el-input>
54
+ </el-form-item>
55
+ <el-form-item label="确认密码" prop="confirmPassword">
56
+ <el-input
57
+ v-model="state.confirmPassword"
58
+ type="password"
59
+ show-password
60
+ autocomplete="new-password"
61
+ ></el-input>
62
+ </el-form-item>
63
+ </el-form>
64
+ </div>
65
+ </Dialog>
66
+ </div>
67
+ </template>
68
+ <script setup lang="ts">
69
+ import { useUserStore } from '@/store/user'
70
+ import { onMounted, reactive, ref, watch } from 'vue'
71
+ import Dialog from '@/components/dialog/index.vue'
72
+ import { ElMessage } from 'element-plus'
73
+ import { PublicData } from '@/utils/crypto'
74
+ const userStore = useUserStore()
75
+ const state = reactive({
76
+ changePasswordShow: false,
77
+ oldPassword: '',
78
+ newPassword: '',
79
+ confirmPassword: '',
80
+ })
81
+ const handleCommand = (data: string) => {
82
+ switch (data) {
83
+ case 'quit':
84
+ break
85
+ case 'personalCenter':
86
+ break
87
+ case 'changePassword':
88
+ state.changePasswordShow = true
89
+ break
90
+ }
91
+ }
92
+ const passwordSubmit = async () => {
93
+ // 密码长度8-20个字符
94
+ // 密码不能包含空格
95
+ // 密码由字母开头且必须包含大写字母、小写字母、数字,支持特殊字符!@#$%^&*()-+
96
+ if (state.newPassword !== state.confirmPassword) {
97
+ ElMessage.error('两次输入的密码不一致,请重新输入')
98
+ return
99
+ }
100
+ const passwordRegex =
101
+ /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)[A-Za-z][A-Za-z\d!@#$%^&*()\-+]{7,19}$/
102
+ if (!passwordRegex.test(state.newPassword)) {
103
+ ElMessage.error(
104
+ '密码必须由字母开头,且包含大写字母、小写字母、数字,支持特殊字符!@#$%^&*()-+,长度为8-20个字符',
105
+ )
106
+ return
107
+ }
108
+
109
+ // const userInfo = await api(
110
+ // apiUrl.getUserInfo,
111
+ // 'get',
112
+ // {
113
+ // id: userStore.otherMessage.userId,
114
+ // },
115
+ // false,
116
+ // )
117
+ // if (PublicData(state.oldPassword) !== userInfo.data.password) {
118
+ // ElMessage.error('旧密码输入错误,请重新输入')
119
+ // return
120
+ // }
121
+ const params = {
122
+ username: userStore.otherMessage.username,
123
+ oldPwd: PublicData(state.oldPassword),
124
+ newPwd: PublicData(state.newPassword),
125
+ }
126
+ }
127
+ // 切换角色
128
+ const change = (key: string) => {
129
+ // keepAliveStore.changeCurrentRole(key)
130
+ }
131
+ onMounted(() => {
132
+ // keepAliveStore.changeCurrentRole(active.value, true, true)
133
+ })
134
+ watch(
135
+ () => state.changePasswordShow,
136
+ (newVal: boolean) => {
137
+ if (!newVal) {
138
+ state.oldPassword = ''
139
+ state.newPassword = ''
140
+ state.confirmPassword = ''
141
+ }
142
+ },
143
+ )
144
+ </script>
145
+ <style scoped lang="scss">
146
+ .header {
147
+ // background-image: url(@/assets/img/header.png);
148
+ background-color: var(--el-color-primary);
149
+ height: 100%;
150
+ width: calc(100% - 48px);
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: space-between;
154
+ padding: 0 24px;
155
+ .logoBox {
156
+ display: flex;
157
+ align-items: center;
158
+ height: 36px;
159
+ width: 220px;
160
+ justify-content: space-around;
161
+ .logoIcon {
162
+ display: flex;
163
+ align-items: center;
164
+ background-image: url(@/assets/img/logo.png);
165
+ background-size: cover;
166
+ height: 36px;
167
+ width: 36px;
168
+ }
169
+ .title {
170
+ color: #fff;
171
+ font-size: 20px;
172
+ font-weight: bold;
173
+ margin-left: 10px;
174
+ }
175
+ }
176
+ .logo {
177
+ font-size: 40px;
178
+ border-radius: 8px;
179
+ width: 40px;
180
+ height: 40px;
181
+ display: flex;
182
+ justify-content: center;
183
+ align-items: center;
184
+ }
185
+ .name {
186
+ color: #000;
187
+ text-shadow:
188
+ -1px 0 #fff,
189
+ 0 1px #fff,
190
+ 1px 0 #fff,
191
+ 0 -1px #fff;
192
+ font-size: 24px;
193
+ font-weight: bold;
194
+ margin-left: 10px;
195
+ }
196
+ .roleSelect {
197
+ width: 120px;
198
+ color: #fff;
199
+ }
200
+ .otherBox {
201
+ display: flex;
202
+ align-items: center;
203
+ height: 100%;
204
+
205
+ .person {
206
+ width: 40px;
207
+ height: 40px;
208
+ border-radius: 50%;
209
+ font-size: 20px;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ background-color: var(--el-color-primary-light-9);
214
+ background-image: url(@/assets/img/user.png);
215
+ background-size: cover;
216
+ }
217
+ }
218
+ }
219
+ .changePassword {
220
+ width: 400px;
221
+ .changePasswordForm {
222
+ margin-top: 20px;
223
+ width: 100%;
224
+ height: 100%;
225
+ display: flex;
226
+ flex-direction: column;
227
+ justify-content: center;
228
+ align-items: center;
229
+ .el-form-item {
230
+ width: 100%;
231
+ margin-bottom: 20px;
232
+ .el-input {
233
+ width: 100%;
234
+ }
235
+ }
236
+ }
237
+ }
238
+ </style>
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <div class="common-layout">
3
+ <el-header>
4
+ <layoutHeader>
5
+ <div style="width: calc(100% - 600px); padding: 0 24px">
6
+ <layoutMenu @fold-change="foldChange" mode="horizontal"></layoutMenu>
7
+ </div>
8
+ </layoutHeader>
9
+ </el-header>
10
+ <el-container class="main">
11
+ <el-main>
12
+ <div style="margin-bottom: 12px">
13
+ <el-breadcrumb separator="/">
14
+ <el-breadcrumb-item
15
+ v-for="item in routeList"
16
+ :key="item.path"
17
+ :to="item.meta.code ? item.path : undefined"
18
+ style="font-size: 12px"
19
+ >
20
+ {{ item.meta.title }}
21
+ </el-breadcrumb-item>
22
+ </el-breadcrumb>
23
+ </div>
24
+ <div
25
+ class="topContent"
26
+ :style="'background:#fff0;padding: 0;border-radius: 0;height: calc(100% - 12px - 12px - 10px);'"
27
+ >
28
+ <template v-if="router.currentRoute.value.meta.style !== 'nopanel'">
29
+ <div class="panel" style="height: calc(100% - 24px)">
30
+ <el-scrollbar
31
+ height="100%"
32
+ style="width: 100%; height: calc(100% - 8px)"
33
+ >
34
+ <router-view v-slot="{ Component }">
35
+ <component :is="Component" />
36
+ </router-view>
37
+ </el-scrollbar>
38
+ </div>
39
+ </template>
40
+ <template v-else>
41
+ <router-view v-slot="{ Component }">
42
+ <component :is="Component" />
43
+ </router-view>
44
+ </template>
45
+ </div>
46
+ </el-main>
47
+ </el-container>
48
+ </div>
49
+ </template>
50
+ <script setup lang="ts">
51
+ import { computed, reactive, watch, watchEffect } from 'vue'
52
+ import layoutHeader from './header.vue'
53
+ import layoutMenu from './menu.vue'
54
+ import { useRoute, useRouter } from 'vue-router'
55
+ const router = useRouter()
56
+ const route = useRoute()
57
+ const routeList = computed(() => {
58
+ return [
59
+ {
60
+ path: '/home',
61
+ meta: {
62
+ title: '首页',
63
+ code: 'home',
64
+ },
65
+ },
66
+ ].concat(
67
+ route.matched
68
+ .filter((item) => item.path !== '/' && item.path !== '/home')
69
+ .map((item) => {
70
+ return {
71
+ path: item.path,
72
+ meta: {
73
+ title: item.meta.title as string,
74
+ code: item.meta.code as string,
75
+ },
76
+ }
77
+ }),
78
+ )
79
+ })
80
+ const state = reactive({
81
+ isCollapse: false,
82
+ hasBack: false,
83
+ })
84
+ const foldChange = (isCollapse: boolean) => {
85
+ state.isCollapse = isCollapse
86
+ }
87
+ watch(
88
+ () => router.currentRoute.value,
89
+ () => {
90
+ // 当前路由
91
+ const currentRoute = router.currentRoute.value
92
+ state.hasBack =
93
+ typeof window !== 'undefined' &&
94
+ window.history?.state?.back !== '/login' &&
95
+ window.history?.state?.back !== currentRoute.fullPath
96
+ },
97
+ {
98
+ immediate: true,
99
+ deep: true,
100
+ },
101
+ )
102
+ </script>
103
+ <style scoped lang="scss">
104
+ .common-layout {
105
+ height: 100vh;
106
+ width: 100vw;
107
+ }
108
+ .tabs {
109
+ height: 48px;
110
+ background-color: #fff;
111
+ display: flex;
112
+ align-items: center;
113
+ padding: 0 24px;
114
+ :deep() {
115
+ .el-header {
116
+ padding: 0;
117
+ }
118
+ .el-tabs__header {
119
+ border: none;
120
+ margin: 0 0 var(--el-main-padding);
121
+ }
122
+ .el-tabs__nav {
123
+ border: none !important;
124
+ }
125
+ .el-tabs__item,
126
+ .is-active {
127
+ background-color: #fff;
128
+ border: none !important;
129
+ }
130
+ .el-tabs__item.is-active {
131
+ background-color: #fff !important;
132
+ /* background-color: var(--el-color-primary-light-9) !important; */
133
+ }
134
+ .el-tabs--card > .el-tabs__header {
135
+ border-bottom: none;
136
+ }
137
+ }
138
+ }
139
+ :deep() {
140
+ .el-scrollbar__view {
141
+ height: 100% !important;
142
+ width: 100%;
143
+ }
144
+ }
145
+ .main {
146
+ height: calc(100% - 48px);
147
+ // height: calc(100% - var(--el-header-height) - 48px);
148
+ }
149
+ .menuBox {
150
+ transition: width 0.3s;
151
+ overflow: hidden;
152
+ }
153
+ .topContent {
154
+ border-radius: 12px;
155
+ background-color: #fff;
156
+ height: calc(100% - 20px - var(--el-tabs-header-height) - 48px);
157
+ .pageHeader {
158
+ height: 24px;
159
+ margin-bottom: 10px;
160
+ display: flex;
161
+ align-items: center;
162
+ .goBack {
163
+ cursor: pointer;
164
+ }
165
+ }
166
+ }
167
+ </style>
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <div class="menuList">
3
+ <el-scrollbar class="wrap-scroll" v-if="mode === 'vertical'">
4
+ <el-menu
5
+ :mode="mode"
6
+ ref="menuRef"
7
+ :default-openeds="state.openedList"
8
+ :collapse="homeStore.isCollapse"
9
+ :unique-opened="true"
10
+ :default-active="router.currentRoute.value.name"
11
+ :ellipsis-icon="MoreFilled"
12
+ >
13
+ <SideBarItem
14
+ v-for="item in homeStore.menuList"
15
+ :key="item.menuCode!"
16
+ :item="item"
17
+ :isCollapse="homeStore.isCollapse"
18
+ />
19
+ </el-menu>
20
+ </el-scrollbar>
21
+ <div class="horizontalMenu" v-else>
22
+ <el-menu
23
+ :mode="mode"
24
+ ref="menuRef"
25
+ :default-openeds="state.openedList"
26
+ :collapse="homeStore.isCollapse"
27
+ :unique-opened="true"
28
+ :default-active="router.currentRoute.value.name"
29
+ :ellipsis-icon="MoreFilled"
30
+ >
31
+ <SideBarItem
32
+ v-for="item in homeStore.menuList"
33
+ :key="item.menuCode!"
34
+ :item="item"
35
+ :isCollapse="homeStore.isCollapse"
36
+ />
37
+ </el-menu>
38
+ </div>
39
+ <div class="foldBox" v-if="mode === 'vertical'">
40
+ <div
41
+ class="iconBox"
42
+ @click="foldChange"
43
+ :class="{ active: homeStore.isCollapse }"
44
+ >
45
+ <icon name="icon-zhankaizhedie"></icon>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </template>
50
+
51
+ <script lang="ts" setup>
52
+ import { onMounted, reactive, ref } from 'vue'
53
+ import SideBarItem from './sideBarItem.vue'
54
+ import icon from '@/components/icon/index.vue'
55
+ import { usePermissionStore } from '@/store/permission'
56
+ import { useRouter } from 'vue-router'
57
+ import { MoreFilled } from '@element-plus/icons-vue'
58
+ interface Props {
59
+ mode: 'vertical' | 'horizontal'
60
+ isOpen?: boolean
61
+ }
62
+ const props = withDefaults(defineProps<Props>(), {
63
+ mode: 'vertical',
64
+ isOpen: false,
65
+ })
66
+ const router = useRouter()
67
+ const store = usePermissionStore()
68
+ const homeStore = reactive({
69
+ isCollapse: false,
70
+ menuList: [] as Array<menuItem>,
71
+ })
72
+ const emit = defineEmits(['foldChange'])
73
+ const menuRef = ref()
74
+ // 展开折叠切换
75
+ const foldChange = () => {
76
+ homeStore.isCollapse = !homeStore.isCollapse
77
+ if (!homeStore.isCollapse && props.isOpen) {
78
+ state.openedList.forEach((item: string) => {
79
+ menuRef.value?.open(item)
80
+ })
81
+ }
82
+ emit('foldChange', homeStore.isCollapse)
83
+ }
84
+ const state = reactive({
85
+ openedList: [] as Array<string>,
86
+ })
87
+
88
+ onMounted(() => {
89
+ homeStore.menuList = [...store.menuList]
90
+ if (props.isOpen) {
91
+ state.openedList = store.openList
92
+ }
93
+ })
94
+ </script>
95
+ <style scoped lang="scss">
96
+ .menuList {
97
+ height: 100%;
98
+ width: 100%;
99
+ overflow: hidden;
100
+ }
101
+
102
+ .wrap-scroll {
103
+ height: calc(100% - 50px);
104
+ }
105
+
106
+ .horizontalMenu {
107
+ width: 100%;
108
+ display: flex;
109
+ justify-content: center;
110
+ }
111
+ :deep() {
112
+ .el-menu {
113
+ border: none;
114
+ }
115
+ .el-menu--horizontal.el-menu {
116
+ max-width: 100% !important;
117
+ }
118
+ }
119
+ .foldBox {
120
+ display: flex;
121
+ justify-content: center;
122
+ align-items: center;
123
+ .iconBox {
124
+ transition: all 0.3s;
125
+ }
126
+ .active {
127
+ transform: rotate(180deg);
128
+ }
129
+ }
130
+ </style>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <el-sub-menu
3
+ class="grid"
4
+ v-if="props.item.children"
5
+ :index="props.item.menuCode"
6
+ >
7
+ <template #title>
8
+ <!-- <icon :name="props.item.icon || 'icon-changongxiao_gongsishuju'"></icon> -->
9
+ <span v-if="!props.isCollapse" class="menuTitle ellipsis">
10
+ {{ props.item.menuName }}
11
+ </span>
12
+ </template>
13
+ <SideBarItem
14
+ v-for="i in props.item.children"
15
+ :key="i.menuCode"
16
+ :item="i"
17
+ :isCollapse="props.isCollapse"
18
+ />
19
+ </el-sub-menu>
20
+ <el-menu-item @click="goto" v-else :index="props.item.menuCode">
21
+ <!-- <icon :name="props.item.icon || 'icon-changongxiao_gongsishuju'"></icon> -->
22
+ <span class="menuTitle ellipsis">{{ props.item?.menuName }}</span>
23
+ </el-menu-item>
24
+ </template>
25
+
26
+ <script lang="ts" setup>
27
+ import { useRouter } from 'vue-router'
28
+ const router = useRouter()
29
+ type Props = {
30
+ item: menuItem
31
+ isCollapse: boolean
32
+ }
33
+ const props = defineProps<Props>()
34
+ const goto = () => {
35
+ router.push({
36
+ path: props.item.menuPath,
37
+ query: { pageCode: props.item.menuCode },
38
+ })
39
+ }
40
+ </script>
41
+ <style lang="scss" scoped>
42
+ .menuTitle {
43
+ margin-left: 10px;
44
+ }
45
+ .ellipsis {
46
+ display: flex;
47
+ align-items: center;
48
+ }
49
+ </style>