手写vue移动端Table组件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

以下是我手写的一个小dome,移动端中需要用到Table组件又很难找到合适的,那就手写一个Table组件,可以根据自己需求来定制,更灵活,更贴合


提示:以下是本篇文章正文内容,下面案例可供参考

使用步骤

1.table组件

代码如下:

<template>
    <div :class="loading ? 'sjs-table overflowHidden' : 'sjs-table overflowAuto'" :style="{
        width:width
    }" ref="scrollContainer" @scroll="handleScroll">
        <div class="table-noData y-align" :style="{width:width}" v-if="!loading && tableData.length == 0">
            <van-image :src="require('@/images/img/noData.png')" width="10rem" height="6rem"/>
            <div class="noDataText">
                暂无数据
            </div>
        </div>
        <table v-else class="rl" style="border-collapse:collapse;" ref="sjsTable">
            <thead>
                <tr :class="{'fix-header':fixHeader}">
                    <th class="sjs-table_th"
                        v-for="(column,columnIndex) in columns"
                        :key="column.field"
                        :ref="column.field"
                        :style="{
                            zIndex:(100 - columnIndex),
                            minWidth:column.width,
                            textAlign:column.align,
                            position: column.fixed ? 'sticky' : 'static',
                            padding: column.field == 'rown' || column.field == 'serialNum' ? '0.63rem 0.31rem 0.63rem 1.88rem' : '0.63rem 0.31rem 0.63rem 0.94rem',
                        }"
                    >
                        {{ column.title }}
                    </th>
                </tr>
            </thead>
            <tbody >
                <tr v-for="(row, rowIndex) in tableData" :key="rowIndex" :class="{'trBorder':hasBorder}" @click="rowClick(row)">
                    <td  v-for="(column,columnIndex) in columns"
                        :key="column.field"
                        :ref="column.field"
                        :style="{
                            zIndex:(100 - columnIndex),
                            minWidth:column.width,
                            position: column.fixed ? 'sticky' : 'static',
                            padding: column.field == 'rown' || column.field == 'serialNum' ? '0.63rem 0.31rem 0.63rem 1.88rem' : '0.63rem 0.31rem 0.63rem 0.94rem',
                        }"
                        class="sjs-table_cell fontFamilyTCloudRegularNumber"
                    >
                        <slot :row="row" :cell="row[column.field]" :column="column"></slot>
                    </td>
                </tr>
            </tbody>
            <tfoot v-if="finished || loading || error">
                <tr>
                    <td :colspan="columns.length" class="rl">
                        <div class="van-list__finished-text table-bottom" :style="{width:width}" v-if="finished">
                            {{finishedText}}
                        </div>
                        <div class="table-loading table-bottom" :style="{width:width}" v-if="loading">
                            <van-loading size="1.5rem" class="text">加载中</van-loading>
                        </div>
                        <div class="van-list__finished-text table-bottom" :style="{width:width}" v-if="error" @click="errorLoad">
                            {{errorText}}
                        </div>
                    </td>
                </tr>
            </tfoot>
            <div v-else style="height: 50px"></div>
        </table>
    </div>
</template>

<script>
    export default {
        name: "sjsTable",
        props:{
            width:{
                type:String,
                default:'100vw'
            },
            //表头
            columns:{
                type: Array,
                required: true,
            },
            //列表数据
            tableData:{
                type: Array,
                required: true,
            },
            //表头固定
            fixHeader:{
                type:Boolean,
                default:false,
            },
            hasBorder:{
                type:Boolean,
                default:true,
            },
            error:{
                type:Boolean,
                default:false,
            },
            errorText:{
                type:String,
                default:'请求失败,点击重新加载'
            },
            loading:{
                type:Boolean,
                default:false,
            },
            finished:{
                type:Boolean,
                default:false,
            },
            finishedText:{
                type:String,
                default:'没有更多了'
            },
        },
        data(){
            return{
                isLoad:true,
                oldScrollTop:0,
            }
        },
        watch:{
            tableData(val,oldValue){
                if(oldValue.length == 0 && val.length > 0){
                    this.setFixedTable()
                }else if(oldValue.length > 0 && val.length > 0){
                    this.$nextTick(()=>{
                        this.columns.forEach((item)=>{
                            if(item.fixed){
                                let nodes = this.$refs[item.field]
                                nodes.forEach((node,i,arr)=>{
                                    this.$refs[item.field][i].style.left = arr[0].style.left;
                                })
                            }
                        })
                    })
                }else if (val.length == 0) {
                    this.$refs.scrollContainer.scrollTo(0, 0)
                }
            },
            loading(val){
              if(!val){
                  if(this.isLoad) {
                      this.isLoad = false
                      this.setFixedTable()
                  }
              }
            },
        },
        methods:{
            setFixedTable(){
                this.$nextTick(()=>{
                    let sjsTable = this.$refs.sjsTable
                    if(sjsTable) {
                        let sjsTableLeft = sjsTable.getBoundingClientRect().left
                        this.columns.forEach((item) => {
                            if (item.fixed) {
                                let nodes = this.$refs[item.field]
                                if(nodes && nodes.length > 0) {
                                    nodes.forEach((node, i, arr) => {
                                        let leftPX = node.getBoundingClientRect().left
                                        this.$refs[item.field][i].style.left = (Math.floor((leftPX - sjsTableLeft) * 100) / 100) + 'px';
                                    })
                                }
                            }
                        })
                    }
                })
            },
            //单行点击
            rowClick(row){
                this.$emit('rowClick',row)
            },

            handleScroll() {
                if(!this.finished && !this.loading) {
                    const scrollContainer = this.$refs.scrollContainer
                    const scrollTop = scrollContainer.scrollTop
                    const clientHeight = scrollContainer.clientHeight
                    const scrollHeight = scrollContainer.scrollHeight
                    //上下滚动触发 load事件
                    console.log(scrollTop,'=====',clientHeight,'=====',scrollHeight)
                    if(this.oldScrollTop !== scrollTop){
                        this.oldScrollTop = scrollTop
                        if (Math.ceil(scrollTop + clientHeight) + 10 >= scrollHeight) {
                            console.log('已经滑动到底部')
                            this.$emit('load')
                        }
                    }
                }
            },
            errorLoad(){
                this.$emit('errorLoad')
            },
        }
    }
</script>

<style scoped lang="less">

    .overflowAuto{
        overflow: auto;
        -webkit-overflow-scrolling: auto;
    }
    .overflowHidden{
        overflow: hidden;
    }
    .sjs-table{
        position: relative;

        background: #ffffff;
        table{
            min-width: 100%;
        }
        &_th{
            background: #F5F7FB;
            font-size: 0.88rem;
            font-weight: 400;
            color: #6C7074;
            line-height: 1.38rem;
        }
        &_cell{
            background: #ffffff;
            height: 3.38rem;
            font-size: 1rem;
            font-weight: 400;
            color: #2C2F33;
            line-height: 1.5rem;
        }
    }

    .fix-header{
        position: sticky;
        top: 0px;
        z-index: 1000;
    }
    .trBorder{
        border-bottom: 0.06rem solid rgba(0,0,0,0.06) ;
    }

    .table-bottom{
        /*width: 100%;*/
        /*background: #ffffff;*/
        position: sticky;
        left: 0;
    }
    .table-loading{
        //loding样式
        padding: 0.94rem;
        background-color: rgba(255,255,255,.7);
        .text {
            width: 100%;
            text-align: center;
        }
    }
    .table-noData{
        padding: 4rem 0rem;
    }
    .noDataText{
        font-size: 0.88rem;
        font-weight: 400;
        color: #ABB3BA;
        line-height: 1.38rem;
        margin-top: 0.94rem;
    }
</style>

2.运用

html:

	<sjs-table
		v-if="showTable"
		@rowClick="rowClick"
		width="100vw"
		:loading="tableLoading"
		:style="STARTDATE && ENDDATE ? 'height:calc(100vh - 5.77rem)' : 'height:calc(100vh - 5.39rem)'"
		:hasBorder="false"
		:columns="busiTableColumn"
		:fixHeader="true"
		:table-data="busiTableData"
		finished-text="没有更多了"
		:finished="finished"
		:error="error"
		@load="onLoad"
		@errorLoad="errorLoad">
		   	<template v-slot="{ row, cell,column}">
		        <div :class="'cell cell_'+column.align">
		            {{cell ? cell : '-'}}
		        </div>
		   	</template
	</sjs-table>
data(){
	return {
		tableLoading:false,
		showTable:true,
		busiTableData:[],
		busiTableColumn:[
		    {field: "NUM", title: "序号", align: "left", width: '4rem', fixed: true},
		    {field: "BOND_CODE", title: "债券代码", align: "left", width: '6rem', fixed: true},
		    {field: "BOND_ABBR", title: "债券简称", width: '8rem', align: "left"},
		    {field: "BOND_TYPE", title: "债券类型", width: '11.8rem', align: "left"},
		    {field: "ACTION", title: "业务事项", width: '7.8rem', align: "left"},
		    {field: "BOND_RATING", title: "债券评级", width: '7.8rem', align: "left"},
		    {field: "ISSUE_VALUE", title: "发行量(亿元)", width: '7.8rem', align: "left"},
		    {field: "FACE_RATE", title: "票面利率(%)", width: '7.8rem', align: "left"},
		    {field: "START_DATE", title: "业务日期", width: '13.8rem', align: "left"},
		],
		pageNo: 0,
		pageSize: 20,
		finished: false,
		error: false,
	}
}

methods:{
	onLoad() {
	    this.getTableDataLoad()
	},
	errorLoad() {
	    --this.pageNo
	    this.finished = false
	    this.error = false
	    this.getTableDataLoad()
	},
	getTableDataLoad(){
	    this.tableLoading = true
	    this.$axios.axiosPost({
	        url: ,
	        data: {
	          
	            "pageNo": ++this.pageNo,
	            "pageSize": this.pageSize,
	        }
	    }).then(res => {
	        if (res.status == '200' && res.data) {
	            if (res.data < this.pageSize) {
	                this.finished = true
	            } else {
	                this.finished = false
	            }
	            this.busiTableData = this.busiTableData.concat(res.data)
	            this.error = false
	        }else {
	            this.error = true
	        }
	    }).catch(()=>{
	        this.error = true
	    }).finally(()=>{
	        this.$nextTick(()=> {
	            this.tableLoading = false
	        })
	    })
	},
	refreshTableData(){
	    this.pageNo = 0
	    this.pageSize = 20
	    this.finished = false
	    this.error = false
	    this.busiTableData = []
	    this.getTableDataLoad()
	},
}

			


说明

此组件功能涵盖分页,表头固定,列固定,无数据状态,loading状态,error状态。也可以扩展出下拉刷新、虚拟滚动等等。。。
希望大家在工作中除了不断CV之外,可以手写一个贴合当前逻辑,自己也更加可以灵活运用的小组件。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值