vue3组件封装系列-表格及分页

第二弹来了,不知道有多少人是看过我的第一篇文章的,今天本来是没想更新的,但是现在项目正在验收期准备上线,闲着还不如来发发文。虽然这两天可能会高产,下一次高产就不知道是什么时候了。话不多说,先上图。

上面就是效果图了,基于vue3+element-plus觉得还行的可以继续看下去

源码

src\components\MTable\index.vue

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

这个文件其实就是一些自定义样式,不想分开写的也可以写在一起

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

这里是关于分页的处理

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,
  };
}

关于组件的源码其实就这些了,下面简单写个使用示例

vue 复制代码
<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定制的也不多,或者整个项目都差不多,可以直接写在组件里面

这一期写到这里也就差不多了,有什么意见建议的欢迎提出。

相关推荐
Leyla14 分钟前
【代码重构】好的重构与坏的重构
前端
影子落人间17 分钟前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
世俗ˊ42 分钟前
CSS入门笔记
前端·css·笔记
子非鱼92142 分钟前
【前端】ES6:Set与Map
前端·javascript·es6
6230_1 小时前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人1 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛1 小时前
HTML 揭秘:HTML 编码快速入门
前端·html
啥子花道1 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
麒麟而非淇淋1 小时前
AJAX 入门 day3
前端·javascript·ajax
茶茶只知道学习2 小时前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css