手机端自封装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>
posted @ 2025-06-12 10:19  二月花开  阅读(12)  评论(0)    收藏  举报