【el-table】

文章目录

  • 前言
  • 一、需求描述
  • 二、实现步骤
    • 1.实现树形结构
    • 2.实现懒加载
    • 3.实现列字段排序
    • 4.实现表头固定
    • 5.实现详情路由跳转
    • 6.实现后端分页
  • 三、手写树形表格

前言

el-table 使用总结


一、需求描述

实现树形数据与懒加载、列字段排序、表头固定、详情路由跳转、后端分页

图示:
【el-table】_第1张图片

二、实现步骤

1.实现树形结构

添加属性:

渲染树形数据时,必须要指定 row-key
        row-key=“id”
通过指定 row 中的 hasChildren 字段来指定哪些行是包含子节点。children 与 hasChildren 通过 tree-props 配置
        :tree-props="{children: ‘children’, hasChildren: ‘hasChildren’}"
row-class-name 属性来为 Table 中的某一行添加 class,本例用于设置父子内容背景色
        :row-class-name=“tableRowBgColor”

设置父子内容背景色:

	
	tableRowBgColor ({row}) {
	    if (row.hasChildren) {
	        return 'has-children-row'
	    }
	    if (row.isChildren) {
	        return 'is-children-row'
	    }
	    return ''
	}

单元格内容换行:

	<el-table-column 
	    :label="$t('loc.assignedTo')"
	    width="120">
	    <template slot-scope="scope">
	        <span>{{$t('loc.recipients')}} ({{scope.row.sharedUsers.length}})span>
	        <br/>
	        <span class="overflow-ellipsis w-full" :title="scope.row.sharedUserNames">{{scope.row.sharedUserNames}}span>
	    template>
	el-table-column>

解决限制宽度使列内内容超限省略,引起懒加载按钮与内容换行:
在这里插入图片描述

	/deep/ .creator-col > .cell {
	    display: flex;
	    align-items: center;
	    .el-table__expand-icon {
	        flex: none;
	    }
	    .creator-name {
	        flex: auto;
	        min-width: 0;
	    }
	}
	
	.overflow-ellipsis {
	    white-space: nowrap;
	    overflow: hidden;
	    text-overflow: ellipsis;
	}

解决 el-table-column 限宽,列内容与表头错位问题:
【el-table】_第2张图片

	/deep/ .el-table table{
	  width: 0px;
	}

2.实现懒加载

添加属性:

设置 Table 的 lazy 属性为 true 与加载函数 load
        lazy
        :load=“getSharedRecordPlans”

懒加载列:

            <el-table-column 
                :label="$t('loc.assignedBy2')"
                sortable="custom"
                width="120"
                class-name="creator-col"
                :column-key="'CREATE_SHARE_USER'">
                <template slot-scope="scope">
                    <div class="overflow-ellipsis creator-name" :title="scope.row.createUsername">{{scope.row.createUsername}}div>
                template>
            el-table-column>

加载子节点数据的函数:
resolve() 中传入子节点数据

methods: {
	...
	        getSharedRecordPlans (tree, treeNode, resolve) {
            let params = {
                shareId: tree.id,
                pageSize: 50,
            }
            this.$axios.post($api.urls().listSharedRecordPlans, params)
                .then(response => {
                    // 分享记录下周计划列表
                    let childrenItems = []
                    if (response.items.length > 0) {
                        // 向子记录中添加父记录内容
                        response.items.map((item) => {
                            childrenItems.push(Object.assign({}, item, {
                                'shareUser': tree.shareUser, 
                                'planCount': 1,
                                'sharedAtUtc': tree.sharedAtUtc,
                                'sharedUsers': tree.sharedUsers,
                                'sharedUserNames': tree.sharedUserNames,
                                'comment': tree.comment,
                                'isChildren': true
                                }))
                        })
                    }
                    resolve(childrenItems)
                }).catch(error => {})
        },
	...
}

3.实现列字段排序

添加属性:

列中设置 sortable 属性,通过 Table 的default-sort属性设置默认的排序列和排序顺序。可以使用sort-method或者sort-by使用自定义的排序规则。

如果需要后端排序,需将sortable设置为custom,同时在 Table 上监听sort-change事件
        @sort-change=“sortChange”
本例采用后端排序

html 代码

:column-key="‘SHARE_USER’" 设置对应后端枚举值

    data () {
        return {
       		...
            sortField: '', //拍序列
            sortOrder: '', //排序规则
       		...
   		}
	}
	
	methods: {
		...
		sortChange (params) {
		    let order = params.order
		    if (!order) {
		        this.sortField = undefined
		        this.sortOrder = undefined
		    } else {
		        let columnKey = params.column.columnKey
		        this.sortField = columnKey ? columnKey : params.column.label
		        this.sortOrder = params.order === 'ascending' ? 'ASC' : 'DESC'
		    }
		    // 刷新数据
		    this.loadData()
		}
		...

       	loadData () {
			...
            // 选择了排序,则按照字段排序
            if (this.sortField && this.sortOrder) {
                params['orderKey'] = this.sortField
                params['orderType'] = this.sortOrder
            }
       		...
            this.loader(params).then(data => {
            	...
            }).catch(error => {
                ...
            })
  		}
   		...
	}

loadData

	TeacherSharedTable.vue
	
	<template>
		...
	        <div>
	            <shared-plan-table ref="sharededPlanTable" :loader="listSharedPlans">shared-plan-table>
	        div>
		...
	templete>

	<script>
		import LessonApi from '@/api/lessons2'
		import SharedPlanTable from './components/SharedPlanTable'

		export default {
		    name: 'SharedPlanList',
		    components: {SharedPlanTable},
		    ...
	        methods: {
       			listSharedPlans (params) {
		            params['shareUserId'] = this.shareUserId
		            return LessonApi.listSharedPlans(params)
		            ...
      			}
			}
		}
	script>

4.实现表头固定

添加属性:

只要在el-table元素中定义了height属性,即可实现固定表头
        :height=“tableHeight”

原始方案:

新问题:合适的 tableHeight

ofCourse el-table 中添加 ref=“table”

	SharePlanTable.vue 

	created () {
		...
		this.calcTableHeight ()
		...
	}
	
	methods: {
		...
        // 计算表格高度
        calcTableHeight () {
            this.$nextTick(function () {
                this.tableHeight = window.innerHeight - this.$refs.table.$el.offsetTop - 50;
                // console.log("aaa", this.tableHeight)
                // 监听窗口大小变化
                let self = this;
                window.onresize = function() {
                    self.tableHeight = window.innerHeight - self.$refs.table.$el.offsetTop - 50
                }
            })
        }
   		...
	}

【el-table】_第3张图片
莫名奇妙的问题, 在一个页面回退时,方法正常调用, tableHeight = 0

新方案:

	<template>
    	
    	<div class="admin-shared-table display-flex flex-direction-col h-full">
    		<div>
    			<el-table ...>
    			eltable>
    		div>
    	div>
    	
    	<div class="table-footer flex-none">
            <el-pagination
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
                :current-page="currentPage"
                :page-sizes="[10, 20, 30]"
                :page-size="pageSize"
                layout="total, sizes, ->, prev, pager, next"
                :total="total">
            el-pagination>
    	div>
	.display-flex {
	    display: -webkit-flex;
	    display: flex;
	}
	
	.flex-direction-col {
	    flex-direction: column
	}
	
	.flex-none {
	    flex: none;
	}

	.flex-auto {
	    flex: auto;
	    min-width: 0;
	}

原理:
纵向 flex
表格 div auto 拓展         !!!el-table 设置 height=“100%”
分页 div none 自身撑开高度

flex布局中flex:1和flex:auto的区别

5.实现详情路由跳转

打开率跳转列:

	
	<el-table-column 
	    :label="$t('loc.openRate')"
	    width="90">
	    <template slot-scope="scope">
	        
	        <el-button
	            size="medium"
	            type="text"
	            :disabled="scope.row.planCount > 1 || scope.row.planCount === 0"
	            @click="viewOpenRate(scope.row.shareId, scope.row.planId)"><span>{{scope.row.percent}}span>
	        el-button>
	    template>
	el-table-column>

父级记录条打开率跳转按钮禁用,但禁用样式不美观

修改按钮禁用样式:

	/deep/ .el-button.is-disabled {
		// 恢复 ElementUI 黑色字体色号
	    color: #606266 !important;
	    // 恢复透明度
	    opacity: 1 !important;
	}

路由跳转:

	methods: {
		...
        // 查看周计划 Open Rate 跳转
        viewOpenRate (shareId, planId) {
            this.$router.push({
                name: 'view-plan',
                params: {
                    planId: planId
                },
                query: {
                    shareId: shareId,
                    param: 'openRate'
                }
            })
        }
   		...
	}
	src/router/modules/lesson2.js
    // lesson Plan
    {
      path: 'weekly-lesson-planning',
      component: () => import('@/views/modules/lesson2/Layout.vue'),
      children: [
        {
          path: '',
          name: 'plan',
          component: () => import('@/views/modules/lesson2/lessonPlan/WeeklyPlan.vue'),
          children: [
            {
              path: 'list',
              name: 'list-plan',
              component: () => import('@/views/modules/lesson2/lessonPlan/PlanList.vue')
            },
            {
              // path: 'assignedPlan/:adminId',
              path: 'create-virtual-shadow-list',
              name: 'assigned-plan',
              component: () => import('@/views/modules/lesson2/lessonPlan/AssignedPlanList.vue')
            },
            {
              path: 'shadowed-list',
              name: 'teacherShadow',
              component: () => import('@/views/modules/lesson2/lessonPlan/TeacherShadowedList.vue')
            }
          ]
        },
        {
          path: 'edit/:planId',
          name: 'edit-plan',
          component: () => import('@/views/modules/lesson2/lessonPlan/EditPlan.vue')
        },
        {
          path: 'view/:planId',
          name: 'view-plan',
          component: () => import('@/views/modules/lesson2/lessonPlan/ViewPlan.vue')
        }
        ...
		]
	}

路径 url:
        http://localhost:8080/#/lessons/weekly-lesson-planning/view/EBEE2C19-1031-4B48-82DF-3BEDF8EE61C5?shareId=ECFA84C8-FE79-4237-9313-454589D18503¶m=openRate
params planId -> /:planId
query shareId … ->?shareId=

vue 中的动态传参和query传参:

Vue router 如何传参
        params:/router1/:id,这里的 id 叫做 params。例如/router1/123, /router1/789
接收参数:this.$route.params.id

        query:/router1?id=123,这里的 id 叫做 query。例如/router1?id=456
接收参数: this.$route.query.id

  created () {
    // 获取周计划 ID
    this.planId = this.$route.params.planId
  }

【el-table】_第4张图片

6.实现后端分页

	methods: {
		...
        handleSizeChange (size) {
            this.pageSize = size
            this.currentPage = 1
            // 刷新数据
            this.loadData()
        },

        handleCurrentChange (page) {
            this.currentPage = page
            // 刷新数据
            this.loadData()
        }
   		...
	}

后台代码:

	planServiceImpl.java
	
	...
    /**
     * 获取分享记录列表
     *
     * @param request 获取分享记录列表请求信息
     * @return 分享记录列表响应信息
     */
    @Override
    public ListSharedRecordsResponse listSharedRecords(ListSharedRecordsRequest request) {
   		...
	        // 检查参数
	        this.checkListParam(request);
	        ...
	        // 获取这些老师被分享的记录
	        PageList<ShareEntity> shareEntitiesPage = planShareDao.listByShareUserIds(request.getPageSize(),
	                request.getPage(), request.getOrderKey(), request.getOrderType(), user.getAgencyId(), teacherIds);
	        ...
       	}
		...

    /**
     * 检查分页、排序参数
     * @param request 请求信息
     */
    private void checkListParam(ListSharedRecordsRequest request) {
        // 页码不能小于 1,默认第 1 页
        if (request.getPage() < 1) {
            request.setPage(1);
        }
        // 每页条数不能小于 1,默认 1 页 10 条记录
        if (request.getPageSize() < 1) {
            request.setPageSize(10);
        }
        // 每页条数不能大于 50
        if (request.getPageSize() > 50) {
            request.setPageSize(50);
        }
        // 解析排序字段
        PlanOrderKey planOrderKey = PlanOrderKey.parse(request.getOrderKey());
        // 设置排序字段
        request.setOrderKey(planOrderKey.toString());
        // 解析排序类型
        OrderType orderType = OrderType.parse(request.getOrderType());
        // 设置排序类型
        request.setOrderType(orderType.toString());
    }
    ...
	planOrderKey.java
	
	package com.learninggenie.common.data.enums.lesson2;
	
	public enum PlanOrderKey {
	    TIME,       // 时间
	    STATUS,     // 状态
	    CREATE_SHARE_USER, // 创建分享人
	    SHARE_USER; // 被分享人
	
	    /**
	     * 解析枚举类型字符串
	     * @param key 枚举类型字符串
	     * @return 枚举对象
	     */
	    public static PlanOrderKey parse(String key) {
	        // 过滤空值
	        if (key == null) {
	            return TIME;
	        }
	        // 遍历枚举对象
	        for (PlanOrderKey orderKey : PlanOrderKey.values()) {
	            // 如果和要解析的名称一致,则返回
	            if (orderKey.toString().equalsIgnoreCase(key.trim())) {
	                return orderKey;
	            }
	        }
	        // 没有匹配的,则默认按照时间排序
	        return TIME;
	    }
	}
	PlanShareDaoImpl.java
	
	@Repository
	public class PlanShareDaoImpl extends ServiceImpl<PlanShareMapper, ShareEntity> implements PlanShareDao {
	...
	    /**
	     * 根据分享用户 ID 列表查询分享记录
	     *
	     * @param shareUserIds 分享用户 ID 列表
	     * @return 分享记录列表
	     */
	    @Override
	    public PageList<ShareEntity> listByShareUserIds(Integer pageSize, Integer pageNum, String orderKey, String orderType, String agencyId, List<String> shareUserIds) {
	        Page<ShareEntity> page = Page.of(pageNum, pageSize);
	        Page<ShareEntity> pageData = this.baseMapper.listByShareUserIds(page, agencyId, shareUserIds, orderKey, orderType);
	        return PageUtil.toPage(pageData);
	    }
	...
	}
	PlanShareMapper.xml
	
	
	DOCTYPE mapper PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
	        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
	<mapper namespace="com.learninggenie.common.data.mapper.mybatisplus.lesson2.PlanShareMapper">
	    
	    <select id="listByShareUserIds" resultType="com.learninggenie.common.data.entity.lesson2.plan.ShareEntity">
	        SELECT s.*, share_up.DisplayName AS ShareUsername, create_up.DisplayName AS CreateUsername
	            FROM lessons2_plan_share s
	            JOIN authentication_user share_user ON s.ShareUserId = share_user.Id
	            JOIN authentication_user_profile share_up ON share_user.Id = share_up.UserId
	            JOIN authentication_user create_user ON s.CreateUserId = create_user.Id
	            JOIN authentication_user_profile create_up ON create_user.Id = create_up.UserId
	        WHERE s.IsDeleted = 0
	            AND s.AgencyId = #{agencyId}
	            
	            <if test="shareUserIds != null and shareUserIds.size() > 0">
	                AND s.ShareUserId IN
	                <foreach collection="shareUserIds" item="shareUserId" open="(" close=")" separator=",">
	                    #{shareUserId}
	                foreach>
	            if>
	            <choose>
	                
	                <when test="orderKey == 'SHARE_USER'">
	                    ORDER BY share_up.DisplayName ${orderType}, s.UpdateAtUtc DESC
	                when>
	                
	                <when test="orderKey == 'CREATE_SHARE_USER'">
	                    ORDER BY create_up.DisplayName ${orderType}, s.UpdateAtUtc DESC
	                when>
	                
	                <otherwise>
	                    ORDER BY s.UpdateAtUtc DESC
	                otherwise>
	            choose>
	    select>
	    ...
    Mapper>
	PlanShareMapper.java
	
	@Mapper
	@Component
	public interface PlanShareMapper extends BaseMapper<ShareEntity> {
		...
		    /**
     * 根据分享用户 ID 列表查询分享记录
     * @param pageList 分页参数
     * @param agencyId 机构 ID
     * @param shareUserIds 分享用户 ID 列表
     * @param orderKey 排序 Key
     * @param orderType 排序类型
     * @return 分享记录列表
     */
    Page<ShareEntity> listByShareUserIds(@Param("pageList") Page<ShareEntity> pageList,
                                         @Param("agencyId") String agencyId,
                                         @Param("shareUserIds") List<String> shareUserIds,
                                         @Param("orderKey") String orderKey,
                                         @Param("orderType") String orderType);
		...
	}
	ShareEntity.class
	
	@TableName("lessons2_plan_share")
	public class ShareEntity {

	    @ApiModelProperty(value = "ID", required = true)
	    @TableId(value = "Id")
	    private String id;
	
	    @ApiModelProperty(value = "分享的是哪个老师", required = true)
	    @TableField(value = "ShareUserId")
	    private String shareUserId;
	
	    @ApiModelProperty(value = "分享人姓名")
	    @TableField(exist = false)
	    private String shareUsername;
    	...
    }
	PageUtil.class

	package com.learninggenie.common.data.utils;
	
	import com.baomidou.mybatisplus.core.metadata.IPage;
	import com.learninggenie.common.data.entity.PageList;
	
	import java.util.function.Function;
	import java.util.stream.Collectors;
	
	public class PageUtil {
	
	    /**
	     * 将 Mybatis Plus 分页转换为 Dao层分页
	     *
	     * @param mpPage Mybatis Plus 分页
	     * @param     实体类型
	     * @return Dao层分页
	     */
	    public static <T> PageList<T> toPage(IPage<T> mpPage) {
	        PageList<T> page = new PageList<>();
	        page.setRecords(mpPage.getRecords());
	        page.setPageNum(mpPage.getCurrent());
	        page.setPageSize(mpPage.getSize());
	        page.setTotal(mpPage.getTotal());
	        return page;
	    }
	
	    /**
	     * 分页类型转换
	     *
	     * @param pageList 源分页信息
	     * @param mapper   转换函数
	     * @param       源类型
	     * @param       目标类型
	     * @return 目标类型分页信息
	     */
	    public static <T, R> PageList<R> map(PageList<T> pageList, Function<T, R> mapper) {
	        PageList<R> page = new PageList<>();
	        page.setRecords(pageList.getRecords().stream().map(mapper).collect(Collectors.toList()));
	        page.setPageNum(pageList.getPageNum());
	        page.setPageSize(pageList.getPageSize());
	        page.setTotal(pageList.getTotal());
	        return page;
	    }
	
	    /**
	     * @param pageNum  当前页
	     * @param pageSize 页大小
	     * @param       目标类型
	     * @return 空页面分页信息
	     */
	    public static <T> PageList<T> emptyPage(Long pageNum, Long pageSize) {
	        PageList<T> page = new PageList<>();
	        page.setPageNum(pageNum);
	        page.setPageSize(pageSize);
	        page.setTotal(0L);
	        return page;
	    }
	}

三、手写树形表格

自定义类 Element 树形数据表格并实现表头排序

图示:
【el-table】_第5张图片


	// 表格
    <table class="table" style="font-family: SourceSansPro-Regular;font-weight: Regular;color: #222222;">
    	// 表头固定 存在滑动 border 消失问题
        <thead style="position: sticky;top: 0px;">
            <tr class="measure">
                <td style="background-color: #f2f2f2;max-width: 300px;">
                    <span class="font-bold" style="padding-left: 25px;">{{$t('loc.mses')}}span>
                td>
                <td style="background-color: #e7f2ff;width: 240px;" class="td-center" @click="observationSort('noObservationCount')">
                    <span class="font-bold">{{$t('loc.noObservation')}}span>
                    <span class="pos-rlt">
                        <span style="left: 5px;" class="fa fa-caret-up pos-abt lg-sort-asc" :class="showAscSign('noObservationCount')">span>
                        <span style="left: 5px;" class="fa fa-caret-down pos-abt lg-sort-desc" :class="showDescSign('noObservationCount')">span>
                    span>
                td>
                <td style="background-color: #edfae7;min-width: 260px;" class="td-center" @click="observationSort('oneObservationCount')">
                    <span class="font-bold">{{$t('loc.observationCount1')}}span>
                    <span class="pos-rlt">
                        <span style="left: 5px;" class="fa fa-caret-up pos-abt lg-sort-asc" :class="showAscSign('oneObservationCount')">span>
                        <span style="left: 5px;" class="fa fa-caret-down pos-abt lg-sort-desc" :class="showDescSign('oneObservationCount')">span>
                    span>
                td>
                <td style="background-color: #ffffea;min-width: 240px;" class="td-center" @click="observationSort('multiObservationCount')">
                    <span class="font-bold">{{$t('loc.observationCountMore2')}}span>
                    <span class="pos-rlt">
                        <span style="left: 5px;" :class="showAscSign('multiObservationCount')" class="fa fa-caret-up pos-abt lg-sort-asc">span>
                        <span style="left: 5px;" :class="showDescSign('multiObservationCount')" class="fa fa-caret-down pos-abt lg-sort-desc">span>
                    span>
                td>
            tr>
        thead>
        <tbody v-for="(measure, index) in copyObservationData" :key="index">
            <tr @click="openMeasureDetail(measure)" class="measure bg-light text-black-mon">
                <td>
                    <div style="display: flex;align-items: center;">
                        <span v-if="measure.open" class="fa fa-caret-up m-r-xs">span>
                        <span v-if="!measure.open" class="fa fa-caret-down m-r-xs">span>
                        <div>
                            <span class="font-bold">{{measure.measureAbbr}}span>
                            <span v-if="measure.measureAbbr"><br/>span>
                            <span>{{measure.measureName}}span>
                        div>
                    div>
                td>
                <td class="td-center">
                    <span class="font-bold">{{measure.noObservationCount}}span>
                td>
                <td class="td-center">
                    <span class="font-bold">{{measure.oneObservationCount}}span>
                td>
                <td class="td-center">
                    <span class="font-bold">{{measure.multiObservationCount}}span>
                td>
            tr>
            <tr v-if="measure.open" class="measure">
                <td style="vertical-align: middle;">
                    <span style="padding-left: 15px">span>
                td>
                <td>
                    <span :class="colorShow(childrenInfo[child])" v-for="(child, index) in measure.noObservationChildren" :key="index">
                        {{childrenInfo[child].name}}<span v-if="index != measure.noObservationChildren.length-1">,span>
                    span>
                td>
                <td>
                    <span :class="colorShow(childrenInfo[child])" v-for="(child, index) in measure.oneObservationChildren" :key="index">
                        {{childrenInfo[child].name}}<span v-if="index != measure.oneObservationChildren.length-1">,span>
                    span>
                td>
                <td>
                    <span :class="colorShow(childrenInfo[child])" v-for="(child, index) in measure.multiObservationChildren" :key="index">
                        {{childrenInfo[child].name}}<span v-if="index != measure.multiObservationChildren.length-1">,span>
                    span>
                td>
            tr>
        tbody>
    table>
   

	// 实现排序
	data () {
		...
            sortStatus: {
                currentSort: '', // 当前排序字段
                noObservationCount: 0,
                oneObservationCount: 0,
                multiObservationCount: 0
            }
		...
 	}

	methods:{
		...
        // 展开测评点下小孩信息
        openMeasureDetail (measure) {
            this.$set(measure, 'open', !measure.open);
        }
        
         // 测评点观察排序
        observationSort (observationType) {
            // 置空当前排序条件外排序状态
            this.sortStatus.currentSort = observationType;
            var currentSortStatus = this.sortStatus[observationType];
            if (currentSortStatus == 2) {
                currentSortStatus = 0;
                this.sortStatus[observationType] = 0;
            } else {
                currentSortStatus += 1;
                this.sortStatus[observationType] += 1;
            }
            this.copyObservationData = this.observationData.concat();
            if (!currentSortStatus) {
                return;
            }
            this.copyObservationData.sort(function(a,b) {
                var x = a[observationType];
                var y = b[observationType];
                if (currentSortStatus == 1) {
                    // 升序
                    return ((x<y)?-1:(x>y)?1:0);
                } else {
                    // 降序
                    return ((x<y)?1:(x>y)?-1:0);
                }
            });
        }
        ...
	}
 	

你可能感兴趣的:(#,Element,vue.js,javascript,elementui,前端框架)