踩坑记28 el-table default-sort属性 sort() 排序 & toggleRowExpansion 折叠 展开 & el-icon 导入报错 组件名与html元素名重复

本文详细介绍了在Element-Plus库中使用el-table进行自动和手动排序的实践,包括default-sort属性的使用、手动排序函数sortData的实现,以及解决排序问题的策略。同时,针对表格的展开与折叠功能,展示了如何使用toggleRowExpansion方法实现一键展开/折叠。此外,还提到了el-icon组件导入时的命名冲突问题及其解决方案。

2021.9.23

坑86(el-table、default-sort、排序):el-table 表格的自动/手动排序。自动:el-table自带能力。手动:自写排序函数处理事件。

el-table自动排序参考文档:  Table 表格 | ElementPlus (element-plus.org)   排序Sorting部分。

在表格上设置 default-sort 属性即可自动排序,类型为对象。prop 排序属性,order 排序方式,类型都为字符串。

// descending 降序  升序(默认) 
// 经实验发现,非descending一律升序
:default-sort='{prop:"sortCode", order:"ascending"}'

!注意:如果部分数据无排序prop字段,则整体排序会混乱,甚至会多出重复数据。

另外在列上设置 sortable 属性为true时,对应列会展示上下箭头标签,点击可控制升序/降序/原序。

尝试隐藏排序字段列(失败):

1、v-show='false'无效,依然显示;

2、v-if='false'则直接不渲染,相当于没有该列数据,default-sort设置的排序将不会生效

参考:element el-table列 显示隐藏的问题 - 收破烂的小伙子 - 博客园 (cnblogs.com)

 element-ui表格组件table实现列的动态显示与隐藏 - 简书 (jianshu.com) v-if、v-for方案,隐藏列(没渲染)无法用于排序。

3、class-name方案:参考 请问一下elementui使用el-table怎么隐藏列 - SegmentFault 思否   使用class-name,其中table的table-layout属性添加与否未发现区别(scoped的style需要用到深度选择器)。在列上设置类名class-name,会传递给该列包括表头在内的每一个单元格,scoped的style需要用到深度选择器

class-name='notShow'

:deep(.notShow){
    display: none !important;
}

class-name方案存在问题:

1、若该列设置了宽度属性(在template里,style设置加important也没有效果),则该宽度会由其右侧一列延用,比如排序列设置较窄,width='60',则其右侧的名称列会变窄;

2、最右侧会留大片空白,因为各列宽度是基于排序列存在时的情况计算的。

考虑了其他table插件:Handsontable 收费,功能较全  Handsontable--在线Excel表格 - 简书 (jianshu.com)

手动排序:基于以上种种坑,最后还是写了一个排序函数 sortData(data,sign) 来实现排序。data为数据数组,sign为排序属性,此次默认采用Number类型的属性。在向el-table传数据前调用即可。

2021.10.26更新排序函数 sortData(data,sign),解决父项是单个则无法通过Array.sort排序多个子项的问题。采用的方法是使用Array.map确保访问到每个子项。

// el-table调用
<el-table :data='sortData(infoTable,"sortCode")' row-key="itemId" >
...
// 排序(2021.10.26更新),sign需为Number
export function sortData(data,sign){
    data=data.sort((a,b)=>{
        if( typeof a[sign] ==='number' && typeof b[sign] ==='number'){
            return a[sign]-b[sign]  // 升序排列,小到大
        }else if(typeof a[sign] !=='number' && typeof b[sign] !=='number'){
            return 0  // 不变
        }else if(typeof a[sign] !=='number'){
            return 1  // a在后
        }else{
            return -1  // b在后
        }
    }).map(item=>{
        // 排序子项
        if(item.children && item.children.length>0){
            item.children=sortData(item.children,sign)
        }
        return item
    })
    return data
}


/*
// 排序(原版,有bug),sign需为Number
export function sortData(data,sign){
    // 使用Array.sort存在问题:
    // 当外层只有一项时,排序函数不会被调用,也就不会排序内部的多个子项
    data.sort((a,b)=>{
        if(a.children && a.children.length>0){
            sortData(a.children,sign)
        }
        if(b.children && b.children.length>0){
            sortData(b.children,sign)
        }


        if( typeof a[sign] ==='number' && typeof b[sign] ==='number'){
            return a[sign]-b[sign]  // 升序排列,小到大
        }else if(typeof a[sign] !=='number' && typeof b[sign] !=='number'){
            return 0  // 不变
        }else if(typeof a[sign] !=='number'){
            return 1  // a在后
        }else{
            return -1  // b在后
        }
    })
    return data
}
*/

坑87(el-table、折叠、展开):一键展开/折叠el-table。通过调用el-table的 toggleRowExpansion(row,isExpand) 方法实现。row 为行数据,isExpand 为是否展开。具体代码如下:

<el-button @click='handletotalExpand()'>{{isExpand?'折叠':'展开'}}</el-button>
...
const state=reactive({
    isExpand:false,
})
// ref绑定到对应el-table
let infoTableRef=ref(null)

// 工具函数,实现el-table展开/折叠
// data为表格元素实例的数据数组,table为表格元素,isExpand是否展开
const expandTable=(data,table,isExpand)=>{
    data.map(item=>{
        table.toggleRowExpansion(item,isExpand)
        if(item.children && item.children.length>0){
            expandTable(item.children,table,isExpand)
        }
    })
}

// 处理展开/折叠
const handletotalExpand=()=>{
    state.isExpand=!state.isExpand
        // 调用工具函数
    expandTable(infoTableRef.value.data,infoTableRef.value,state.isExpand)
}

return {
    handletotalExpand,
    infoTableRef,
}

参考:elementUI中el-table树形与el-tree树形结构的一键折叠与展开_cabinx的博客-优快云博客

坑88(el-icon、导入报错):导入el-icon的switch图标时报错如下:

Do not use built-in or reserved HTML elements as component id: switch

// 不要使用内置或保留的HTML元素作为组件id

报错原因是switch图标导入组件名称设置为switch与html元素名重复了。更改导入名称即可,代码如下:

// 将switch图标的组件名称改为i-switch

// 原先设定的组件名称是switch,会报错

app.component('i-switch',Switch) 

参考:Vue报错'Do not use built-in or reserved HTML elements as component id:解决方法_S筱潇S四维Smile-优快云博客  

聊聊 Vue 组件命名那些事 - CNode技术社区 (cnodejs.org)

by 莫得感情踩坑机(限定)

&lt;template&gt; &lt;div class=&quot;app-container&quot;&gt; &lt;el-form :model=&quot;queryParams&quot; ref=&quot;queryForm&quot; size=&quot;small&quot; :inline=&quot;true&quot; v-show=&quot;showSearch&quot;&gt; &lt;el-form-item label=&quot;菜单&quot; prop=&quot;menuName&quot;&gt; &lt;el-input v-model=&quot;queryParams.menuName&quot; placeholder=&quot;请输入菜单&quot; clearable @keyup.enter.native=&quot;handleQuery&quot; /&gt; &lt;/el-form-item&gt; &lt;el-form-item label=&quot;状态&quot; prop=&quot;status&quot;&gt; &lt;el-select v-model=&quot;queryParams.status&quot; placeholder=&quot;菜单状态&quot; clearable&gt; &lt;el-option v-for=&quot;dict in dict.type.sys_normal_disable&quot; :key=&quot;dict.value&quot; :label=&quot;dict.label&quot; :value=&quot;dict.value&quot; /&gt; &lt;/el-select&gt; &lt;/el-form-item&gt; &lt;el-form-item&gt; &lt;el-button type=&quot;primary&quot; icon=&quot;el-icon-search&quot; size=&quot;mini&quot; @click=&quot;handleQuery&quot;&gt;搜索&lt;/el-button&gt; &lt;el-button icon=&quot;el-icon-refresh&quot; size=&quot;mini&quot; @click=&quot;resetQuery&quot;&gt;重置&lt;/el-button&gt; &lt;/el-form-item&gt; &lt;/el-form&gt; &lt;el-row :gutter=&quot;10&quot; class=&quot;mb8&quot;&gt; &lt;el-col :span=&quot;1.5&quot;&gt; &lt;el-button type=&quot;primary&quot; plain icon=&quot;el-icon-plus&quot; size=&quot;mini&quot; @click=&quot;handleAdd&quot; v-hasPermi=&quot;[&#39;system:menu:add&#39;]&quot; &gt;新增&lt;/el-button&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;1.5&quot;&gt; &lt;el-button type=&quot;info&quot; plain icon=&quot;el-icon-sort&quot; size=&quot;mini&quot; @click=&quot;toggleExpandAll&quot; &gt;展开/折叠&lt;/el-button&gt; &lt;/el-col&gt; &lt;right-toolbar :showSearch.sync=&quot;showSearch&quot; @queryTable=&quot;getList&quot;&gt;&lt;/right-toolbar&gt; &lt;/el-row&gt; &lt;el-table v-if=&quot;refreshTable&quot; v-loading=&quot;loading&quot; :data=&quot;menuList&quot; row-key=&quot;menuId&quot; :default-expand-all=&quot;isExpandAll&quot; :tree-props=&quot;{children: &#39;children&#39;, hasChildren: &#39;hasChildren&#39;}&quot; &gt; &lt;el-table-column prop=&quot;menuName&quot; label=&quot;菜单&quot; :show-overflow-tooltip=&quot;true&quot; width=&quot;160&quot;&gt;&lt;/el-table-column&gt; &lt;el-table-column prop=&quot;icon&quot; label=&quot;图标&quot; align=&quot;center&quot; width=&quot;100&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;svg-icon :icon-class=&quot;scope.row.icon&quot; /&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;el-table-column prop=&quot;orderNum&quot; label=&quot;排序&quot; width=&quot;60&quot;&gt;&lt;/el-table-column&gt; &lt;el-table-column prop=&quot;perms&quot; label=&quot;权限标识&quot; :show-overflow-tooltip=&quot;true&quot;&gt;&lt;/el-table-column&gt; &lt;el-table-column prop=&quot;component&quot; label=&quot;组件路径&quot; :show-overflow-tooltip=&quot;true&quot;&gt;&lt;/el-table-column&gt; &lt;el-table-column prop=&quot;status&quot; label=&quot;状态&quot; width=&quot;80&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;dict-tag :options=&quot;dict.type.sys_normal_disable&quot; :value=&quot;scope.row.status&quot;/&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;el-table-column label=&quot;创建时间&quot; align=&quot;center&quot; prop=&quot;createTime&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span&gt;{{ parseTime(scope.row.createTime) }}&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;el-table-column label=&quot;操作&quot; align=&quot;center&quot; class-name=&quot;small-padding fixed-width&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-edit&quot; @click=&quot;handleUpdate(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:edit&#39;]&quot; &gt;修改&lt;/el-button&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-plus&quot; @click=&quot;handleAdd(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:add&#39;]&quot; &gt;新增&lt;/el-button&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-delete&quot; @click=&quot;handleDelete(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:remove&#39;]&quot; &gt;删除&lt;/el-button&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;/el-table&gt; &lt;!-- 添加或修改菜单对话框 --&gt; &lt;el-dialog :title=&quot;title&quot; :visible.sync=&quot;open&quot; width=&quot;680px&quot; append-to-body&gt; &lt;el-form ref=&quot;form&quot; :model=&quot;form&quot; :rules=&quot;rules&quot; label-width=&quot;100px&quot;&gt; &lt;el-row&gt; &lt;el-col :span=&quot;24&quot;&gt; &lt;el-form-item label=&quot;上级菜单&quot; prop=&quot;parentId&quot;&gt; &lt;treeselect v-model=&quot;form.parentId&quot; :options=&quot;menuOptions&quot; :normalizer=&quot;normalizer&quot; :show-count=&quot;true&quot; placeholder=&quot;选择上级菜单&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;24&quot;&gt; &lt;el-form-item label=&quot;菜单类型&quot; prop=&quot;menuType&quot;&gt; &lt;el-radio-group v-model=&quot;form.menuType&quot;&gt; &lt;el-radio label=&quot;M&quot;&gt;目录&lt;/el-radio&gt; &lt;el-radio label=&quot;C&quot;&gt;菜单&lt;/el-radio&gt; &lt;el-radio label=&quot;F&quot;&gt;按钮&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType != &#39;F&#39;&quot;&gt; &lt;el-form-item label=&quot;菜单图标&quot; prop=&quot;icon&quot;&gt; &lt;el-popover placement=&quot;bottom-start&quot; width=&quot;460&quot; trigger=&quot;click&quot; @show=&quot;$refs[&#39;iconSelect&#39;].reset()&quot; &gt; &lt;IconSelect ref=&quot;iconSelect&quot; @selected=&quot;selected&quot; :active-icon=&quot;form.icon&quot; /&gt; &lt;el-input slot=&quot;reference&quot; v-model=&quot;form.icon&quot; placeholder=&quot;点击选择图标&quot; readonly&gt; &lt;svg-icon v-if=&quot;form.icon&quot; slot=&quot;prefix&quot; :icon-class=&quot;form.icon&quot; style=&quot;width: 25px;&quot; /&gt; &lt;i v-else slot=&quot;prefix&quot; class=&quot;el-icon-search el-input__icon&quot; /&gt; &lt;/el-input&gt; &lt;/el-popover&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;显示排序&quot; prop=&quot;orderNum&quot;&gt; &lt;el-input-number v-model=&quot;form.orderNum&quot; controls-position=&quot;right&quot; :min=&quot;0&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;菜单&quot; prop=&quot;menuName&quot;&gt; &lt;el-input v-model=&quot;form.menuName&quot; placeholder=&quot;请输入菜单&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType == &#39;C&#39;&quot;&gt; &lt;el-form-item prop=&quot;routeName&quot;&gt; &lt;el-input v-model=&quot;form.routeName&quot; placeholder=&quot;请输入路由&quot; /&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;默认不填则和路由地址相同:如地址为:`user`,则称为`User`(注意:为避免字的冲突,特殊情况下请自定义,保证唯一性)&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 路由&lt;/span&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType != &#39;F&#39;&quot;&gt; &lt;el-form-item prop=&quot;isFrame&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;选择是外链则路由地址需要以`http(s)://`开头&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 是否外链 &lt;/span&gt; &lt;el-radio-group v-model=&quot;form.isFrame&quot;&gt; &lt;el-radio label=&quot;0&quot;&gt;是&lt;/el-radio&gt; &lt;el-radio label=&quot;1&quot;&gt;否&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType != &#39;F&#39;&quot;&gt; &lt;el-form-item prop=&quot;path&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 路由地址 &lt;/span&gt; &lt;el-input v-model=&quot;form.path&quot; placeholder=&quot;请输入路由地址&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType == &#39;C&#39;&quot;&gt; &lt;el-form-item prop=&quot;component&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;访问的组件路径,如:`system/user/index`,默认在`views`目录下&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 组件路径 &lt;/span&gt; &lt;el-input v-model=&quot;form.component&quot; placeholder=&quot;请输入组件路径&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType != &#39;M&#39;&quot;&gt; &lt;el-form-item prop=&quot;perms&quot;&gt; &lt;el-input v-model=&quot;form.perms&quot; placeholder=&quot;请输入权限标识&quot; maxlength=&quot;100&quot; /&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi(&#39;system:user:list&#39;)`)&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 权限字符 &lt;/span&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType == &#39;C&#39;&quot;&gt; &lt;el-form-item prop=&quot;query&quot;&gt; &lt;el-input v-model=&quot;form.query&quot; placeholder=&quot;请输入路由参数&quot; maxlength=&quot;255&quot; /&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&#39;访问路由的默认传递参数,如:`{&quot;id&quot;: 1, &quot;name&quot;: &quot;ry&quot;}`&#39; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 路由参数 &lt;/span&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType == &#39;C&#39;&quot;&gt; &lt;el-form-item prop=&quot;isCache&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 是否缓存 &lt;/span&gt; &lt;el-radio-group v-model=&quot;form.isCache&quot;&gt; &lt;el-radio label=&quot;0&quot;&gt;缓存&lt;/el-radio&gt; &lt;el-radio label=&quot;1&quot;&gt;不缓存&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType != &#39;F&#39;&quot;&gt; &lt;el-form-item prop=&quot;visible&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;选择隐藏则路由将不会出现在侧边栏,但仍然可以访问&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 显示状态 &lt;/span&gt; &lt;el-radio-group v-model=&quot;form.visible&quot;&gt; &lt;el-radio v-for=&quot;dict in dict.type.sys_show_hide&quot; :key=&quot;dict.value&quot; :label=&quot;dict.value&quot; &gt;{{dict.label}}&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item prop=&quot;status&quot;&gt; &lt;span slot=&quot;label&quot;&gt; &lt;el-tooltip content=&quot;选择停用则路由将不会出现在侧边栏,也不能被访问&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; 菜单状态 &lt;/span&gt; &lt;el-radio-group v-model=&quot;form.status&quot;&gt; &lt;el-radio v-for=&quot;dict in dict.type.sys_normal_disable&quot; :key=&quot;dict.value&quot; :label=&quot;dict.value&quot; &gt;{{dict.label}}&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;/el-form&gt; &lt;div slot=&quot;footer&quot; class=&quot;dialog-footer&quot;&gt; &lt;el-button type=&quot;primary&quot; @click=&quot;submitForm&quot;&gt;确 定&lt;/el-button&gt; &lt;el-button @click=&quot;cancel&quot;&gt;取 消&lt;/el-button&gt; &lt;/div&gt; &lt;/el-dialog&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { listMenu, getMenu, delMenu, addMenu, updateMenu } from &quot;@/api/system/menu&quot; import Treeselect from &quot;@riophae/vue-treeselect&quot; import &quot;@riophae/vue-treeselect/dist/vue-treeselect.css&quot; import IconSelect from &quot;@/components/IconSelect&quot; export default { name: &quot;Menu&quot;, dicts: [&#39;sys_show_hide&#39;, &#39;sys_normal_disable&#39;], components: { Treeselect, IconSelect }, data() { return { // 遮罩层 loading: true, // 显示搜索条件 showSearch: true, // 菜单表格树数据 menuList: [], // 菜单树选项 menuOptions: [], // 弹出层标题 title: &quot;&quot;, // 是否显示弹出层 open: false, // 是否展开,默认全部折叠 isExpandAll: false, // 重新渲染表格状态 refreshTable: true, // 查询参数 queryParams: { menuName: undefined, visible: undefined }, // 表单参数 form: {}, // 表单校验 rules: { menuName: [ { required: true, message: &quot;菜单称不能为空&quot;, trigger: &quot;blur&quot; } ], orderNum: [ { required: true, message: &quot;菜单顺序不能为空&quot;, trigger: &quot;blur&quot; } ], path: [ { required: true, message: &quot;路由地址不能为空&quot;, trigger: &quot;blur&quot; } ] } } }, created() { this.getList() }, methods: { // 选择图标 selected(name) { this.form.icon = name }, /** 查询菜单列表 */ getList() { this.loading = true listMenu(this.queryParams).then(response =&gt; { this.menuList = this.handleTree(response.data, &quot;menuId&quot;) this.loading = false }) }, /** 转换菜单数据结构 */ normalizer(node) { if (node.children &amp;&amp; !node.children.length) { delete node.children } return { id: node.menuId, label: node.menuName, children: node.children } }, /** 查询菜单下拉树结构 */ getTreeselect() { listMenu().then(response =&gt; { this.menuOptions = [] const menu = { menuId: 0, menuName: &#39;主类目&#39;, children: [] } menu.children = this.handleTree(response.data, &quot;menuId&quot;) this.menuOptions.push(menu) }) }, // 取消按钮 cancel() { this.open = false this.reset() }, // 表单重置 reset() { this.form = { menuId: undefined, parentId: 0, menuName: undefined, icon: undefined, menuType: &quot;M&quot;, orderNum: undefined, isFrame: &quot;1&quot;, isCache: &quot;0&quot;, visible: &quot;0&quot;, status: &quot;0&quot; } this.resetForm(&quot;form&quot;) }, /** 搜索按钮操作 */ handleQuery() { this.getList() }, /** 重置按钮操作 */ resetQuery() { this.resetForm(&quot;queryForm&quot;) this.handleQuery() }, /** 新增按钮操作 */ handleAdd(row) { this.reset() this.getTreeselect() if (row != null &amp;&amp; row.menuId) { this.form.parentId = row.menuId } else { this.form.parentId = 0 } this.open = true this.title = &quot;添加菜单&quot; }, /** 展开/折叠操作 */ toggleExpandAll() { this.refreshTable = false this.isExpandAll = !this.isExpandAll this.$nextTick(() =&gt; { this.refreshTable = true }) }, /** 修改按钮操作 */ handleUpdate(row) { this.reset() this.getTreeselect() getMenu(row.menuId).then(response =&gt; { this.form = response.data this.open = true this.title = &quot;修改菜单&quot; }) }, /** 提交按钮 */ submitForm: function() { this.$refs[&quot;form&quot;].validate(valid =&gt; { if (valid) { if (this.form.menuId != undefined) { updateMenu(this.form).then(response =&gt; { this.$modal.msgSuccess(&quot;修改成功&quot;) this.open = false this.getList() }) } else { addMenu(this.form).then(response =&gt; { this.$modal.msgSuccess(&quot;新增成功&quot;) this.open = false this.getList() }) } } }) }, /** 删除按钮操作 */ handleDelete(row) { this.$modal.confirm(&#39;是否确认删除称为&quot;&#39; + row.menuName + &#39;&quot;的数据项?&#39;).then(function() { return delMenu(row.menuId) }).then(() =&gt; { this.getList() this.$modal.msgSuccess(&quot;删除成功&quot;) }).catch(() =&gt; {}) } } } &lt;/script&gt; 美化
最新发布
09-24
你提供的是一段 **Vue 菜单管理页面(Menu Management)** 的代码,功能完整但 UI 较为原始。我们将对其进行 **全面美化升级**,目标是: - 提升整体视觉层次感专业度 - 优化树形表格、表单、弹窗的样式 - 增强图标选择器交互体验 - 统一配色、圆角、阴影风格 - 支持响应式布局和现代动效 --- 以下是 **完整美化后的 Vue 菜单管理页面代码**,包含 `&lt;template&gt;`、`&lt;script&gt;` 和 **全新设计的 `&lt;style scoped&gt;`**。 ```vue &lt;template&gt; &lt;div class=&quot;app-container menu-container&quot;&gt; &lt;!-- 查询表单 --&gt; &lt;el-form :model=&quot;queryParams&quot; ref=&quot;queryForm&quot; size=&quot;small&quot; :inline=&quot;true&quot; v-show=&quot;showSearch&quot; class=&quot;search-form&quot;&gt; &lt;el-form-item label=&quot;菜单&quot; prop=&quot;menuName&quot;&gt; &lt;el-input v-model=&quot;queryParams.menuName&quot; placeholder=&quot;请输入菜单&quot; clearable prefix-icon=&quot;el-icon-search&quot; style=&quot;width: 240px&quot; @keyup.enter.native=&quot;handleQuery&quot; /&gt; &lt;/el-form-item&gt; &lt;el-form-item label=&quot;状态&quot; prop=&quot;status&quot;&gt; &lt;el-select v-model=&quot;queryParams.status&quot; placeholder=&quot;菜单状态&quot; clearable style=&quot;width: 180px&quot; &gt; &lt;el-option v-for=&quot;dict in dict.type.sys_normal_disable&quot; :key=&quot;dict.value&quot; :label=&quot;dict.label&quot; :value=&quot;dict.value&quot; /&gt; &lt;/el-select&gt; &lt;/el-form-item&gt; &lt;el-form-item&gt; &lt;el-button type=&quot;primary&quot; icon=&quot;el-icon-search&quot; size=&quot;mini&quot; @click=&quot;handleQuery&quot;&gt;搜索&lt;/el-button&gt; &lt;el-button icon=&quot;el-icon-refresh&quot; size=&quot;mini&quot; @click=&quot;resetQuery&quot;&gt;重置&lt;/el-button&gt; &lt;/el-form-item&gt; &lt;/el-form&gt; &lt;!-- 操作按钮区 --&gt; &lt;el-row :gutter=&quot;10&quot; class=&quot;mb8 action-bar&quot;&gt; &lt;el-col :span=&quot;1.5&quot;&gt; &lt;el-button type=&quot;primary&quot; plain icon=&quot;el-icon-plus&quot; size=&quot;mini&quot; @click=&quot;handleAdd&quot; v-hasPermi=&quot;[&#39;system:menu:add&#39;]&quot; &gt;新增&lt;/el-button&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;1.5&quot;&gt; &lt;el-button type=&quot;info&quot; plain icon=&quot;el-icon-sort&quot; size=&quot;mini&quot; @click=&quot;toggleExpandAll&quot; &gt;{{ isExpandAll ? &#39;折叠全部&#39; : &#39;展开全部&#39; }}&lt;/el-button&gt; &lt;/el-col&gt; &lt;right-toolbar :showSearch.sync=&quot;showSearch&quot; @queryTable=&quot;getList&quot;&gt;&lt;/right-toolbar&gt; &lt;/el-row&gt; &lt;!-- 菜单树表格 --&gt; &lt;el-table v-if=&quot;refreshTable&quot; v-loading=&quot;loading&quot; :data=&quot;menuList&quot; row-key=&quot;menuId&quot; :default-expand-all=&quot;isExpandAll&quot; :tree-props=&quot;{ children: &#39;children&#39;, hasChildren: &#39;hasChildren&#39; }&quot; class=&quot;menu-table&quot; highlight-current-row &gt; &lt;!-- 菜单--&gt; &lt;el-table-column prop=&quot;menuName&quot; label=&quot;菜单&quot; :show-overflow-tooltip=&quot;true&quot; min-width=&quot;160&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span class=&quot;menu-name&quot;&gt;{{ scope.row.menuName }}&lt;/span&gt; &lt;el-tag size=&quot;mini&quot; :type=&quot;menuTypeTag[scope.row.menuType]&quot; class=&quot;ml-2&quot;&gt; {{ menuTypeName[scope.row.menuType] }} &lt;/el-tag&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 图标 --&gt; &lt;el-table-column prop=&quot;icon&quot; label=&quot;图标&quot; align=&quot;center&quot; width=&quot;100&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;svg-icon :icon-class=&quot;scope.row.icon&quot; v-if=&quot;scope.row.icon&quot; class=&quot;icon-preview&quot; /&gt; &lt;span v-else class=&quot;text-muted&quot;&gt;&mdash;&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 排序 --&gt; &lt;el-table-column prop=&quot;orderNum&quot; label=&quot;排序&quot; width=&quot;80&quot; align=&quot;center&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span class=&quot;text-primary font-weight-medium&quot;&gt;{{ scope.row.orderNum }}&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 权限标识 --&gt; &lt;el-table-column prop=&quot;perms&quot; label=&quot;权限标识&quot; :show-overflow-tooltip=&quot;true&quot; min-width=&quot;140&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span v-if=&quot;scope.row.perms&quot; class=&quot;code-tag&quot;&gt;{{ scope.row.perms }}&lt;/span&gt; &lt;span v-else class=&quot;text-muted&quot;&gt;&mdash;&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 组件路径 --&gt; &lt;el-table-column prop=&quot;component&quot; label=&quot;组件路径&quot; :show-overflow-tooltip=&quot;true&quot; min-width=&quot;160&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span v-if=&quot;scope.row.component&quot; class=&quot;text-code&quot;&gt;{{ scope.row.component }}&lt;/span&gt; &lt;span v-else class=&quot;text-muted&quot;&gt;&mdash;&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 状态 --&gt; &lt;el-table-column prop=&quot;status&quot; label=&quot;状态&quot; width=&quot;90&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;dict-tag :options=&quot;dict.type.sys_normal_disable&quot; :value=&quot;scope.row.status&quot; /&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 创建时间 --&gt; &lt;el-table-column prop=&quot;createTime&quot; label=&quot;创建时间&quot; width=&quot;170&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;span class=&quot;text-secondary small&quot;&gt;{{ parseTime(scope.row.createTime) }}&lt;/span&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 操作 --&gt; &lt;el-table-column label=&quot;操作&quot; align=&quot;center&quot; width=&quot;180&quot; class-name=&quot;action-column&quot;&gt; &lt;template slot-scope=&quot;scope&quot;&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-edit&quot; @click=&quot;handleUpdate(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:edit&#39;]&quot; &gt;修改&lt;/el-button&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-plus&quot; @click=&quot;handleAdd(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:add&#39;]&quot; &gt;新增&lt;/el-button&gt; &lt;el-button size=&quot;mini&quot; type=&quot;text&quot; icon=&quot;el-icon-delete&quot; @click=&quot;handleDelete(scope.row)&quot; v-hasPermi=&quot;[&#39;system:menu:remove&#39;]&quot; &gt;删除&lt;/el-button&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;/el-table&gt; &lt;!-- 分页占位 --&gt; &lt;div v-if=&quot;false&quot; class=&quot;pagination-placeholder&quot;&gt;&lt;/div&gt; &lt;!-- 添加或修改菜单对话框 --&gt; &lt;el-dialog :title=&quot;title&quot; :visible.sync=&quot;open&quot; width=&quot;720px&quot; append-to-body center&gt; &lt;el-form ref=&quot;form&quot; :model=&quot;form&quot; :rules=&quot;rules&quot; label-width=&quot;100px&quot; class=&quot;dialog-form&quot;&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;上级菜单&quot; prop=&quot;parentId&quot;&gt; &lt;treeselect v-model=&quot;form.parentId&quot; :options=&quot;menuOptions&quot; :normalizer=&quot;normalizer&quot; :show-count=&quot;true&quot; placeholder=&quot;选择上级菜单&quot; no-options-text=&quot;暂无菜单数据&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;菜单类型&quot; prop=&quot;menuType&quot;&gt; &lt;el-radio-group v-model=&quot;form.menuType&quot;&gt; &lt;el-radio label=&quot;M&quot;&gt;目录&lt;/el-radio&gt; &lt;el-radio label=&quot;C&quot;&gt;菜单&lt;/el-radio&gt; &lt;el-radio label=&quot;F&quot;&gt;按钮&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType !== &#39;F&#39;&quot;&gt; &lt;el-form-item label=&quot;菜单图标&quot; prop=&quot;icon&quot;&gt; &lt;el-popover placement=&quot;bottom-start&quot; width=&quot;460&quot; trigger=&quot;click&quot; @show=&quot;$refs.iconSelect.reset()&quot; &gt; &lt;IconSelect ref=&quot;iconSelect&quot; @selected=&quot;selected&quot; :active-icon=&quot;form.icon&quot; /&gt; &lt;el-input slot=&quot;reference&quot; v-model=&quot;form.icon&quot; placeholder=&quot;点击选择图标&quot; readonly class=&quot;icon-selector-input&quot; &gt; &lt;svg-icon v-if=&quot;form.icon&quot; slot=&quot;prefix&quot; :icon-class=&quot;form.icon&quot; class=&quot;icon-prefix&quot; /&gt; &lt;i v-else slot=&quot;prefix&quot; class=&quot;el-icon-search el-input__icon&quot; /&gt; &lt;/el-input&gt; &lt;/el-popover&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;显示排序&quot; prop=&quot;orderNum&quot;&gt; &lt;el-input-number v-model=&quot;form.orderNum&quot; controls-position=&quot;right&quot; :min=&quot;0&quot; :max=&quot;999&quot; class=&quot;w-100&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;菜单&quot; prop=&quot;menuName&quot;&gt; &lt;el-input v-model=&quot;form.menuName&quot; placeholder=&quot;请输入菜单&quot; maxlength=&quot;30&quot; show-word-limit /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType === &#39;C&#39;&quot;&gt; &lt;el-form-item label=&quot;路由&quot; prop=&quot;routeName&quot;&gt; &lt;el-tooltip content=&quot;默认不填则和路由地址相同:如地址为 user,则称为 User&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-input v-model=&quot;form.routeName&quot; placeholder=&quot;请输入路由&quot; maxlength=&quot;50&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType !== &#39;F&#39;&quot;&gt; &lt;el-form-item label=&quot;是否外链&quot;&gt; &lt;el-tooltip content=&quot;选择是外链则路由地址需以 http(s):// 开头&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-radio-group v-model=&quot;form.isFrame&quot;&gt; &lt;el-radio label=&quot;0&quot;&gt;是&lt;/el-radio&gt; &lt;el-radio label=&quot;1&quot;&gt;否&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType !== &#39;F&#39;&quot;&gt; &lt;el-form-item label=&quot;路由地址&quot; prop=&quot;path&quot;&gt; &lt;el-tooltip content=&quot;访问的路由地址,如:user,外链需以 http(s):// 开头&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-input v-model=&quot;form.path&quot; placeholder=&quot;请输入路由地址&quot; maxlength=&quot;200&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType === &#39;C&#39;&quot;&gt; &lt;el-form-item label=&quot;组件路径&quot; prop=&quot;component&quot;&gt; &lt;el-tooltip content=&quot;组件路径,如:system/user/index,默认在 views 目录下&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-input v-model=&quot;form.component&quot; placeholder=&quot;请输入组件路径&quot; maxlength=&quot;255&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType !== &#39;M&#39;&quot;&gt; &lt;el-form-item label=&quot;权限字符&quot; prop=&quot;perms&quot;&gt; &lt;el-tooltip content=&quot;如:@PreAuthorize(`@ss.hasPermi(&#39;system:user:list&#39;)`)&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-input v-model=&quot;form.perms&quot; placeholder=&quot;请输入权限标识&quot; maxlength=&quot;100&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType === &#39;C&#39;&quot;&gt; &lt;el-form-item label=&quot;路由参数&quot;&gt; &lt;el-tooltip content=&#39;默认传递参数,如:{&quot;id&quot;: 1, &quot;name&quot;: &quot;ry&quot;}&#39; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-input v-model=&quot;form.query&quot; placeholder=&quot;请输入 JSON 参数&quot; maxlength=&quot;255&quot; /&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType === &#39;C&#39;&quot;&gt; &lt;el-form-item label=&quot;是否缓存&quot;&gt; &lt;el-tooltip content=&quot;选择缓存则会被 keep-alive 缓存,需匹配组件 name&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-radio-group v-model=&quot;form.isCache&quot;&gt; &lt;el-radio label=&quot;0&quot;&gt;缓存&lt;/el-radio&gt; &lt;el-radio label=&quot;1&quot;&gt;不缓存&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;el-row :gutter=&quot;20&quot;&gt; &lt;el-col :span=&quot;12&quot; v-if=&quot;form.menuType !== &#39;F&#39;&quot;&gt; &lt;el-form-item label=&quot;显示状态&quot;&gt; &lt;el-tooltip content=&quot;隐藏则不会出现在侧边栏,但仍可访问&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-radio-group v-model=&quot;form.visible&quot;&gt; &lt;el-radio v-for=&quot;dict in dict.type.sys_show_hide&quot; :key=&quot;dict.value&quot; :label=&quot;dict.value&quot; &gt;{{ dict.label }}&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;el-col :span=&quot;12&quot;&gt; &lt;el-form-item label=&quot;菜单状态&quot; prop=&quot;status&quot;&gt; &lt;el-tooltip content=&quot;停用则无法访问且不显示&quot; placement=&quot;top&quot;&gt; &lt;i class=&quot;el-icon-question text-primary small ml-1&quot;&gt;&lt;/i&gt; &lt;/el-tooltip&gt; &lt;el-radio-group v-model=&quot;form.status&quot;&gt; &lt;el-radio v-for=&quot;dict in dict.type.sys_normal_disable&quot; :key=&quot;dict.value&quot; :label=&quot;dict.value&quot; &gt;{{ dict.label }}&lt;/el-radio&gt; &lt;/el-radio-group&gt; &lt;/el-form-item&gt; &lt;/el-col&gt; &lt;/el-row&gt; &lt;/el-form&gt; &lt;div slot=&quot;footer&quot; class=&quot;dialog-footer&quot;&gt; &lt;el-button type=&quot;primary&quot; @click=&quot;submitForm&quot;&gt;确 定&lt;/el-button&gt; &lt;el-button @click=&quot;cancel&quot;&gt;取 消&lt;/el-button&gt; &lt;/div&gt; &lt;/el-dialog&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import { listMenu, getMenu, delMenu, addMenu, updateMenu } from &quot;@/api/system/menu&quot; import Treeselect from &quot;@riophae/vue-treeselect&quot; import &quot;@riophae/vue-treeselect/dist/vue-treeselect.css&quot; import IconSelect from &quot;@/components/IconSelect&quot; export default { name: &quot;Menu&quot;, dicts: [&#39;sys_show_hide&#39;, &#39;sys_normal_disable&#39;], components: { Treeselect, IconSelect }, data() { return { loading: true, showSearch: true, menuList: [], menuOptions: [], title: &quot;&quot;, open: false, isExpandAll: false, refreshTable: true, queryParams: { menuName: undefined, visible: undefined, status: undefined }, form: {}, rules: { menuName: [{ required: true, message: &quot;菜单称不能为空&quot;, trigger: &quot;blur&quot; }], orderNum: [{ required: true, message: &quot;排序不能为空&quot;, trigger: &quot;blur&quot; }], path: [{ required: true, message: &quot;路由地址不能为空&quot;, trigger: &quot;blur&quot; }] }, // 菜单类型映射 menuTypeTag: { M: &#39;warning&#39;, C: &#39;success&#39;, F: &#39;info&#39; }, menuTypeName: { M: &#39;目录&#39;, C: &#39;菜单&#39;, F: &#39;按钮&#39; } } }, created() { this.getList() }, methods: { selected(name) { this.form.icon = name }, getList() { this.loading = true listMenu(this.queryParams).then(response =&gt; { this.menuList = this.handleTree(response.data, &quot;menuId&quot;) this.loading = false }) }, normalizer(node) { if (node.children &amp;&amp; !node.children.length) delete node.children return { id: node.menuId, label: node.menuName, children: node.children } }, getTreeselect() { listMenu().then(response =&gt; { this.menuOptions = [] const menu = { menuId: 0, menuName: &#39;主类目&#39;, children: [] } menu.children = this.handleTree(response.data, &quot;menuId&quot;) this.menuOptions.push(menu) }) }, cancel() { this.open = false this.reset() }, reset() { this.form = { menuId: undefined, parentId: 0, menuName: undefined, icon: undefined, menuType: &quot;M&quot;, orderNum: 0, isFrame: &quot;1&quot;, isCache: &quot;0&quot;, visible: &quot;0&quot;, status: &quot;0&quot;, perms: undefined, path: undefined, component: undefined, query: undefined, routeName: undefined } this.resetForm(&quot;form&quot;) }, handleQuery() { this.getList() }, resetQuery() { this.resetForm(&quot;queryForm&quot;) this.handleQuery() }, handleAdd(row) { this.reset() this.getTreeselect() this.form.parentId = row?.menuId || 0 this.open = true this.title = &quot;添加菜单&quot; }, toggleExpandAll() { this.isExpandAll = !this.isExpandAll this.refreshTable = false this.$nextTick(() =&gt; { this.refreshTable = true }) }, handleUpdate(row) { this.reset() this.getTreeselect() getMenu(row.menuId).then(response =&gt; { this.form = response.data this.open = true this.title = &quot;修改菜单&quot; }) }, submitForm() { this.$refs.form.validate(valid =&gt; { if (valid) { const req = this.form.menuId ? updateMenu : addMenu req(this.form).then(() =&gt; { this.$modal.msgSuccess(this.form.menuId ? &quot;修改成功&quot; : &quot;新增成功&quot;) this.open = false this.getList() }) } }) }, handleDelete(row) { this.$modal.confirm(`是否确认删除称为&quot;${row.menuName}&quot;的数据项?`).then(() =&gt; { return delMenu(row.menuId) }).then(() =&gt; { this.getList() this.$modal.msgSuccess(&quot;删除成功&quot;) }).catch(() =&gt; {}) } } } &lt;/script&gt; &lt;style scoped lang=&quot;scss&quot;&gt; $primary: #409eff; $success: #67c23a; $info: #909399; $warning: #e6a23c; $danger: #f56c6c; $text-primary: #303133; $text-secondary: #606266; $bg-color: #f5f7fa; $border-radius-md: 8px; $border-radius-lg: 12px; $box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); $transition: all 0.3s ease; .menu-container { padding: 24px; background-color: $bg-color; min-height: 100vh; font-family: &#39;Segoe UI&#39;, -apple-system, BlinkMacSystemFont, sans-serif; } // 查询表单 .search-form { background: #fff; padding: 18px 16px; border-radius: $border-radius-md; box-shadow: $box-shadow; margin-bottom: 16px; ::v-deep .el-form-item { margin-bottom: 0; margin-right: 12px; .el-input__inner, .el-select .el-input__inner { border-radius: $border-radius-md; border: 1px solid #ebeef5; transition: $transition; &amp;:focus { border-color: $primary; box-shadow: 0 0 8px rgba($primary, 0.1); } } .el-input--prefix .el-input__inner { padding-left: 35px; } } } // 操作栏 .action-bar { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; .el-button { border-radius: $border-radius-md; font-size: 13px; padding: 7px 12px; } } // 表格美化 .menu-table { ::v-deep th { background-color: #fafbfc; font-weight: 600; color: $text-primary; padding: 12px 0; } ::v-deep td { padding: 10px 0; color: $text-secondary; font-size: 14px; } .menu-name { font-weight: 500; color: $text-primary; } .icon-preview { font-size: 18px; color: $primary; } .text-muted { color: #999; font-size: 13px; } .code-tag { font-family: Consolas, Monaco, monospace; font-size: 12px; background-color: #f0f2f5; border: none; padding: 2px 6px; border-radius: 4px; } .text-code { font-family: Consolas, Monaco, monospace; font-size: 13px; color: #d9480f; } .small { font-size: 12px; } .action-column { .el-button { margin: 0 4px; padding: 0; } } } // 弹窗美化 ::v-deep .el-dialog { border-radius: $border-radius-lg; .el-dialog__header { background-color: #f2f6fc; padding: 20px 24px; border-bottom: 1px solid #ebeef5; font-weight: 600; color: $text-primary; } .el-dialog__body { padding: 24px 20px; } .dialog-footer { text-align: center; padding: 16px 0 24px; } } // 图标选择器输入框 .icon-selector-input { ::v-deep .el-input__inner { padding-left: 35px !important; } ::v-deep .icon-prefix { width: 16px !important; height: 16px; margin: 6px 8px 0 8px; color: $primary; } } // 树选择器 ::v-deep .vue-treeselect { &amp;__control { border-radius: $border-radius-md; } &amp;__placeholder { color: #999; } } // 响应式适配 @media (max-width: 768px) { .menu-container { padding: 16px; } .search-form { flex-direction: column; } .action-bar { justify-content: flex-start; } ::v-deep .el-dialog { width: 90% !important; margin-top: 40px !important; } } &lt;/style&gt; ``` --- ### ✅ 美化亮点总结 | 模块 | 优化点 | |------|--------| | **整体布局** | 浅灰背景 + 白色卡片 + 圆角 + 投影,提升现代感 | | **查询表单** | 卡片包裹 + 聚焦动效 + 统一输入框样式 | | **表格** | 加粗标题、类型标签化(目录/菜单/按钮)、权限字段代码样式 | | **图标预览** | 实时显示 SVG 图标,增强可视化 | | **弹窗** | 居中对齐、圆角加大、提示图标统一右上角 | | **表单项分组** | 使用 `el-row` + `gutter` 更好地组织复杂表单 | | **响应式** | 移动端自动调整布局宽度 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值