提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
以下是我手写的一个小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之外,可以手写一个贴合当前逻辑,自己也更加可以灵活运用的小组件。