自学内容网 自学内容网

Vue3.2+TS+arco-design报表封装,所有的报表页面皆可用一个组件进行完成

多功能表格统一封装

在我们进行后台管理系统开发的时候,一定少不了报表的开发,

报表无非就是筛选,统计,分页等功能,但是一旦报表多了起来之后,每次都去开发一个表格,每次都去写一个table,还要加分页,就显得非常没有必要

所以我封装了一个多功能表格,以后每次,只需要导入这个组件,便可以实现报表的统一管理

1.如何使用

我们把筛选项目和表格抽离为两个组件,相关接口调用放在外部,以便统一处理,为什么不把筛选也封装,因为筛选项不一致,会有很多不同的参数,所以筛选项必须抽离出来

筛选的参数使用v-model绑定,方便自动更新参数的数据,相关的page参数使用hooks封装

<template>
  <div class="table">
    <div class="filter">
      <Filter v-model:params="params" @onSearch="onSearch" @onReset="onReset" />
    </div>
    <div class="datainfo">
      <basciTable
        :columns="columns"
        :page="page"
        :loading="loading"
        :data="tableData"
        @updatePageChange="pageChange"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { useTableData, getTimeRange } from '../page-hooks/index.js';
import { getPersonStatistics } from '@/services/fpcs-service';
import Filter from './filter.vue';
import basciTable from '../components/basci-table.vue';

const { page } = useTableData();

const loading = ref(false);

const params = reactive({
  fullName: '', // 人员类型
  idCard: '',
  policeOrgId: '',
  time: getTimeRange(),
  warningTag: 'YKONG',
  hasChild: false,
});

const columns = [
  {
    title: '姓名',
    dataIndex: 'fullName',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '身份证号',
    dataIndex: 'idCard',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '等级',
    dataIndex: 'controlLevelName',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '类别',
    dataIndex: 'personCategoryName',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '状态',
    dataIndex: 'controlStatusName',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '户籍',
    dataIndex: 'domicilePlaceAddress',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '责任',
    dataIndex: 'policeOrgName',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '责任222',
    dataIndex: 'policeResponsibleStaffName',
    ellipsis: true,
    tooltip: true,
  },
];

const tableData = ref([]);
const onSearch = async () => {
  loading.value = true;
  const warningStartTime = params.time[0];
  const warningEndTime = params.time[1];
  const info = {
    ...params,
    ...page.value,
    warningStartTime,
    warningEndTime,
  } as { [key: string]: any };
  delete info.time;
  try {
    const res = await getPersonStatistics(info);
    if (res.code === '200') {
      tableData.value = res.data.records;
      page.value.total = res.data.total;
    }
  } catch (error) {
    console.log(error);
  } finally {
    loading.value = false;
  }
};

const pageChange = (val: any) => {
  page.value = val;
  onSearch();
};

const onReset = () => {
  params.fullName = '';
  params.idCard = '';
  params.warningTag = 'YKONG';
  params.time = getTimeRange();
  params.hasChild = false;
  params.policeOrgId = '';
  onSearch();
};

onMounted(() => {
  onSearch();
});
<style scoped lang="less">
.table {
  height: 100%;
  display: flex;
  flex-direction: column;
  .datainfo {
    flex: 1;
    min-height: 0;
    height: 100%;
    :deep(.arco-tabs) {
      height: 100%;
      display: flex;
      flex-direction: column;
      .arco-tabs-content,
      .arco-tabs-content-list,
      .arco-tabs-pane {
        height: 100%;
      }
    }
  }
}
</style>

page参数的封装
import { ref } from 'vue';
import dayjs from 'dayjs';

// 通用表格数据

export const useTableData = () => {
  const page = ref({
    total: 0,
    current: 1,
    size:15
  })
  return {
    page
  }
}

// 通用时间范围
export const getTimeRange = () => {
  const endTime = dayjs();  // 当前时间
  const startTime = endTime.subtract(1, 'month');  // 当前时间的前一个月
  return [startTime.format('YYYY-MM-DD HH:mm:ss'), endTime.format('YYYY-MM-DD HH:mm:ss')];
}

// 通用时间范围 不带时分秒
export const getTimeRangeInDay = () => {
  const endTime = dayjs();  // 当前时间
  const startTime = endTime.subtract(1, 'month');  // 当前时间的前一个月
  return [startTime.format('YYYY-MM-DD'), endTime.format('YYYY-MM-DD')];
}

筛选参数的封装,使用computed属性之后,使用get和set把相关的参数的更新暴露出去
<template>
  <div class="filter-box">
    <a-form
      ref="formRef"
      :model="filterParams"
      label-align="right"
      auto-label-width
      @keyup.enter.prevenDefault="onSearch"
    >
      <a-row :gutter="8">
        <a-col :span="6">
          <a-form-item label="姓名" show-colon>
            <a-input
              v-model="filterParams.fullName"
              allow-clear
              placeholder="请输入姓名"
            />
          </a-form-item>
        </a-col>
        <a-col :span="5">
          <a-form-item field="idCard" label="身份证号" show-colon>
            <a-input
              v-model="filterParams.idCard"
              allow-clear
              placeholder="请输入"
            />
          </a-form-item>
        </a-col>
        <a-col :span="7">
          <a-form-item label="责任派出所" show-colon>
            <div class="org-select">
              <OrgTreeSelect
                v-model="filterParams.policeOrgId"
                placeholder="请选择责任派出所"
              />
              <a-checkbox v-model="filterParams.hasChild">是否拥有下级</a-checkbox>
            </div>
          </a-form-item>
        </a-col>
        <a-col :span="6">
          <a-form-item field="dateVal" label="预警时间" show-colon>
            <a-range-picker
              v-model="filterParams.time"
              :allow-clear="false"
              show-time
              style="width: 100%"
            />
          </a-form-item>
        </a-col>
        <a-col :span="6">
          <a-form-item label="统计类型" show-colon>
            <a-select
              v-model="filterParams.warningTag"
              :options="type"
              placeholder="请选择责任单位"
            />
          </a-form-item>
        </a-col>
                <a-col :span="18">
          <a-form-item>
            <div class="btn-group">
              <a-button
                type="primary"
                style="margin-left: 5px"
                @click="onSearch"
              >
                <template #icon><icon-search /></template>
                查询
              </a-button>
              <a-button style="margin-left: 5px" @click="onReset">
                <template #icon><icon-refresh /></template>
                重置
              </a-button>
            </div>
          </a-form-item>
        </a-col>
      </a-row>
    </a-form>
  </div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import OrgTreeSelect from '@/view/personnel-back/components/org-tree-select.vue';
const props = defineProps({
  params: {
    default: () => ({}),
  },
});

const type = [
  {
    label: '云控人员',
    value: 'YKONG',
  },
  {
    label: '涉京人员',
    value: 'SRJING',
  },
  {
    label: '涉晋人员',
    value: 'SJIN ',
  },
  {
    label: '历史人员',
    value: '',
  },
];

const emits = defineEmits(['update:params', 'onSearch', 'onReset']);
const filterParams = computed<any>({
  get() {
    return props.params;
  },
  set(val) {
    emits('update:params', val);
  },
});
const onSearch = () => {
  emits('onSearch');
};
const onReset = () => {
  emits('onReset');
};
</script>
<style scoped lang="less">
.filter-box {
  padding: 10px 10px 0 10px;
  :deep(.arco-form) {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    .btn-group {
      display: flex;
      justify-content: flex-end;
      width: 100%;
    }
    .org-select{
      width: 100%;
      display: flex;
      .arco-checkbox{
        width: 150px;
      }
    }
  }
}
</style>
最后是表格的封装,无非就是控制列,控制分页,控制数据更新,控制当页面高度缩小,分页组件会自动上拉
<template>
  <div class="contain-box">
    <div class="tablebox">
      <a-table
        ref="tableRef"
        :bordered="{headerCell:headerBorder}"
        :scroll="{ y: '100%' }"
        :data="data"
        :columns="columns"
        :loading="props.loading"
        :pagination="false"
      ></a-table>
    </div>
    <div v-if="props.isNeedPage" class="pation">
      <a-pagination
        :current="currentPage?.current"
        :total="currentPage?.total"
        :page-size="currentPage?.size"
        :page-size-options="[15, 30, 45, 60, 75]"
        show-total
        show-jumper
        show-page-size
        @change="pageChange"
        @page-size-change="pageSizeChange"
      />
    </div>
  </div>
</template>
<script setup lang="ts">
import { computed } from 'vue';

const props = defineProps({
  columns: {
    type: Array,
    default: () => [],
  },
  page: {
    type: Object,
    default: () => {},
  },
  data: {
    type: Array,
    default: () => [],
  },
  loading: {
    type: Boolean,
    default: false,
  },
  headerBorder: {
    type: Boolean,
    default: false,
  },
  isNeedPage: {
    type: Boolean,
    default: true,
  }
});

const currentPage = computed(() => props.page);

const emits = defineEmits(['updatePageChange']);
const pageChange = (page: number) => {
  emits('updatePageChange', { ...props.page, current: page });
};

const pageSizeChange = (pageSize: number) => {
  emits('updatePageChange', { ...props.page, size: pageSize });
};
</script>
<style scoped lang="less">
.contain-box {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 10px;
  .tablebox {
    flex: 1;
    min-height: 0;
  }
  .pation {
    display: flex;
    justify-content: flex-end;
    padding: 10px;
  }
}
</style>

现在,咱们得多功能的组件就封装好了,还能自动控制需不需要分页

如果有其他的功能,也可以在这个基础上不断新增


原文地址:https://blog.csdn.net/m0_56986233/article/details/143935423

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!