vite + vue3 + ts 搭建一套自己的后台管理系统 (3)

1,154 阅读4分钟

前言

上一篇本专栏的文章更新还是很久以前,因为各种原因就搁置了,最近腾出了点时间把这篇文章补一下,也算是巩固自己的所学。本篇文章我们来封装几个常用的组件。

项目初始化

之前的文章有聊过如何进行项目初始化配置,本项目我们使用最简洁的初始化配置,采用 vite 添加 router,引入Elememt plus 即可

Table 二次封装

如果表格列比较多,我们就需要写很多个el-table-column 标签来匹配匹配数据

image.png 我们对 el-table 组件进行二次封装,希望达到添加表头和表格数据即可实现表格的功能。来看下封装后使用实例与效果

image.png

image.png 如何实现,我们来看下 myTable 组件。最基础的表格,需要的传递的数据为表格表头,所以我们需要将这两个参数通过 props 进行定义

  <script setup>
   defineProps({
      data:{
        type:Array,
        default:()=>[
            // {
            //   date: '2016-05-03',
            //   name: 'Tom',
            //   address: 'No. 189, Grove St, Los Angeles',
            // },
        ]
      },
      columns:{
        type:Array,
        default:()=>[
          // {
          //   prop:'name',
          //   label: '名称',
          // }
        ]
      }
   })
  </script>

这里columns 中 prop的值需要与data 中的属性name 对应,不然无法取到对应的属性值。在el-table 中我们以 el-table-column 为载体将columns 遍历渲染表格

<template>
    <el-table :data="data" stripe style="width: 100%">
        <el-table-column v-for="item in columns" :id="item.prop"
            :prop="item.prop" 
            :label="item.label">
        </el-table-column>
    </el-table>
</template>

但这样的话就限制了我们修改展示数据的能力以及数据获取能力,我们可以可以通过slot 作用域插槽来将数据暴露出去使用。如果有小伙伴不是太了解插槽的使用方法,可以看我之前的文章juejin.cn/post/720709… 通过添加slot 将row 与$index 同时将我们的表头数据也抛出。

<template>
    <el-table :data="data" stripe style="width: 100%" v-bind="$attrs">
        <el-table-column v-for="item in columns" :id="item.prop"
            :prop="item.prop" 
            :label="item.label">
           <template #default="scope">
             <slot :columns="item" :row="scope.row" :$index="scope.$index">{{ scope.row[item.prop] }}</slot>
           </template>
        </el-table-column>
    </el-table>
</template>

在父组件中,我们可以通过columns.prop得知当前为哪列,如果是opetation 我们可以插入按钮,通过 scope.row获取当前行的数据

<template>
  <div>
    <myTable :data="tableData" :columns="columns">
       <template #default="scope">
        <div v-if="scope.columns.prop=='opetation'" @click="handelClick(scope.row)">
           <el-button>编辑</el-button>
        </div>
       </template>
    </myTable>
  </div>
</template>

<script lang="ts" setup>
import myTable from '../components/my-table.vue';
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:1,
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:0,
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:0,
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:1,
  },
]

const columns=[
          {
            prop:'name',
            label: '名称',
            width:120
          },
          {
            prop:'date',
            label: '日期',
            minWidth:'180'
          },
          {
            prop:'address',
            label: '地点',
          },
          {
            prop:'status',
            label: '状态',
          },
          {
            prop:'opetation',
            label:'操作'
          }
]
const handelClick=(row)=>{
  console.log(row)
}
</script>

image.png

我们来继续完善,可以通过添加属性来判断是否需要序号列和多选列,通过 v-bind="$attrs" 来实现属性透传。

image.png

当然我们也可以通过具名插槽在我们的父组件中使用,实现效果和用属性来控制是一样的。

image.png

image.png

v-bind="$attrs"

对于封装el-table的自定义组件来说,v-bind="$attrs"的作用主有以下几点意义:

  1. 属性透传:v-bind="$attrs"可以将父组件传递给自定义组件的所有属性直接传递给el-table,实现属性的透传。这样可以避免在封装组件中一个一个地定义props,减少代码量,提高开发效率。

  2. 动态属性支持:通过v-bind="$attrs",自定义组件可以支持动态的属性传递。父组件可以根据需要动态地传递属性给自定义组件,而不需要在封装组件中预先定义所有可能的属性。

  3. 保持一致性:v-bind="attrs"可以保持自定义组件与eltable之间的属性一致性。如果在封装组件中定义了props来接收属性,可能会导致自定义组件与eltable之间的属性不一致,从而产生一些意想不到的问题。而使用vbind="attrs"可以保持自定义组件与el-table之间的属性一致性。如果在封装组件中定义了props来接收属性,可能会导致自定义组件与el-table之间的属性不一致,从而产生一些意想不到的问题。而使用v-bind="attrs"可以确保自定义组件与el-table之间的属性保持一致。

总之,v-bind="$attrs"的作用在于简化代码、支持动态属性传递,并保持自定义组件与el-table之间的属性一致性。这样可以使封装组件更加灵活、易用,并且不会改变el-table的基本结构和功能

在我们自定义中的组件中,el-table 的其他功能是不受影响的。我们来测试一下。

//父组件
<template>
  <div>
    <myTable :data="tableData" :columns="columns" :selection="true" :border="true" @selection-change="selectionChange">
       <template #selection>
        <el-table-column type="selection"></el-table-column>
        <el-table-column type="index" label="序号" width="60"></el-table-column>
       </template>
       <template #default="scope">
        <div v-if="scope.columns.prop=='opetation'" @click="handelClick(scope.row)">
           <el-button>编辑</el-button>
        </div>
       </template>

    </myTable>
  </div>
</template>

<script lang="ts" setup>
import myTable from '../components/my-table.vue';
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:1,
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:0,
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:0,
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    status:1,
  },
]

const columns=[
          {
            prop:'name',
            label: '名称',
            width:120
          },
          {
            prop:'date',
            label: '日期',
            minWidth:'180'
          },
          {
            prop:'address',
            label: '地点',
          },
          {
            prop:'status',
            label: '状态',
          },
          {
            prop:'opetation',
            label:'操作'
          }
]
const handelClick=(row)=>{
  console.log(row)
}
const selectionChange=(row)=>{
  console.log(row)
}

</script>

//子组件
<template>
    <el-table :data="data" stripe style="width: 100%" v-bind="$attrs">
        <el-table-column v-if="selection" type="selection"></el-table-column>
        <el-table-column v-if="order" type="index" label="序号" width="60"></el-table-column>
        <el-table-column v-for="item in columns" :id="item.prop"
            :prop="item.prop" 
            :label="item.label">
           <template #default="scope">
             <slot :columns="item" :row="scope.row" :$index="scope.$index">{{ scope.row[item.prop] }}</slot>
           </template>
        </el-table-column>
    </el-table>
</template>
  <script setup>
   defineProps({
      data:{
        type:Array,
        default:()=>[]
      },
      columns:{
        type:Array,
        default:()=>[]
      },
      selection:{
        type:Boolean,
        default:false
      },
      order:{
        type:Boolean,
        default:false
      }
   })
  </script>

image.png

整体思路就是通过作用域插槽将父组件传给子组件的数据再暴露给父组件使用。感兴趣的小伙伴可以自己再进行组件完善。

最后

也是在开发中学习,比如之前 $attrs 属性也没有使用过,只知道有这么个东西,现在算是有了一点点的了解。下一篇试下自己实现一个简单的 el-table