首页 > 其他分享 >vue3 表格下拉刷新

vue3 表格下拉刷新

时间:2024-12-27 12:31:59浏览次数:8  
标签:flex const pageSize 表格 vue3 value 刷新 false page

<script setup lang="ts">
import { Empty } from 'ant-design-vue';

export interface TBColumn {
  title: string
  dataIndex: string
  // key: string
  ellipsis?: boolean
  width?: string
  align?: string
  [key: string]: any
}

const props = withDefaults(defineProps<{
  modelValue: any[] // 重要: 外部在使用这个的时候,不要修改,要通过endSuccess这些方法来修改
  columns: TBColumn[]
  defaultPageSize: number
  defaultPage: number
}>(), {
  modelValue: () => [],
  columns: () => [],
  defaultPageSize: 10,
  defaultPage: 1,
});

const emit = defineEmits<{
  (e: 'update:modelValue', val: any[]): void
  (e: 'load', val: { page: number, pageSize: number }): void
  (e: 'clickRow', val: any): void
}>();

const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;

const dataSource = ref<any[]>([]);
const page = ref(props.defaultPage);
const pageSize = ref(props.defaultPageSize);
const noMoreData = ref(false); // 是否有更多数据
const loading = ref(false); // 是否正在加载中

let lockFlag = false; // 锁,用于保证一次加载只能调用一次回调方法

const clickIdx = ref(-1); // 当前点击的数据序号

watch(() => props.modelValue, () => {
  dataSource.value = props.modelValue || [];
}, { immediate: true });

onMounted(() => {
  // dataSource.value = [];
  // page.value = props.defaultPage;
  // pageSize.value = props.defaultPageSize;
  // noMoreData.value = false;
  // loading.value = false;
  nextTick(() => {
    lockFlag = false;
    emit('load', { page: page.value, pageSize: pageSize.value });
  });
});

/**
 * 加载更多
 */
function loadMore() {
  if (noMoreData.value) {
    return;
  }
  if (loading.value) {
    return;
  }
  loading.value = true;
  lockFlag = false;
  emit('load', { page: page.value, pageSize: pageSize.value });
}

function endSuccess(data: any[]) {
  if (lockFlag) {
    return;
  }
  lockFlag = true;
  if (page.value === 1) {
    dataSource.value = data;
  }
  else {
    dataSource.value = [...dataSource.value, ...data];
  }

  if (data.length < pageSize.value) {
    noMoreData.value = true;
  }
  else {
    noMoreData.value = false;
    page.value += 1;
  }
  emit('update:modelValue', [...dataSource.value]);
  loading.value = false;
}

function endError() {
  if (lockFlag) {
    return;
  }
  lockFlag = true;
  loading.value = false;
}

function reload() {
  if (loading.value && page.value === props.defaultPage) {
    return;
  }
  dataSource.value = [];
  page.value = props.defaultPage;
  pageSize.value = props.defaultPageSize;
  noMoreData.value = false;
  loading.value = false;
  clickIdx.value = -1;
  emit('update:modelValue', []);
  lockFlag = false;
  emit('load', { page: page.value, pageSize: pageSize.value });
}

function onClickRow(val: any) {
  if (clickIdx.value === val.index) {
    clickIdx.value = -1;
  }
  else {
    clickIdx.value = val.index;
  }
  emit('clickRow', { ...val, highlighted: clickIdx.value !== -1 });
}

function deselectRow() {
  clickIdx.value = -1;
}

defineExpose({
  endSuccess,
  endError,
  reload,
  deselectRow,
});
</script>

<template>
  <div class="loadmore-table">
    <div class="loadmore-table-head">
      <div
        v-for="(column, index) in columns"
        :key="index"
        class="loadmore-table-th"
        :style="{
          width: column.width ? column.width : 'auto',
          textAlign: column.align ? column.align : 'center',
        }"
      >
        {{ column.title }}
      </div>
    </div>
    <ScrollLoad class="loddmore-table-body" @load-more="loadMore">
      <div v-if="!dataSource.length">
        <a-empty :image="simpleImage" />
      </div>
      <div v-else>
        <div
          v-for="(record, index) in dataSource"
          :key="index"
          class="loadmore-table-tr"
          :class="[clickIdx === index ? 'actived-cls' : '']"
        >
          <div
            v-for="(column, idx) in columns"
            :key="idx"
            class="loadmore-table-td"
            :style="{
              width: column.width ? column.width : 'auto',
              textAlign: column.align ? column.align : 'center',
            }"
            @click="onClickRow({ text: record[column.dataIndex], record, index, column })"
          >
            <div class="loadmore-table-td-con" :class="[column.ellipsis ? 'ellipsis' : '']">
              <slot name="bodyCell" :text="record[column.dataIndex]" :record="record" :index="index" :column="column">
                <a-tooltip>
                  <template #title>
                    {{ record[column.dataIndex] || '' }}
                  </template>
                  {{ record[column.dataIndex] || '' }}
                </a-tooltip>
              </slot>
            </div>
          </div>
        </div>
      </div>
    </ScrollLoad>
  </div>
</template>

<style lang="less" scoped>
.loadmore-table {
    width: 100%;
    height: 100%;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    .loadmore-table-head {
        display: flex;
        background-color: rgba(28, 85, 155, 0.7);
        .loadmore-table-th {
            flex-shrink: 1;
            flex-grow: 1;
            position: relative;
            padding: 7px 5px;
            // height: 34px;
            line-height: 20px;
            font-weight: 500;
            font-size: 15px;
            color: #FFFFFF;
            &::after {
                content: '';
                display: inline-block;
                width: 1px;
                height: 20px;
                background-color: #1C559B;
                position: absolute;
                top: 50%;
                right: 0;
                transform: translateY(-50%);
            }
        }
    }
    .loddmore-table-body {
        flex: 1;
        overflow: auto;
        padding-top: 4px;
        .loadmore-table-tr {
            display: flex;
            .loadmore-table-td {
                flex-shrink: 1;
                flex-grow: 1;
                display: flex;
                align-items: center;
                // height: 40px;
                line-height: 20px;
                padding: 10px 5px;
                font-weight: 400;
                font-size: 15px;
                color: rgba(255, 255, 255, 0.9);
                overflow: hidden;
                .loadmore-table-td-con {
                  flex-grow: 1;
                  flex-shrink: 1;
                }
            }
        }
        .loadmore-table-tr:nth-of-type(2n) {
            background-color: rgba(10, 46, 81, 0.4);
        }
        .ellipsis {
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
    }
}
</style>
<script setup lang="ts">
import dayjs from 'dayjs';
import loadMoreTable from '@/views/components/loadMoreTable.vue';
import type { TBColumn } from '@/views/components/loadMoreTable.vue';
import { postAlarmAlarmSum } from '@/api/modules/backend';

const emit = defineEmits<{
  (e: 'choose', val: any): void
  (e: 'addAlarm', val: any): void
}>();

const tableRef = ref();
const list = ref<any[]>([]);

// 表格选项
const columns: TBColumn[] = [
  {
    title: '设备ID/SN',
    dataIndex: 'deviceSn',
    key: 'deviceSn',
    ellipsis: true,
    width: '48%',
    align: 'left',
  },
  {
    title: '更新时间',
    dataIndex: 'maxAlarmTime',
    key: 'maxAlarmTime',
    ellipsis: true,
    width: '24%',
    align: 'center',
  },
  {
    title: '告警',
    dataIndex: 'alarmCount',
    key: 'alarmCount',
    ellipsis: true,
    width: '14%',
    align: 'center',
  },
  {
    title: '处置',
    dataIndex: 'alarmStatus',
    key: 'alarmStatus',
    ellipsis: true,
    width: '14%',
    align: 'center',
  },
];

async function featchData(pageConfig: { page: number, pageSize: number }) {
  try {
    const params = {
      currentPage: pageConfig.page,
      pageSize: pageConfig.pageSize,
    };
    const res = await postAlarmAlarmSum(params);
    if (res.success) {
      const arr = res.data?.records || [];
      tableRef.value.endSuccess(arr);
    }
    else {
      tableRef.value.endError();
    }
  }
  /* eslint-disable-next-line */
  catch (error) {
    tableRef.value.endError();
  }
}

function onClickRow(val: any) {
  const obj = { ...val.record, highlighted: val.highlighted };
  emit('choose', obj);
  emit('addAlarm', obj);
}

function deselectRow() {
  tableRef.value.deselectRow();
}

function getData() {
  tableRef.value?.reload();
}

defineExpose({
  getData,
  deselectRow,
});
</script>

<template>
  <div class="w-full h-full">
    <loadMoreTable
      ref="tableRef"
      v-model="list"
      :columns="columns"
      :default-page-size="25"
      :default-page="1"
      @load="featchData"
      @click-row="onClickRow"
    >
      <template #bodyCell="{ column, text }">
        <template v-if="column.dataIndex === 'maxAlarmTime'">
          <div>{{ text ? dayjs(text).format('HH:mm:ss') : '' }}</div>
        </template>
        <template v-if="column.dataIndex === 'alarmCount'">
          <div class="text-[#FF4242] font-medium">
            {{ text }}
          </div>
        </template>
        <template v-if="column.dataIndex === 'alarmStatus'">
          <div :class="[text === '1' ? 'tag-gray' : text === '3' ? 'tag-green' : '']">
            {{ text === '1' ? '否' : text === '3' ? '是' : text }}
          </div>
        </template>
      </template>
    </loadMoreTable>
  </div>
</template>

<style lang="less" scoped>
.tag-green {
  width: 28px;
  height: 20px;
  background: #002800;
  background-image: radial-gradient(circle at 50% 100%, #00ff47c9 0%, #00430b00 76%);
  box-shadow: inset 0 0 20px 0 #00ff0361;
  border-radius: 2px;
}

.tag-gray {
  width: 28px;
  height: 20px;
  background: #333333;
  background-image: radial-gradient(circle at 50% 100%, #cdcdcdc9 0%, #43434300 76%);
  box-shadow: inset 0 0 20px 0 #cfcfcf61;
  border-radius: 2px;
}

:deep(.loadmore-table) {
  .loadmore-table-td {
    color: rgba(255, 255, 255, 0.6) !important;
  }
  .actived-cls {
    box-shadow: 0 0 9px 0 #32669C, inset 0 0 21px 0 #4B8ED3;
    background-color: rgba(10, 46, 81, 1) !important;
    border-radius: 4px;
  }
}
</style>

 

标签:flex,const,pageSize,表格,vue3,value,刷新,false,page
From: https://www.cnblogs.com/Byme/p/18635395

相关文章

  • vue3 拖动弹窗
    <scriptsetuplang="ts">import{useDraggable,useMouseInElement}from'@vueuse/core';importtype{CSSProperties}from'vue';interfaceProps{isMask?:booleandialogStyle?:Record<string,any>......
  • 46.在 Vue3 中使用 OpenLayers 双击鼠标显示品牌代言人名片
    在现代Web开发中,地图可视化已成为非常常见的需求之一,尤其是在地理位置相关的应用中。OpenLayers是一款强大的开源JavaScript库,能够帮助开发者在网页中实现各种地图功能。结合Vue3的强大功能,我们可以轻松地将OpenLayers集成到Vue项目中,展示地图上的地理信息。本文......
  • HarmonyOS NEXT开发实战:打造高效上拉刷新与下拉加载组件(一)空页面的设计与实现
    本文正在参加华为鸿蒙有奖征文征文活动前言:在鸿蒙开发的世界中,用户体验至关重要。我在网络上搜寻了一番,发现现有的上拉刷新和下拉加载组件要么功能不全,要么不够优雅。因此,我决定自己动手,打造一套既美观又实用的组件。本系列文章将深入解析如何使用鸿蒙系统组件封装出优秀的上拉......
  • HarmonyOS NEXT开发实战:实现高效下拉刷新与上拉加载组件(二)刷新核心逻辑与空页面集成
    前言:在上一篇文章中,我们深入探讨了如何在HarmonyOS中实现一个功能完备的空页面组件。现在,我们将进入下拉刷新和上拉加载功能的核心逻辑实现。这不仅仅是技术实现,更是对用户体验的深刻理解。本文将详细介绍如何将空页面与下拉刷新、上拉加载逻辑相结合,打造一个既高效又用户友好的......
  • vue3 setup函数内的防抖/节流节流不生效解决方式
    //debounce<template><!--生效--><el-inputv-model="value"@keyup="handelKeyUp"></el-input><!--不生效--><el-inputv-model="value"@keyup="debounce(handelKeyUp2,300)">......
  • Bash 将 Python 列表格式的字符串转为空格分隔字符串的 N 种方法
    MY_LIST='["item1","item2","item3"]'echo"Originallist:$MY_LIST"MY_LIST_CONVERTED=$(echo$MY_LIST|sed's/\[//g'|sed's/\]//g'|sed's/"//g'|sed's/,//......
  • vue3入门教程:ref能否完全替代reactive?
    1.使用场景与数据类型ref:主要用于基本数据类型(如String、Number、Boolean等)。也可以用于对象/数组,但需要通过.value访问。适合单个响应式数据的管理。reactive:主要用于对象类型(如Object、Array)。直接访问属性,不需要.value。适合多个响应式数据的管理。2.访问方式......
  • 大学生期末项目spingboot+vue3实现快递业务项目【附源码】
    目录一.登录界面 二.用户端 2.1用户端寄快递界面2.2寄快递功能2.3取快递功能 ​编辑 2.4查快递功能2.5快递投诉与拦截2.6查询登录者的信息 三.快递员端  3.1查询可视化界面3.2接单与抢单  3.3配送订单3.4快递员查询个人信息三.网点管理员......
  • 在 .NET C# 中的邮件合并过程中操作表格单元格
    TXTextControl中的邮件合并类是一个强大的库,旨在通过将数据合并到模板中来自动创建文档。它充当结构化数据(例如来自数据库、JSON或XML)和动态文档生成之间的桥梁,对于需要自动化文档工作流程的应用程序来说非常有用。从本质上讲,MailMerge类简化了创建专业、数据驱动文档......
  • vxe-table表格自定义表头(必填加*)
    <vxe-tableref="table":show-header-overflow="true":show-overflow="true":data="chooseAttributes"border:header-cell-style="{background:'#......