踩坑记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
&lt;template&gt; &lt;ContentWrap&gt; &lt;!-- 搜索工作栏保持不变 --&gt; &lt;el-form class=&quot;-mb-15px&quot; :model=&quot;queryParams&quot; ref=&quot;queryFormRef&quot; :inline=&quot;true&quot; label-width=&quot;68px&quot; &gt; &lt;!-- 搜索栏内容保持不变 --&gt; &lt;el-form-item label=&quot;&quot; prop=&quot;TrgetYear&quot;&gt; &lt;el-date-picker v-model=&quot;queryParams.TrgetYear&quot; type=&quot;year&quot; format=&quot;YYYY&quot; value-format=&quot;YYYY&quot; placeholder=&quot;选择年份&quot; /&gt; &lt;/el-form-item&gt; &lt;el-form-item prop=&quot;TrgetMetering&quot;&gt; &lt;el-select v-model=&quot;queryParams.TrgetMetering&quot; class=&quot;!w-200px&quot;&gt; &lt;el-option label=&quot;水&quot; value=&quot;1&quot; /&gt; &lt;el-option label=&quot;电&quot; value=&quot;2&quot; /&gt; &lt;el-option label=&quot;蒸汽&quot; value=&quot;3&quot; /&gt; &lt;el-option label=&quot;综合能耗&quot; value=&quot;4&quot; /&gt; &lt;/el-select&gt; &lt;/el-form-item&gt; &lt;el-form-item&gt; &lt;el-button @click=&quot;handleQuery&quot;&gt;&lt;Icon icon=&quot;ep:search&quot; class=&quot;mr-5px&quot; /&gt; 搜索&lt;/el-button&gt; &lt;el-button @click=&quot;resetQuery&quot;&gt;&lt;Icon icon=&quot;ep:refresh&quot; class=&quot;mr-5px&quot; /&gt; 重置&lt;/el-button&gt; &lt;el-button type=&quot;primary&quot; plain @click=&quot;handleBatchInsert&quot; v-hasPermi=&quot;[&#39;energy:data:trgetsetting:create&#39;]&quot; :loading=&quot;batchInsertLoading&quot; &gt; &lt;Icon icon=&quot;ep:plus&quot; class=&quot;mr-5px&quot; /&gt; 一键生成 &lt;/el-button&gt; &lt;el-button type=&quot;danger&quot; plain @click=&quot;toggleExpandAll&quot;&gt; &lt;Icon icon=&quot;ep:sort&quot; class=&quot;mr-5px&quot; /&gt; 展开/折叠 &lt;/el-button&gt; &lt;el-button type=&quot;success&quot; plain @click=&quot;handleExport&quot; :loading=&quot;exportLoading&quot; v-hasPermi=&quot;[&#39;energy:data:trgetsetting:export&#39;]&quot; &gt; &lt;Icon icon=&quot;ep:download&quot; class=&quot;mr-5px&quot; /&gt; 导出 &lt;/el-button&gt; &lt;el-button type=&quot;danger&quot; plain :disabled=&quot;isEmpty(checkedIds)&quot; @click=&quot;handleDeleteBatch&quot; v-hasPermi=&quot;[&#39;energy:data:trgetsetting:delete&#39;]&quot; &gt; &lt;Icon icon=&quot;ep:delete&quot; class=&quot;mr-5px&quot; /&gt; 批量删除 &lt;/el-button&gt; &lt;/el-form-item&gt; &lt;/el-form&gt; &lt;/ContentWrap&gt; &lt;!-- 列表 --&gt; &lt;ContentWrap&gt; &lt;el-table row-key=&quot;id&quot; v-loading=&quot;loading&quot; :data=&quot;treeList&quot; :stripe=&quot;true&quot; :show-overflow-tooltip=&quot;true&quot; :default-expand-all=&quot;isExpandAll&quot; @selection-change=&quot;handleRowCheckboxChange&quot; :tree-props=&quot;{ children: &#39;children&#39;, hasChildren: &#39;hasChildren&#39; }&quot; ref=&quot;tableRef&quot; &gt; &lt;!-- 移除了v-if=&quot;refreshTable&quot;以避免表格重新渲染导致的问题 --&gt; &lt;el-table-column type=&quot;selection&quot; width=&quot;55&quot; /&gt; &lt;!-- 编号列保持不变 --&gt; &lt;el-table-column label=&quot;编号&quot; align=&quot;center&quot; prop=&quot;id&quot; /&gt; &lt;!-- 称列 - 树形箭头会显示在这里 --&gt; &lt;el-table-column label=&quot;&quot; align=&quot;left&quot; prop=&quot;trgetName&quot;&gt; &lt;!-- 添加自定义内容以确保缩进和箭头正常显示 --&gt; &lt;template #default=&quot;scope&quot;&gt; &lt;div style=&quot;padding-left: 16px;&quot;&gt;{{ scope.row.trgetName }}&lt;/div&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;!-- 其他列保持不变 --&gt; &lt;el-table-column label=&quot;层级&quot; align=&quot;center&quot; prop=&quot;trgetStratum&quot; /&gt; &lt;el-table-column label=&quot;计量内容&quot; align=&quot;center&quot; prop=&quot;trgetMetering&quot; :formatter=&quot;elecNameFormatter&quot; /&gt; &lt;el-table-column label=&quot;单位&quot; align=&quot;center&quot; prop=&quot;trgetUnit&quot; /&gt; &lt;el-table-column label=&quot;年份&quot; align=&quot;center&quot; prop=&quot;trgetYear&quot; width=&quot;100px&quot; /&gt; &lt;el-table-column label=&quot;Act&quot; align=&quot;center&quot; prop=&quot;trgetAct&quot; /&gt; &lt;el-table-column label=&quot;Jan&quot; align=&quot;center&quot; prop=&quot;trgetJan&quot; /&gt; &lt;el-table-column label=&quot;Feb&quot; align=&quot;center&quot; prop=&quot;trgetFeb&quot; /&gt; &lt;el-table-column label=&quot;Mar&quot; align=&quot;center&quot; prop=&quot;trgetMar&quot; /&gt; &lt;el-table-column label=&quot;Apr&quot; align=&quot;center&quot; prop=&quot;trgetApr&quot; /&gt; &lt;el-table-column label=&quot;May&quot; align=&quot;center&quot; prop=&quot;trgetMay&quot; /&gt; &lt;el-table-column label=&quot;Jun&quot; align=&quot;center&quot; prop=&quot;trgetJun&quot; /&gt; &lt;el-table-column label=&quot;Jul&quot; align=&quot;center&quot; prop=&quot;trgetJul&quot; /&gt; &lt;el-table-column label=&quot;Aug&quot; align=&quot;center&quot; prop=&quot;trgetAug&quot; /&gt; &lt;el-table-column label=&quot;Sep&quot; align=&quot;center&quot; prop=&quot;trgetSep&quot; /&gt; &lt;el-table-column label=&quot;Oct&quot; align=&quot;center&quot; prop=&quot;trgetOct&quot; /&gt; &lt;el-table-column label=&quot;Nov&quot; align=&quot;center&quot; prop=&quot;trgetNov&quot; /&gt; &lt;el-table-column label=&quot;Dec&quot; align=&quot;center&quot; prop=&quot;trgetDec&quot; /&gt; &lt;el-table-column label=&quot;YTD&quot; align=&quot;center&quot; prop=&quot;trgetYTD&quot; /&gt; &lt;el-table-column label=&quot;12MRA&quot; align=&quot;center&quot; prop=&quot;trget12MRA&quot; /&gt; &lt;el-table-column label=&quot;操作&quot; align=&quot;center&quot; min-width=&quot;120px&quot;&gt; &lt;template #default=&quot;scope&quot;&gt; &lt;el-button link type=&quot;primary&quot; @click=&quot;openForm(&#39;update&#39;, scope.row.id)&quot; v-hasPermi=&quot;[&#39;energy:data:trgetsetting:update&#39;]&quot; &gt; 编辑 &lt;/el-button&gt; &lt;el-button link type=&quot;danger&quot; @click=&quot;handleDelete(scope.row.id)&quot; v-hasPermi=&quot;[&#39;energy:data:trgetsetting:delete&#39;]&quot; &gt; 删除 &lt;/el-button&gt; &lt;/template&gt; &lt;/el-table-column&gt; &lt;/el-table&gt; &lt;!-- 分页保持不变 --&gt; &lt;Pagination :total=&quot;total&quot; v-model:page=&quot;queryParams.pageNo&quot; v-model:limit=&quot;queryParams.pageSize&quot; @pagination=&quot;getList&quot; /&gt; &lt;/ContentWrap&gt; &lt;!-- 表单弹窗:添加/修改 --&gt; &lt;TrgetSettingForm ref=&quot;formRef&quot; @success=&quot;getList&quot; /&gt; &lt;/template&gt; &lt;script setup lang=&quot;ts&quot;&gt; import { isEmpty } from &#39;@/utils/is&#39; import download from &#39;@/utils/download&#39; import { handleTree } from &#39;@/utils/tree&#39; import { TrgetSettingApi, Trgetsetting } from &#39;@/api/energy/trgetsetting&#39; import TrgetSettingForm from &#39;./TrgetSettingForm.vue&#39; import { useMessage } from &#39;@/hooks/web/useMessage&#39; import { useI18n } from &#39;vue-i18n&#39; import { onMounted, ref, reactive, nextTick } from &#39;vue&#39; defineOptions({ name: &#39;Trgetsetting&#39; }) const message = useMessage() const { t } = useI18n() // 计量内容映射 const elecNameMap = { &#39;1&#39;: &#39;水&#39;, &#39;2&#39;: &#39;电&#39;, &#39;3&#39;: &#39;蒸汽&#39;, &#39;4&#39;: &#39;综合能耗&#39; } const elecNameFormatter = (_row: any, _column: any, cellValue: string | number) =&gt; { return elecNameMap[String(cellValue)] || &#39;&#39; } // 状态管理 const loading = ref(true) const list = ref&lt;Trgetsetting[]&gt;([]) const treeList = ref&lt;any[]&gt;([]) // 树形结构数据 const total = ref(0) // 移除refreshTable,使用更可靠的方式更新表格 const queryParams = reactive({ pageNo: 1, pageSize: 96, TrgetName: undefined, TrgetStratum: undefined, TrgetMetering: undefined, TrgetUnit: undefined, TrgetYear: undefined as string | undefined, TrgetAct: undefined, TrgetJan: undefined, TrgetFeb: undefined, TrgetMar: undefined, TrgetApr: undefined, TrgetMay: undefined, TrgetJun: undefined, TrgetJul: undefined, TrgetAug: undefined, TrgetSep: undefined, TrgetOct: undefined, TrgetNov: undefined, TrgetDec: undefined, TrgetYTD: undefined, Trget12MRA: undefined }) const queryFormRef = ref() const exportLoading = ref(false) const batchInsertLoading = ref(false) const formRef = ref() const checkedIds = ref&lt;number[]&gt;([]) const isExpandAll = ref(true) const tableRef = ref() // 表格引用 /** 查询列表 - 改进树形结构处理 */ const getList = async () =&gt; { loading.value = true try { const params = { ...queryParams, TrgetYear: queryParams.TrgetYear ? Number(queryParams.TrgetYear) : undefined } const data = await TrgetSettingApi.getTrgetPage(params) list.value = data.list total.value = data.total // 处理树形结构 - 确保根节点parentId为0或null const processedData = data.list.map(item =&gt; ({ ...item, // 确保parentId存在,根节点的parentId设为0 parentId: item.parentId || 0 })) // 转换为树形结构 const treeData = handleTree(processedData, &#39;id&#39;, &#39;parentId&#39;, &#39;children&#39;) // 递归标hasChildren属性 const markHasChildren = (nodes: any[]) =&gt; { return nodes.map(node =&gt; { const hasChildren = node.children &amp;&amp; node.children.length &gt; 0 return { ...node, hasChildren, // 递归处理子节点 children: hasChildren ? markHasChildren(node.children) : [] } }) } treeList.value = markHasChildren(treeData) // 确保表格渲染后应用展开状态 nextTick(() =&gt; { if (tableRef.value) { if (isExpandAll.value) { tableRef.value.expandAll() } else { tableRef.value.collapseAll() } } }) } catch (error) { console.error(&#39;查询失败:&#39;, error) } finally { loading.value = false } } /** 展开/折叠操作 - 改进实现 */ const toggleExpandAll = () =&gt; { isExpandAll.value = !isExpandAll.value // 确保表格实例存在 if (tableRef.value) { nextTick(() =&gt; { if (isExpandAll.value) { tableRef.value.expandAll() } else { tableRef.value.collapseAll() } }) } } /** 搜索按钮操作 */ const handleQuery = () =&gt; { queryParams.pageNo = 1 getList() } /** 重置按钮操作 */ const resetQuery = () =&gt; { queryFormRef.value?.resetFields() queryParams.TrgetYear = undefined handleQuery() } /** 添加/修改操作 */ const openForm = (type: string, id?: number) =&gt; { formRef.value.open(type, id) } /** 删除操作 */ const handleDelete = async (id: number) =&gt; { try { await message.delConfirm() await TrgetSettingApi.deleteTrget(id) message.success(t(&#39;common.delSuccess&#39;)) getList() } catch {} } /** 批量删除 */ const handleDeleteBatch = async () =&gt; { try { await message.delConfirm() await TrgetSettingApi.deleteTrgetList(checkedIds.value) message.success(t(&#39;common.delSuccess&#39;)) getList() } catch {} } /** 选择项变化 */ const handleRowCheckboxChange = (records: Trgetsetting[]) =&gt; { checkedIds.value = records.map(item =&gt; item.id) } /** 导出操作 */ const handleExport = async () =&gt; { try { await message.exportConfirm() exportLoading.value = true const params = { ...queryParams, TrgetYear: queryParams.TrgetYear ? Number(queryParams.TrgetYear) : undefined } const data = await TrgetSettingApi.exportTrget(params) download.excel(data, &#39;能耗目标设置.xls&#39;) } catch (error) { console.error(&#39;导出失败:&#39;, error) } finally { exportLoading.value = false } } /** 一键插入预设数据 */ const handleBatchInsert = async () =&gt; { try { if (!queryParams.TrgetYear) { message.warning(&#39;请先选择年份&#39;) return } const trgetYear = Number(queryParams.TrgetYear); if (isNaN(trgetYear) || trgetYear &lt; 2000 || trgetYear &gt; 2100) { message.warning(&#39;请选择有效的年份(2000-2100)&#39;) return } batchInsertLoading.value = true const checkParams = { ...queryParams, TrgetYear: trgetYear, pageSize: 1, pageNo: 1 } const checkData = await TrgetSettingApi.getTrgetPage(checkParams) if (checkData.total &gt; 0) { message.warning(`该${trgetYear}年已存在数据,无需重复生成`) batchInsertLoading.value = false return } await TrgetSettingApi.createTrget(trgetYear); message.success(&#39;预设数据插入成功&#39;) getList() } catch (error: any) { message.error(error.message || &#39;操作失败,请重试&#39;) } finally { batchInsertLoading.value = false } } /** 初始化 */ onMounted(() =&gt; { getList() }) &lt;/script&gt; 按照层级列表来进行树形结构展示,可以展开折叠的那种
08-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值