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

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

相关推荐
fnd_LN7 分钟前
HTML学习笔记记录---速预CSS(2) 复合属性、盒子模型、边框线、浮动、定位
前端·css·html
城沐小巷11 分钟前
基于Springboot+Vue的仓库管理系统
vue.js·spring boot·后端
对自己不够狠12 分钟前
HarMonyOS使用Tab构建页签
前端·华为·harmonyos
ordinary9015 分钟前
ESLint修正代码规范错误
前端·javascript·代码规范
LCG元15 分钟前
Vue.js组件开发-图片剪裁性能优化最佳方案实例
vue.js
无限大.15 分钟前
理解AJAX与Axios:异步编程的世界
前端·ajax·okhttp
Bigger1 小时前
面试官问:了解哪些最新的 ES 新特性?——这样回答更好!
前端·javascript·ecmascript 6
NullPointerExpection1 小时前
调用企业微信新建日程 API 报 api forbidden 的解决方案
java·前端·企业微信
字节跳动技术团队1 小时前
详解veImageX助力卓特视觉智能、高效生成设计素材
前端·人工智能·后端