前端工作室专注前端开发
 博客首页

表格组件的简单封装

2025-03-18 14:02:42
JS基础与深入
12
文章图片

将表单改写成通过数组形式渲染,直接上代码

表格组件的简单封装

将表单改写成通过数组形式渲染,直接上代码

Alt text

父组件代码


<template>
  这里是表格示例
  <LeeTable
    :table-data="tableData"
    :table-columns="columns"
    :show-action="true"
    :show-pagination="true"
    :total="100"
    @page-change="handlePageChange"
  >
    <!-- 自定义列内容 -->
    <template #status="{ row }">
      <el-tag :type="row.status === 'success' ? 'success' : 'danger'">
        {{ row.status }}
      </el-tag>
    </template>
    <!-- 自定义操作列内容 -->
    <template #address="{ row }">
      <el-input v-if="row._isEdit" v-model="row.address"></el-input>
      <div v-else>{{ row.address }}</div>
    </template>
  </LeeTable>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
const tableData = reactive([{
    id: 1,
    name: '张三',
    age: 20,
    status: 'success',
    address: '北京'
  },{
    id: 2,
    name: '李四',
    age: 20,
    status: 'danger',
    address: '上海'
  },{
    id: 3,
    name: '王五',
    age: 20,
    status: 'success',
    address: '广州'
  },{
    id: 4,
    name: '赵六',
    age: 20,
    status: 'danger',
    address: '深圳'
  }])
const columns = reactive([{
    prop: 'id',
    label: 'ID',
    width: 100  
},{
    prop: 'name',
    label: '姓名',
    width: 100
},{
    prop: 'age',
    editType: 'ElInputNumber',
    label: '年龄',
    editProps: { min: 0, max: 100 },
    width: 180
},{
    prop: 'status',
    label: '状态',
    renderType: 'slot',
    width: 100
},{
    prop: 'address',
    label: '地址插槽',
    renderType: 'slot',
    editType: 'slot',
    width: 100
},{
    prop: '',
    renderType: 'operation',
    buttons: [{
      label: '编辑',
      type: 'primary',
      onClick: (row: any) => {
        console.log(row)
        row._isEdit = true
      }
    },{
      label: '删除',
      type: 'danger'
    }],
    label: '操作',
    width: 200
}])
const handlePageChange = (page: number) => {
  console.log(page)
}
</script>

子组件代码


<template>
  <el-table
    :data="tableData"
    style="width: 100%"
    v-loading="loading"
    :border="border"
    :stripe="stripe"
    v-on="$attrs"
  >
    <el-table-column
      v-for="item in tableColumns"
      :fixed="item.fixed"
      :prop="item.prop"
      :label="item.label"
      :width="item.width || 180"
      :key="item.prop"
    >
      <template #default="{row}">
        <slot v-if="item.renderType === 'slot'" :name="item.prop" :row="row">
        </slot>
        <template v-else-if="!row._isEdit || !item.editType">
          <span v-if="item.renderType !== 'operation'">{{ row[item.prop] }}</span>
          <!-- 操作列 -->
          <span v-else>
            <el-button v-for="(btn, index) in item.buttons" :key="index" :type="btn.type" :size="btn.size" :plain="btn.plain" @click="btn.onClick(row)">
              {{ btn.label }}
            </el-button>
          </span>
        </template>
        <!-- 编辑内容 -->
        <template v-else>
          <slot v-if="item.editType === 'slot'" :name="item.prop" :row="row">
          </slot>
          <component :is="handleComponent(item.editType)" v-model="row[item.prop]" v-bind="item.editProps"></component>
        </template>
      </template>
    </el-table-column>
  </el-table>
 
  <!-- 分页 -->
  <el-pagination
    v-if="showPagination"
    class="mt-4"
    background
    layout="prev, pager, next"
    :total="total"
    :page-size="pageSize"
    :current-page="currentPage"
    @current-change="handlePageChange"
  />
</template>
<script lang="ts" setup>
import { type TableData, type TableColumns } from "@/types/types"
import { ElInputNumber, ElInput, ElSelect, ElDatePicker, ElRadioGroup, ElSwitch, ElCheckboxGroup, ElCascader } from 'element-plus';
interface Props {
  tableData: TableData
  tableColumns: TableColumns
  loading?: boolean
  border?: boolean
  stripe?: boolean
  showPagination?: boolean
  total?: number
  pageSize?: number
  currentPage?: number
}
const props = withDefaults(defineProps<Props>(), {
  tableData: () => [],
  tableColumns: () => [],
  loading: false,
  border: true,
  stripe: true,
  showPagination: false,
  total: 0,
  pageSize: 10,
  currentPage: 1
})
const emit = defineEmits(['page-change'])
const handlePageChange = (page: number) => {
  emit('page-change', page)
}
const handleComponent = (renderType:string) => {
  switch (renderType) {
    case 'ElInput':
    return ElInput
    case 'ElInputNumber':
    return ElInputNumber
    case 'ElSelect':
    return ElSelect
    case 'ElDatePicker':
    return ElDatePicker
    case 'ElRadioGroup':
    return ElRadioGroup
    case 'ElSwitch':
    return ElSwitch
    case 'ElCheckboxGroup':
    return ElCheckboxGroup
    case 'ElCascader':
    return ElCascader
    default:
      return renderType
  }
}
</script>

虚拟表格组件简单封装

Alt text

父组件

<template>
  这里是表格示例
  <leeVxeTable :table-data="tableData" :columns="columns">
    <!-- 自定义列内容 -->
    <template #status="{ row }">
      <el-tag :type="row.status === 'success' ? 'success' : 'danger'">
        {{ row.status }}
      </el-tag>
    </template>
    <!-- 自定义操作列内容 -->
    <template #address="{ row }">
      <el-input v-if="row._isEdit" v-model="row.address"></el-input>
      <div v-else>{{ row.address }}</div>
    </template>
  </leeVxeTable>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
const tableData = reactive([
  {
    id: 1,
    name: '张三',
    age: 20,
    status: 'success',
    address: '北京',
    height: 175,
    weight: 120
  },
  {
    id: 2,
    name: '李四',
    age: 20,
    status: 'danger',
    address: '上海',
    height: 180,
    weight: 110
  },
  {
    id: 3,
    name: '王五',
    age: 20,
    status: 'success',
    address: '广州'
  },
  {
    id: 4,
    name: '赵六',
    age: 20,
    status: 'danger',
    address: '深圳'
  }
])
const columns = reactive([
  {
    field: 'id',
    title: 'ID',
    width: 100
  },
  {
    field: 'name',
    title: '姓名',
    width: 100
  },
  {
    field: 'age',
    editType: 'ElInputNumber',
    title: '年龄',
    editProps: { min: 0, max: 100 },
    width: 180
  },
  {
    field: 'status',
    title: '状态',
    renderType: 'slot',
    width: 100
  },
  {
    field: 'address',
    title: '地址插槽',
    renderType: 'slot',
    editType: 'slot',
    width: 100
  },
  {
    field: 'heightAndWeight',
    title: '体型',
    children: [
      {
        field: 'height',
        title: '身高',
        width: 100
      },
      {
        field: 'weight',
        title: '体重',
        width: 100
      }
    ],
    width: 100
  },
  {
    field: '',
    renderType: 'operation',
    buttons: [
      {
        label: '编辑',
        type: 'primary',
        onClick: (row: any) => {
          console.log(row)
          row._isEdit = true
        }
      },
      {
        label: '删除',
        type: 'danger'
      }
    ],
    title: '操作',
    width: 200
  }
])
</script>

子组件

<template>
  <div>
    <vxe-table
      border
      show-overflow
      height="600"
      :scroll-y="{enabled: true, gt: 0}"
      :data="tableData">
      <template v-for="(item, index) in columns" :key="index">
        <template v-if="item.children">
          <vxe-colgroup :title="item.title">
            <vxe-column v-for="(item2, index2) in item.children" :type="item2.type" :width="item2.width" :key="index2" :field="item2.field" :title="item2.title">
              <template #default="{ row }">
                <slot v-if="item2.renderType === 'slot'" :name="item2.field" :row="row">
                </slot>
                <template v-else-if="!row._isEdit || !item2.editType">
                  <span v-if="item2.renderType !== 'operation'">{{ row[item2.field] }}</span>
                  <!-- 操作列 -->
                  <!-- <span v-else>
                    <el-button v-for="(btn, index) in item.buttons" :key="index" :type="btn.type" :size="btn.size" :plain="btn.plain" @click="btn.onClick(row)">
                      {{ btn.label }}
                    </el-button>
                  </span> -->
                </template>
                <!-- 编辑内容 -->
                <template v-else>
                  <slot v-if="item2.editType === 'slot'" :name="item2.field" :row="row">
                  </slot>
                  <component :is="handleComponent(item.editType)" v-model="row[item2.field]" v-bind="item2"></component>
                </template>
              </template>
            </vxe-column>
          </vxe-colgroup>
        </template>
        <template v-else>
          <vxe-column :type="item.type" :width="item.width" :key="index" :field="item.field" :title="item.title">
            <template #default="{ row }">
                <slot v-if="item.renderType === 'slot'" :name="item.field" :row="row">
                </slot>
                <template v-else-if="!row._isEdit || !item.editType">
                  <span v-if="item.renderType !== 'operation'">{{ row[item.field] }}</span>
                  <!-- 操作列 -->
                  <span v-else>
                    <el-button v-for="(btn, index) in item.buttons" :key="index" :type="btn.type" :size="btn.size" :plain="btn.plain" @click="btn.onClick(row)">
                      {{ btn.label }}
                    </el-button>
                  </span>
                </template>
                <!-- 编辑内容 -->
                <template v-else>
                  <slot v-if="item.editType === 'slot'" :name="item.field" :row="row">
                  </slot>
                  <component :is="handleComponent(item.editType)" v-model="row[item.field]" v-bind="item"></component>
                </template>
              </template>
          </vxe-column>
        </template>
      </template>
    </vxe-table>
  </div>
</template>
<script lang="ts" setup>
import { type TableData, type TableColumns } from "@/types/types"
import { ElInputNumber, ElInput, ElSelect, ElDatePicker, ElRadioGroup, ElSwitch, ElCheckboxGroup, ElCascader } from 'element-plus';
interface Props {
  tableData: TableData
  columns: TableColumns
}
const props = withDefaults(defineProps<Props>(), {
  tableData: () => [],
  columns: () => [],
})
const handleComponent = (renderType:string) => {
  switch (renderType) {
    case 'ElInput':
    return ElInput
    case 'ElInputNumber':
    return ElInputNumber
    case 'ElSelect':
    return ElSelect
    case 'ElDatePicker':
    return ElDatePicker
    case 'ElRadioGroup':
    return ElRadioGroup
    case 'ElSwitch':
    return ElSwitch
    case 'ElCheckboxGroup':
    return ElCheckboxGroup
    case 'ElCascader':
    return ElCascader
    default:
      return renderType
  }
}
// interface RowVO {
//   id: number
//   name: string
//   role: string
//   sex: string
//   age: number
//   address: string
// }
// const tableData = ref<RowVO[]>([])
// // 模拟行数据
// const loadList = (size = 200) => {
//   const dataList: RowVO[] = []
//   for (let i = 0; i < size; i++) {
//     dataList.push({
//       id: 10000 + i,
//       name: 'Test' + i,
//       role: 'Developer',
//       sex: '男',
//       age: 40,
//       address: 'Address'
//     })
//   }
//   tableData.value = dataList
// }
// loadList(500)
</script>

githuab地址为:'https://github.com/leezhenwang/vue3-TypeScript-demo.git/

小恐龙

专注于WEB和移动前端开发

5年以上经验业余讲师被访问基金爱好者
社交帐号