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

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

vue3-h5-table

介绍

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

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

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

效果

props 说明
minTableHeight 表格最小高度 可选 默认600
rowNum 表格显示几行 可选 默认 6
headerHeight 头部默认高度 可选 默认 60
rowHeight 每行数据的默认高度 默认 100
column 每列数据说明 见下文
tableDatas 表格数据
fixedHeader 是否固定表头 默认true
typescript">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>;
  tableDatas: Array<any>;
  isClick?: boolean; // 是否需要触发行点击事件
  disable?: boolean; // 是否启用下拉加载
  error?: boolean; // 数据加载失败
  loading?: boolean; // 数据处于加载状态
  finish?: boolean; // 数据 是否完全加载
  loadingText?: string; // 加载文案
  errorText?: string; // 失败文案
  finishedText?: string; // 完成文案
  offset?: number; //触发加载的底部距离
  rootValue?: number; //  
};

使用 实例:

<template>
  <div class="position">
    <section style="height: 200px"></section>
    <h5-table
      ref="h5TableRef"
      :column="column"
      :table-datas="tableDatas"
      @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 tableDatas = 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(() => {
    tableDatas.value = tableDatas.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 (type === 0) {
    tableDatas.value.splice(0, tableDatas.value.length, ...datas);
    return;
  }
  if (propsKey === "positionAndUse") {
    if (type === 1) {
      tableDatas.value.sort((a, b) => Number(b.position) - Number(a.position));
    } else {
      tableDatas.value.sort((a, b) => Number(a.position) - Number(b.position));
    }
  }
};

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

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.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. 将点击 显示操作栏目的操作 更多内置化,便于使用

2023.10.7

处理了 屏幕尺寸变化(一般生产环境 用户屏幕尺寸不会发生变化) 引发一些问题

2023.10.10

表头 固定优化 处理ios fixed 滑动问题

2023.10.20

cell组件 改为函数组件 加快渲染速度

2023.10.31

解决cloumn修改宽带导致表格宽度改变引发的空白问题

2024.1.8

修复滚动过快 无法触发load事件问题

转载请说明出处内容投诉
CSS教程_站长资源网 » 一个 适用 vue3 ts h5移动端 table组件

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买