一个 适用 vue3 ts h5移动端 table组件

vue3-h5-table

介绍

适用于 vue3 + ts 的 h5 移动端项目 table 组件

支持 左侧固定 滑动 每行点击回调 支持 指定列排序

链接 :https://github.com/duKD/vue3-h5-table

效果

请添加图片描述

props 说明
minTableHeight 表格最小高度 可选 默认600
rowNum 表格显示几行 可选 默认 6
headerHeight 头部默认高度 可选 默认 60
rowHeight 每行数据的默认高度 默认 100
column 每列数据说明 见下文
tableDates 表格数据
fixedHeader 是否固定表头 默认true
export type columnItemType = {
    
    
   title:string // 列名
   dataIndex?:string // table data key 值 
   width?:number // 列 宽度
   slotKey?:string // 插槽作用域 id
   sortable?:boolean //是否 支持排序
   align?: 'left'|'center'|'right' // 布局
   key?:string // 哪个列数据 作为 唯一key 值 默认 index
   render?:(h:renderType,row:any)=>void // 自定义render
   
}
type propsType = {
    
    
  minTableHeight?: number; //表格最小高度
  rowNum?: number; // 表格显示几行
  headerHeight?: number; // 头部默认高度
  rowHeight?: number; //每行数据的默认高度
  column: Array<columnItemType>;
  tableDates: Array<any>;
  fixedHeader?: boolean; // 是否固定表头
  isClick?: boolean; // 是否需要触发行点击事件
  disable?: boolean; // 是否启用下拉加载
  error?: boolean; // 数据加载失败
  loading?: boolean; // 数据处于加载状态
  finish?: boolean; // 数据 是否完全加载
  loadingText?: string; // 加载文案
  errorText?: string; // 失败文案
  finishedText?: string; // 完成文案
  offset?: number; //触发加载的底部距离
  rootValue?: number; //  
  optimized?: boolean; // 是否开启优化
};

使用 实例:

<template>
  <div class="position">
    <section style="height: 200px"></section>
    <h5-table
      ref="h5TableRef"
      :fixed-header="true"
      :column="column"
      :table-dates="tableDates"
      @row-click="rowClick"
      @handle-head-sort-click="handleHeadSortClick"
      v-model:error="error"
      :is-click="true"
      v-model:loading="loading"
      :finish="finish"
      @load="onload"
    >
      <template #titleSlot>
        <section class="nameAndMarkValueTitle">
          <div>
            <span class="name_1"> 班费 </span>/<span class="name_2">
              总和
            </span>
          </div>
        </section>
      </template>

      <template #title="item">
        <section class="nameAndMarkValue">
          <div class="name">
            {
   
   { item.select }}
            <span class="type">{
   
   { item.type === 1 ? "深" : "沪" }}</span>
          </div>
          <div class="markValue">{
   
   { item.markValue }}=={
   
   { item.id }}</div>
        </section>
      </template>
      <template #positionAndUse="item">
        <section class="positionAndUse">
          <div class="position">
            {
   
   { item.position }}
          </div>
          <div class="use">{
   
   { item.use }}</div>
        </section>
      </template>

      <template #curAndCost="item">
        <section class="curAndCost">
          <div class="cur">
            {
   
   { item.cur }}
          </div>
          <div class="cost">{
   
   { item.cost }}</div>
        </section>
      </template>
      <template #floatAndProfit="item">
        <section class="floatAndProfit">
          <div class="float">{
   
   { item.float }}</div>
          <div class="profit">{
   
   { item.profit }}</div>
        </section>
      </template>

      <template #rowDownMark>
        <section class="rowDownMark">
          <div class="rowDownMark-item" @click="handelSell">买入</div>
          <div class="rowDownMark-item">卖出</div>
          <div class="rowDownMark-item">行情</div>
        </section>
      </template>
    </h5-table>
  </div>
</template>
<script setup lang="ts">
import {
      
       H5Table } from "../lib/h5-table";
import type {
      
       columnItemType, sortStatusType } from "../lib/h5-table/types";
import {
      
       ref, watch } from "vue";

const column: Array<columnItemType> = [
  {
      
      
    title: "班费/总值",
    key: "id",
    dataIndex: "nameAndMarkValue",
    width: 250,
    slotKey: "title",
    slotTitleKey: "titleSlot",
    align: "left",
  },
  {
      
      
    title: "持仓/可用",
    slotKey: "positionAndUse",
    dataIndex: "positionAndUse",
    sortable: true,
    width: 200,
    align: "right",
  },
  {
      
      
    title: "现价/成本",
    slotKey: "curAndCost",
    dataIndex: "curAndCost",
    // sortable: true,
    width: 200,
    align: "right",
  },
  {
      
      
    title: "浮动/盈亏",
    width: 200,
    slotKey: "floatAndProfit",
    align: "right",
  },
  {
      
      
    title: "账户资产",
    dataIndex: "count",
    width: 200,
  },
];

const datas = [
  {
      
      
    id: 0,
    select: "三年二班",
    type: 1,
    position: "27000",
    use: "5,000",
    markValue: "500,033.341",
    cur: "30.004",
    cost: "32.453",
    newPrice: 20,
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 1,
    select: "四年一班",
    type: 1,
    markValue: "23,933.341",
    position: "28000",
    use: "5,000",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 2,
    select: "三年二班",
    markValue: "500,033,341",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    position: "27300",
    use: "5,000",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 3,
    select: "五年二班",
    markValue: "500,033,341",
    position: "27000",
    use: "5,000",
    cur: "30.004",
    cost: "32.453",
    newPrice: 20,
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 4,
    select: "一年二班",
    markValue: "500,033,341",
    position: "27000",
    use: "5,000",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 5,
    select: "六年三班",
    markValue: "500,033,341",
    position: "37000",
    use: "5,000",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 6,
    select: "六年二班",
    markValue: "500,033,341",
    position: "37000",
    use: "5,000",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
  {
      
      
    id: 7,
    select: "六年五班",
    markValue: "500,033,341",
    position: "37000",
    use: "5,000",
    newPrice: 20,
    cur: "30.004",
    cost: "32.453",
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  },
];

const temp = Array.from({
      
       length: 300 }).map((item, index) => {
      
      
  return {
      
      
    id: index,
    select: "三年二班",
    type: 1,
    position: "27000",
    use: "5,000",
    markValue: "500,033.341",
    cur: "30.004",
    cost: "32.453",
    newPrice: 20,
    float: "+18,879.09",
    profit: "-5.45%",
    count: "120,121",
  };
});

const tableDates = ref<Array<any>>(JSON.parse(JSON.stringify(temp)));

const h5TableRef = ref<typeof H5Table | null>(null);

const loading = ref<boolean>(false);
const error = ref<boolean>(false);
const finish = ref<boolean>(false);

const onload = () => {
      
      
  console.log("loading====");
  setTimeout(() => {
      
      
    tableDates.value = tableDates.value.concat(
      Array.from({
      
       length: 100 }).map((item, index) => {
      
      
        return {
      
      
          id: new Date().getTime() + index,
          select: "三年二班",
          type: 1,
          position: "27000",
          use: "5,000",
          markValue: "500,033.341",
          cur: "30.004",
          cost: "32.453",
          newPrice: 20,
          float: "+18,879.09",
          profit: "-5.45%",
          count: "120,121",
        };
      })
    );
    loading.value = false;
  }, 1000);
  // setTimeout(() => {
      
      
  //   error.value = true;
  // }, 1000);
  // setTimeout(() => {
      
      
  //   finish.value = true;
  // }, 1000);
};

const rowClick = (item: any, index: number) => {
      
      
  if (h5TableRef.value) {
      
      
    //第一个参数 即是 设计稿 插槽的高度
    h5TableRef.value.handleDom(60, index);
  }
};

//处理排序
const handleHeadSortClick = (propsKey: string, type: sortStatusType) => {
      
      
  if (h5TableRef.value) {
      
      
    h5TableRef.value.handleDom(60, -1);
  }
  if (type === 0) {
      
      
    tableDates.value.splice(0, tableDates.value.length, ...datas);
    return;
  }
  if (propsKey === "positionAndUse") {
      
      
    if (type === 1) {
      
      
      tableDates.value.sort((a, b) => Number(b.position) - Number(a.position));
    } else {
      
      
      tableDates.value.sort((a, b) => Number(a.position) - Number(b.position));
    }
  }
};

watch(tableDates.value, () => {
      
      
  console.log("watch====", tableDates);
});

const handelSell = () => {
      
      
  console.log("handelSell====");
};
</script>
<style>
body {
      
      
  padding: 0;
  margin: 0 !important;
}
</style>
<style lang="scss" scoped>
.position {
      
      
  font-size: 24px;

  .nameAndMarkValueTitle {
      
      
    display: flex;
  }
  .nameAndMarkValue {
      
      
    padding: 10px;
    .name {
      
      
      display: inline-block;
      position: relative;
      color: #222;
      font-size: 32px;
      .type {
      
      
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        right: -40px;
        display: inline-block;
        font-size: 24px;
        border: 1px solid #ff858d;
        padding: 0 4px;
        color: #ff858d;
      }
    }
    .markValue {
      
      
      color: #999;
      font-size: 24px;
    }
  }

  .positionAndUse {
      
      
    font-size: 28px;
    .position {
      
      
      color: #222;
    }
    .use {
      
      
      color: #999;
    }
  }

  .curAndCost {
      
      
    font-size: 28px;
    .cur {
      
      
      color: #222;
    }
    .cost {
      
      
      color: #999;
    }
  }

  .floatAndProfit {
      
      
    color: red;
  }

  .rowDownMark {
      
      
    width: 100%;
    display: flex;
    height: 60px;
    background-color: #fcfcfc;
    align-items: center;
    .rowDownMark-item {
      
      
      flex-grow: 1;
      color: #309fea;
      text-align: center;
    }
  }
}
</style>



具体使用参考 github 项目中 app.vue 文件

更新日志

2023.9.7 更新

表格性能优化 支持设置最多同时显示多少条数据优化。(开启下拉加载时不支持)
optimized 开启优化 默认时 30+ 40 + 30 超过 100 部分 会动态的计算 是否隐藏掉 核心逻辑在 useHandleScroll 里面

2023.9.27

  1. 修改transform 渲染方式 不经过 vue 派发更新(数据量过大 会有卡顿),直接用原生js 去控制
    具体表现如下图(1000条列表数据)
    在这里插入图片描述
    在左右滑动1s内 有300多ms 消耗到 vue3 的派发更新上,但是我们人为是知道只有组件样式需要改变,所以可以考虑优化 ,将更新的操作用原声js 实现 ,不走vue 响应更新

在这里插入图片描述

vue派发更新时间就省略了,响应速度提高了200多ms。就基本不会卡顿了

  1. 增加 rootValue 配置 默认75(基于rootValue 去将 props 中的 一些 长度单位 传化成 rem) 修复pad 样式问题(保持和 postCssPxToRem 插件配置一致 )
    postCssPxToRem({
    // 自适应,px>rem转换
    rootValue: 75, // 75表示750设计稿,37.5表示375设计稿
    propList: [“*”], // 需要转换的属性,这里选择全部都进行转换
    selectorBlackList: [“norem”], // 过滤掉norem-开头的class,不进行rem转换
    }),
  2. 优化一些参数命名
  3. 将点击 显示操作栏目的操作 更多内置化,便于使用

猜你喜欢

转载自blog.csdn.net/weixin_45485922/article/details/129444600