67
67
68
68
<!-- 列表 -->
69
69
<ContentWrap >
70
- <el-table
70
+ <el-tree-v2
71
71
v-if =" refreshTable"
72
72
v-loading =" loading"
73
73
:data =" list"
74
- :default-expand-all =" isExpandAll"
75
- row-key =" id"
74
+ :props =" {
75
+ label: 'name',
76
+ children: 'children'
77
+ }"
78
+ :default-expanded-keys =" isExpandAll ? list.map(item => item.id) : []"
79
+ :height =" 600"
80
+ :item-size =" 40"
81
+ :virtual-scroll-horizontal =" true"
82
+ :highlight-current =" true"
83
+ @current-change =" handleCurrentChange"
76
84
>
77
- <el-table-column :show-overflow-tooltip =" true" label =" 菜单名称" prop =" name" width =" 250" />
78
- <el-table-column align =" center" label =" 图标" prop =" icon" width =" 100" >
79
- <template #default =" scope " >
80
- <Icon :icon =" scope.row.icon" />
81
- </template >
82
- </el-table-column >
83
- <el-table-column label =" 排序" prop =" sort" width =" 60" />
84
- <el-table-column :show-overflow-tooltip =" true" label =" 权限标识" prop =" permission" />
85
- <el-table-column :show-overflow-tooltip =" true" label =" 组件路径" prop =" component" />
86
- <el-table-column :show-overflow-tooltip =" true" label =" 组件名称" prop =" componentName" />
87
- <el-table-column label =" 状态" prop =" status" >
88
- <template #default =" scope " >
89
- <el-switch
90
- class =" ml-4px"
91
- v-model =" scope.row.status"
92
- v-hasPermi =" ['system:menu:update']"
93
- :active-value =" CommonStatusEnum.ENABLE"
94
- :inactive-value =" CommonStatusEnum.DISABLE"
95
- :loading =" menuStatusUpdating[scope.row.id]"
96
- @change =" (val) => handleStatusChanged(scope.row, val as number)"
97
- />
98
- </template >
99
- </el-table-column >
100
- <el-table-column align =" center" label =" 操作" >
101
- <template #default =" scope " >
102
- <el-button
103
- v-hasPermi =" ['system:menu:update']"
104
- link
105
- type =" primary"
106
- @click =" openForm('update', scope.row.id)"
107
- >
108
- 修改
109
- </el-button >
110
- <el-button
111
- v-hasPermi =" ['system:menu:create']"
112
- link
113
- type =" primary"
114
- @click =" openForm('create', undefined, scope.row.id)"
115
- >
116
- 新增
117
- </el-button >
118
- <el-button
119
- v-hasPermi =" ['system:menu:delete']"
120
- link
121
- type =" danger"
122
- @click =" handleDelete(scope.row.id)"
123
- >
124
- 删除
125
- </el-button >
126
- </template >
127
- </el-table-column >
128
- </el-table >
85
+ <template #default =" { data } " >
86
+ <div
87
+ class =" custom-tree-node"
88
+ :class =" { 'menu-item': true }"
89
+ >
90
+ <div class =" node-content" >
91
+ <span class =" label" >{{ data.name }}</span >
92
+ <div v-if =" currentNode === data" class =" menu-info" >
93
+ <span class =" info-item" v-if =" data.icon" >
94
+ <span class =" info-label" >图标:</span >
95
+ <span class =" icon-preview" >
96
+ <Icon :icon =" data.icon" />
97
+ <span class =" icon-name" >{{ data.icon }}</span >
98
+ </span >
99
+ </span >
100
+ <span class =" info-item" >
101
+ <span class =" info-label" >排序:</span >
102
+ <span class =" info-value" >{{ data.sort }}</span >
103
+ </span >
104
+ <span class =" info-item" v-if =" data.permission" >
105
+ <span class =" info-label" >权限标识:</span >
106
+ <span class =" info-value" >{{ data.permission }}</span >
107
+ </span >
108
+ <span class =" info-item" v-if =" data.path" >
109
+ <span class =" info-label" >路由地址:</span >
110
+ <span class =" info-value" >{{ data.path }}</span >
111
+ </span >
112
+ <span class =" info-item" v-if =" data.component" >
113
+ <span class =" info-label" >组件路径:</span >
114
+ <span class =" info-value" >{{ data.component }}</span >
115
+ </span >
116
+ <span class =" info-item" v-if =" data.componentName" >
117
+ <span class =" info-label" >组件名称:</span >
118
+ <span class =" info-value" >{{ data.componentName }}</span >
119
+ </span >
120
+ </div >
121
+ </div >
122
+ <div v-show =" currentNode === data" class =" operations" >
123
+ <el-button
124
+ v-hasPermi =" ['system:menu:update']"
125
+ link
126
+ type =" primary"
127
+ @click.stop =" openForm('update', data.id)"
128
+ >
129
+ 修改
130
+ </el-button >
131
+ <el-button
132
+ v-hasPermi =" ['system:menu:create']"
133
+ link
134
+ type =" primary"
135
+ @click.stop =" openForm('create', undefined, data.id)"
136
+ >
137
+ 新增
138
+ </el-button >
139
+ <el-button
140
+ v-hasPermi =" ['system:menu:delete']"
141
+ link
142
+ type =" danger"
143
+ @click.stop =" handleDelete(data.id)"
144
+ >
145
+ 删除
146
+ </el-button >
147
+ </div >
148
+ </div >
149
+ </template >
150
+ </el-tree-v2 >
129
151
</ContentWrap >
130
152
131
153
<!-- 表单弹窗:添加/修改 -->
@@ -155,13 +177,26 @@ const queryParams = reactive({
155
177
const queryFormRef = ref () // 搜索的表单
156
178
const isExpandAll = ref (false ) // 是否展开,默认全部折叠
157
179
const refreshTable = ref (true ) // 重新渲染表格状态
180
+ const currentNode = ref <any >(null ) // 当前选中节点
158
181
159
182
/** 查询列表 */
160
183
const getList = async () => {
161
184
loading .value = true
162
185
try {
163
186
const data = await MenuApi .getMenuList (queryParams )
164
- list .value = handleTree (data )
187
+ // 为每个节点添加 showInfo 属性和样式对象
188
+ const addProps = (items : any []) => {
189
+ items .forEach (item => {
190
+ item .showInfo = false
191
+ item .popupStyle = {}
192
+ if (item .children && item .children .length > 0 ) {
193
+ addProps (item .children )
194
+ }
195
+ })
196
+ }
197
+ const processedData = handleTree (data )
198
+ addProps (processedData )
199
+ list .value = processedData
165
200
} finally {
166
201
loading .value = false
167
202
}
@@ -233,8 +268,136 @@ const handleStatusChanged = async (menu: MenuVO, val: number) => {
233
268
}
234
269
}
235
270
271
+ const handleCurrentChange = (data : any ) => {
272
+ currentNode .value = data
273
+ // 关闭所有信息面板
274
+ list .value .forEach ((item : any ) => {
275
+ item .showInfo = false
276
+ })
277
+ }
278
+
279
+ // 添加点击外部关闭弹出层的处理
280
+ onMounted (() => {
281
+ document .addEventListener (' click' , (event : MouseEvent ) => {
282
+ const target = event .target as HTMLElement
283
+ if (! target .closest (' .menu-info-popup' ) && ! target .closest (' .info-button' )) {
284
+ list .value .forEach ((item : any ) => {
285
+ item .showInfo = false
286
+ })
287
+ }
288
+ })
289
+ })
290
+
236
291
/** 初始化 **/
237
292
onMounted (() => {
238
293
getList ()
239
294
})
240
295
</script >
296
+
297
+ <style lang="scss" scoped>
298
+ :deep(.el-tree-node.is-current > .el-tree-node__content ) {
299
+ background-color : var (--el-color-primary-light-7 ) !important ;
300
+
301
+ .custom-tree-node {
302
+ background-color : var (--el-color-primary-light-7 );
303
+
304
+ .operations {
305
+ background-color : var (--el-color-primary-light-7 );
306
+ }
307
+ }
308
+ }
309
+
310
+ .custom-tree-node {
311
+ flex : 1 ;
312
+ display : flex ;
313
+ align-items : center ;
314
+ justify-content : space-between ;
315
+ padding : 0 8px ;
316
+ height : 40px ;
317
+ position : relative ;
318
+ border-bottom : 1px solid var (--el-border-color-lighter );
319
+ min-width : 800px ;
320
+ transition : background-color 0.3s ;
321
+
322
+ .node-content {
323
+ display : flex ;
324
+ align-items : center ;
325
+ gap : 12px ;
326
+ height : 100% ;
327
+ flex : 1 ;
328
+ min-width : 0 ;
329
+
330
+ .label {
331
+ flex-shrink : 0 ;
332
+ }
333
+
334
+ .menu-info {
335
+ display : flex ;
336
+ align-items : center ;
337
+ gap : 16px ;
338
+ overflow-x : auto ;
339
+ flex : 1 ;
340
+ margin-right : 16px ;
341
+ padding : 0 4px ;
342
+
343
+ & ::-webkit-scrollbar {
344
+ height : 6px ;
345
+ }
346
+
347
+ & ::-webkit-scrollbar-thumb {
348
+ background : var (--el-border-color );
349
+ border-radius : 3px ;
350
+ }
351
+
352
+ & ::-webkit-scrollbar-track {
353
+ background : transparent ;
354
+ }
355
+ }
356
+
357
+ .info-item {
358
+ flex-shrink : 0 ;
359
+ display : flex ;
360
+ align-items : center ;
361
+ gap : 4px ;
362
+
363
+ .info-label {
364
+ color : var (--el-text-color-secondary );
365
+ font-size : 13px ;
366
+ }
367
+
368
+ .info-value {
369
+ color : var (--el-text-color-primary );
370
+ font-size : 13px ;
371
+ }
372
+
373
+ .icon-preview {
374
+ display : flex ;
375
+ align-items : center ;
376
+ gap : 4px ;
377
+ padding : 0 8px ;
378
+ height : 24px ;
379
+ border-radius : 4px ;
380
+ border : 1px solid var (--el-border-color-lighter );
381
+ background-color : var (--el-bg-color );
382
+
383
+ .icon-name {
384
+ font-size : 13px ;
385
+ color : var (--el-text-color-regular );
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ .operations {
392
+ display : flex ;
393
+ gap : 8px ;
394
+ height : 100% ;
395
+ align-items : center ;
396
+ flex-shrink : 0 ;
397
+ position : sticky ;
398
+ right : 8px ;
399
+ padding-left : 8px ;
400
+ transition : background-color 0.3s ;
401
+ }
402
+ }
403
+ </style >
0 commit comments