手机端自封装table组件
子组件:commonTable
<template> <div class="table-box-content"> <div class="table-header" :style="{ width: headerWidth + 'px' }"> <div v-for="(col, index) in columns" :key="index" :style="getColumnStyle(col)"> <slot v-if="$scopedSlots[col.titleSlot]" :name="col.titleSlot"></slot> <span v-else>{{ col.name }}</span> </div> </div> <div class="list-container" :style="{ width: headerWidth + 'px' }"> <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :class="{ nodatawrap: tableData.length == 0 && finished }" > <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" > <div class="table-body-item" :class="[typeof rowClassName === 'function' ? rowClassName(item) : rowClassName]" :style="{ width: headerWidth + 'px' }" v-for="item in tableData" :key="item.id" @click="handleClick(item)" > <div v-for="(col, idx) in columns" :key="idx" :style="getColumnStyle(col)"> <slot v-if="$scopedSlots[col.slot]" :name="col.slot" :data="item"></slot> <span v-else>{{ item[col.key] || '--' }}</span> </div> </div> </van-list> </van-pull-refresh> </div> </div> </template> <script> export default { name: "commonTable", props: { rowClassName: { type: [String, Function], default: null }, columns: { type: Array, default: () => [ // { // key: 'discountNo', name: '折扣配置编码' // }, // { // key: 'discountNo123', name: '折扣配置编码123', slot: 'discountNo123', width: 100, titleSlot: 'discountNo123' // }, ] }, tableData: { type: Array, default: () => [] } }, data() { return { headerWidth: 0, loading: false, // 是否加载中 finished: false, refreshing: false, // 是否刷新中, false表示加载完成 pageIndex: 1, pageSize: 20 } }, methods: { getColumnStyle(col) { return { width: col.width ? `${col.width}px` : 'auto', minWidth: col.minWidth ? `${col.minWidth}px` : '80px' } }, onLoad() { if (this.refreshing) { this.pageIndex = 1; this.finished = false; } this.loading = true; // 抛出事件并附带分页参数 this.$emit('load', { page: this.pageIndex, pageSize: this.pageSize, complete: (hasMore) => { this.loading = false; this.finished = !hasMore; this.refreshing = false; this.pageIndex++; }, refresh: () => { this.loading = false; this.refreshing = false; } }); }, onRefresh() { this.refreshing = true; this.onLoad(); }, handleClick(item) { this.$emit('row-click', item) }, setWidth() { const headerCols = document.querySelectorAll('.table-header > div') let width = 0 headerCols.forEach((col) => { width += col.offsetWidth }) this.headerWidth = width }, }, mounted() { this.$nextTick(() => { this.setWidth() }) }, } </script> <style lang="less" scoped> .table-box-content { position: relative; height: 100%; overflow: auto; &::-webkit-scrollbar { display: none; /* 隐藏滚动条 */ } .table-header { height: 40px; border-radius: 8px 8px 0 0; font-family: PingFangSC-Regular; font-weight: 400; font-size: 14px; color: #ffffff; white-space: nowrap; background: #3d7cfe; > div { display: inline-block; min-width: 80px; height: 40px; line-height: 40px; padding-left: 10px; } } .list-container { position: absolute; top: 40px; left: 0; right: 0; bottom: 0px; overflow-x: hidden; overflow-y: auto; width: 100%; display: flex; flex-direction: column; .table-body-item { height: 38px; white-space: nowrap; &.gray { background: rgba(80, 87, 102, 0.1); } &.grayd { background: rgba(80, 87, 102, 0.2); } &.pink { background: rgba(255, 245, 231, 1); } > div { display: inline-block; min-width: 80px; height: 40px; line-height: 40px; font-size: 12px; color: #333333; padding-left: 10px; white-space: nowrap; overflow: hidden; overflow-x: auto; &::-webkit-scrollbar { display: none; /* 隐藏滚动条 */ } &:active { opacity: 0.8; } } } .van-pull-refresh { overflow: auto !important; width: 100%; } .nodatawrap { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; ::v-deep .van-pull-refresh__track { display: flex; justify-content: center; align-items: center; } } } } </style>
使用方法:
<template> <div class="listnew-container"> <div class="listnew-content"> <div class="listnew-content-title">箱单详情</div> <van-search v-model="keyword" shape="round" background="#ffffff" placeholder="请输入项目号/物料编码" @search="onSearch" /> <div class="table-box"> <commonTable ref="commonTable" :columns="columns" :tableData="list" :row-class-name="getRowClassName" @load="handleLoad" > <template #boxCode="{ data }"> <span @click="boxCodeClick(data)">{{ data.boxCode || '--' }}</span> </template> <template #totalLackAmount="{ data }"> <span>{{ data.totalLackAmount }}元</span> </template> </commonTable> </div> </div> </div> </template> <script> import { queryPage } from '@/api/user' import commonTable from '@/components/commonTable' export default { components: { commonTable }, data() { return { keyword: '', list: [], // 列表数据 columns: [ {key: 'projectCode', name: '项目号', width: 120}, {key: 'boxCode', name: '箱号', width: 80, slot: 'boxCode'}, {key:'signStatus', name: '发运状态', slot:'signStatus', width: 100}, {key: 'partCode', name: '图号', width: 80}, {key: 'partName', name: '名称', width: 80}, {key: 'partNo', name: '件码', width: 140}, {key: 'smallBoxCode', name: '小箱码', width: 140}, {key: 'bigBoxCode', name: '大箱码', width: 140}, {key: 'netWeight', name: '净重(kg)', width: 100}, {key: 'grossWeight', name: '毛重(kg)', width: 80}, ] } }, methods: { boxCodeClick() { }, handleLoad({ page, pageSize, complete }) { this.fetchData(page, pageSize) .then(res => { const hasMore = res.rows.length > 0 && page < res.pageTotal; this.list = page === 1 ? [...res.rows] : [...this.list, ...res.rows]; complete(hasMore); }) .catch(() => { complete(false); // 加载失败时停止加载 }); }, getRowClassName(row) { if (row.packagePlanStatus === '锁产') return 'pink'; if (row.packagePlanStatus === '预排') return 'gray'; return ''; }, onSearch() { // 清空列表数据 this.list = [] this.$refs.commonTable && this.$refs.commonTable.onRefresh() }, async fetchData(page, pageSize) { const params = { data: { projectNo: this.$route.query.projectNo }, page: { pageIndex: page, pageSize: pageSize } }; const res = await queryPage(params); if (res && res.data) { return { rows: res.data.rows, pageTotal: res.data.pageTotal }; } return { rows: [], pageTotal: 0 }; }, }, mounted() { this.$nextTick(() => { this.$refs.commonTable && this.$refs.commonTable.onLoad() }) }, } </script> <style lang="less" scoped> .listnew-container { min-height: 100vh; background-color: #fff; .listnew-content { display: flex; flex-direction: column; padding-top: 24px; height: 100vh; .listnew-content-title { line-height: 1; font-family: PingFangSC-Semibold; font-weight: 500; font-size: 18px; color: #111111; padding-left: 16px; padding-bottom: 4px; } .table-box { flex: 1; padding: 0 16px 20px; overflow: hidden; } } } .status-wrap { display: flex; justify-content: center; align-items: center; width: 44px; height: 22px; color: #ffffff; font-size: 12px; font-weight: 500; border-radius: 2px; margin-top: 10px; &.statusReceived { opacity: 0.5; background: #3498ff; } &.statusLocked { background: #f55f4e; } &.statusCompleted { background: #17ccac; } &.status-fully-arrived { background: #3498ff; } } </style>
<template>
<div class="table-box-content">
<div class="table-header" :style="{ width: headerWidth + 'px' }">
<div v-for="(col, index) in columns" :key="index" :style="getColumnStyle(col)">
<slot v-if="$scopedSlots[col.titleSlot]" :name="col.titleSlot"></slot>
<span v-else>{{ col.name }}</span>
</div>
</div>
<div class="list-container" :style="{ width: headerWidth + 'px' }">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" :class="{ nodatawrap: tableData.length == 0 && finished }" >
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<div
class="table-body-item"
:class="[typeof rowClassName === 'function' ? rowClassName(item) : rowClassName]"
:style="{ width: headerWidth + 'px' }"
v-for="item in tableData"
:key="item.id"
@click="handleClick(item)"
>
<div v-for="(col, idx) in columns" :key="idx" :style="getColumnStyle(col)">
<slot v-if="$scopedSlots[col.slot]" :name="col.slot" :data="item"></slot>
<span v-else>{{ item[col.key] || '--' }}</span>
</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</div>
</template>
<script>
exportdefault {
name:"commonTable",
props: {
rowClassName: {
type: [String, Function],
default:null
},
columns: {
type:Array,
default: () => [
// {
// key: 'discountNo', name: '折扣配置编码'
// },
// {
// key: 'discountNo123', name: '折扣配置编码123', slot: 'discountNo123', width: 100, titleSlot: 'discountNo123'
// },
]
},
tableData: {
type:Array,
default: () => []
}
},
data() {
return {
headerWidth:0,
loading:false, // 是否加载中
finished:false,
refreshing:false, // 是否刷新中, false表示加载完成
pageIndex:1,
pageSize:20
}
},
methods: {
getColumnStyle(col) {
return {
width:col.width ? `${col.width}px` : 'auto',
minWidth:col.minWidth ? `${col.minWidth}px` : '80px'
}
},
onLoad() {
if (this.refreshing) {
this.pageIndex = 1;
this.finished = false;
}
this.loading = true;
// 抛出事件并附带分页参数
this.$emit('load', {
page:this.pageIndex,
pageSize:this.pageSize,
complete: (hasMore) => {
this.loading = false;
this.finished = !hasMore;
this.refreshing = false;
this.pageIndex++;
},
refresh: () => {
this.loading = false;
this.refreshing = false;
}
});
},
onRefresh() {
this.refreshing = true;
this.onLoad();
},
handleClick(item) {
this.$emit('row-click', item)
},
setWidth() {
constheaderCols = document.querySelectorAll('.table-header > div')
letwidth = 0
headerCols.forEach((col) => {
width += col.offsetWidth
})
this.headerWidth = width
},
},
mounted() {
this.$nextTick(() => {
this.setWidth()
})
},
}
</script>
<stylelang="less"scoped>
.table-box-content {
position: relative;
height: 100%;
overflow: auto;
&::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
.table-header {
height: 40px;
border-radius: 8px8px00;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #ffffff;
white-space: nowrap;
background: #3d7cfe;
> div {
display: inline-block;
min-width: 80px;
height: 40px;
line-height: 40px;
padding-left: 10px;
}
}
.list-container {
position: absolute;
top: 40px;
left: 0;
right: 0;
bottom: 0px;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
display: flex;
flex-direction: column;
.table-body-item {
height: 38px;
white-space: nowrap;
&.gray {
background: rgba(80, 87, 102, 0.1);
}
&.grayd {
background: rgba(80, 87, 102, 0.2);
}
&.pink {
background: rgba(255, 245, 231, 1);
}
> div {
display: inline-block;
min-width: 80px;
height: 40px;
line-height: 40px;
font-size: 12px;
color: #333333;
padding-left: 10px;
white-space: nowrap;
overflow: hidden;
overflow-x: auto;
&::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
&:active {
opacity: 0.8;
}
}
}
.van-pull-refresh {
overflow: auto!important;
width: 100%;
}
.nodatawrap {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
::v-deep .van-pull-refresh__track {
display: flex;
justify-content: center;
align-items: center;
}
}
}
}
</style>