第二弹来了,不知道有多少人是看过我的第一篇文章的,今天本来是没想更新的,但是现在项目正在验收期准备上线,闲着还不如来发发文。虽然这两天可能会高产,下一次高产就不知道是什么时候了。话不多说,先上图。
上面就是效果图了,基于vue3+element-plus觉得还行的可以继续看下去
源码
src\components\MTable\index.vue
<template>
<div class="table-list-root">
<el-table
ref="mtable"
v-loading="loading"
v-bind="$attrs"
:border="border"
:header-row-style="defaultHeaderRowStyle"
:header-cell-style="defaultHeaderCellStyle"
:cell-style="defaultCellStyle"
class="table-list__main"
>
<template #empty>
<el-empty description="暂无数据" />
</template>
<el-table-column v-for="filed in columns" :key="filed.prop" v-bind="filed">
<template v-if="filed.prop && $slots[filed.prop]" #default="scope">
<slot :name="filed.prop" :row="scope.row" />
</template>
</el-table-column>
</el-table>
<div v-if="pagination" class="table-list__footer">
<el-pagination
v-bind="paginationProps"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { type PropType, ref, toRefs } from 'vue';
import useTable from './uses/useTable';
import usePagination from './uses/usePagination';
defineOptions({
inheritAttrs: false
});
type filed = {
prop?: string;
[propName: string]: any;
};
const props = defineProps({
loading: {
type: Boolean,
default: false
},
// 表格列
columns: {
type: Array as PropType<filed[]>,
default: () => []
},
border: {
type: Boolean,
default: true
},
pagination: {
type: Object,
default: () => null
}
});
// 这里其实可以使用defineModel,我这样写只是为了让习惯vue2的稍微清晰一点
const emit = defineEmits(['update:pagination']);
const mtable = ref(null);
const { pagination } = toRefs(props);
const { defaultHeaderRowStyle, defaultHeaderCellStyle, defaultCellStyle } = useTable();
const { paginationProps, handleSizeChange, handleCurrentChange } = usePagination(emit, pagination);
</script>
src\components\MTable\uses\useTable.ts
这个文件其实就是一些自定义样式,不想分开写的也可以写在一起
import { ref } from 'vue';
export default function useTable() {
const defaultHeaderRowStyle = ref({
borderRadius: '4px 4px 0px 0px',
backgroundColor: '#F8F8F8',
padding: '0',
height: '54px',
lineHeight: '54px',
});
const defaultHeaderCellStyle = ref({
backgroundColor: '#F8F8F8',
color: '#282828',
fontSize: '14px',
padding: '0',
height: '54px',
});
const defaultCellStyle = ref({
color: '#323233',
fontSize: '14px',
lineHeight: '20px',
});
return {
defaultHeaderRowStyle,
defaultHeaderCellStyle,
defaultCellStyle,
};
}
src\components\MTable\uses\usePagination.ts
这里是关于分页的处理
import { computed } from 'vue';
export default function usePagination(emit:any, pagination:any) {
const paginationProps = computed(() => {
const paginationVal = pagination.value
return {
background: true,
layout: 'prev, pager, next, sizes, jumper',
currentPage: paginationVal.page,
pageSize: paginationVal.size,
...paginationVal,
}
});
const handleSizeChange = (pageSize:number | string) => {
emit('update:pagination', {
...pagination.value,
size: pageSize,
});
};
const handleCurrentChange = (currentPage:number | string) => {
emit('update:pagination', {
...pagination.value,
page: currentPage,
});
};
return {
paginationProps,
handleSizeChange,
handleCurrentChange,
};
}
关于组件的源码其实就这些了,下面简单写个使用示例
<template>
<MTable
v-model:pagination="pagination"
:loading="loading.tableLoading"
:data="tableData"
:columns="TABLECOLUMN"
@update:pagination="getTableData"
>
<template #sent_staff="{ row }">
自定义成员列xxxxxx
</template>
<template #action="{ row }">
<el-button type="primary" link>预览</el-button>
<el-button type="primary" link>删除</el-button>
<el-button type="primary" link>再次发送</el-button>
</template>
</YzTable>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import MTable from '@/components/MTable/index.vue';
const searchQuery = ref({});
const pagination = ref({
page: 1,
size: 10,
total: 0
});
const tableData = ref([]);
const loading = ref({
tableLoading: false,
});
onMounted(() => {
handleQuery();
});
// 点击查询
const handleQuery = async () => {
pagination.value.page = 1;
await getTableData();
};
// 请求表格数据
const getTableData = async () => {
const dataValue = {
...searchQuery.value,
page: pagination.value.page,
page_size: pagination.value.size
};
loading.value.tableLoading = true;
try {
const data = await xxxxxxxx(dataValue);
tableData.value = data.data;
pagination.value.total = data.total;
} finally {
loading.value.tableLoading = false;
}
};
const TABLECOLUMN = [
{
label: '序号',
type: 'index',
width: 70
},
{
label: '计划名称',
prop: 'name'
},
{
label: '发送类型',
prop: 'send_type',
formatter: (row: any) => planSendType[row.send_type]
},
{
label: '发送时间',
prop: 'send_time',
minWidth: 130,
formatter: (row: any) => Moment.format(Number(row.send_time))
},
{
label: '创建人',
prop: 'creator.name'
},
{
label: '所属部门',
prop: 'creator',
formatter: (row: any) => row.creator?.department?.name || '-'
},
{
label: '已发送成员',
prop: 'sent_staff'
},
{
label: '未发送成员',
prop: 'unsent_staff'
},
{
label: '已送达客户',
prop: 'delivered'
},
{
label: '未送达客户',
prop: 'not_delivered'
},
{
label: '创建时间',
prop: 'created_at',
minWidth: 130,
formatter: (row: any) => Moment.format(Number(row.created_at))
},
{
label: '状态',
prop: 'plan_status',
formatter: (row: any) => planStatusType[row.plan_status]
},
{
label: '操作',
prop: 'action',
fixed: 'right',
width: 250
}
];
</script>
以上只是简单的示例,如果结合我上一篇文章,那么整个列表页面,除了表格顶部那些个操作按钮,就全部都是数据驱动了,至于按钮那一块的封装,过于简单,并不准备放出来,如果需要的话可以留言。
还是老样子,除了那几个固定的key必须,其他都不是必须的,并且支持全部table的属性,目前的缺陷是无法自定义表格header,这个目前还没有好的解决方案。总体来说够用了,那种有header定制的也不多,或者整个项目都差不多,可以直接写在组件里面
这一期写到这里也就差不多了,有什么意见建议的欢迎提出。