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