vue3 表格下拉刷新

<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>

 

posted @ 2024-12-27 12:26  abcByme  阅读(2)  评论(0编辑  收藏  举报