【vue3组件封装】Table和TableColumn表格

功能

  • 支持横向分布和纵向分布
  • 可自定义内容(通过插槽)
  • 可修改基本样式(如宽度、高度、文本排列)

效果

写的很简陋哈,只实现了基本功能,还有许多地方需要完善。下面分别为常用的三种模式:

  • 纵向表格(正常情况)
  • 横向表格
  • 可操作表格
    在这里插入图片描述

使用

<template>
    <s-table :dataSource="state.dataSource" layout="column">
         <s-table-column prop="id" label="ID"/>
         <s-table-column prop="name" label="名字"/>
         <s-table-column prop="age" label="年龄"/>
         <s-table-column prop="phone" label="电话号码" />
         <s-table-column prop="hobby" label="爱好"/>
    </s-table>

    <br/>

    <s-table :dataSource="state.dataSource" :header-style="{textAlign:'center',width:'100px'}" align="center">
         <s-table-column prop="name" label="名字"/>
         <s-table-column prop="age" label="年龄"/>
         <s-table-column prop="phone" label="电话号码"/>
         <s-table-column prop="hobby" label="爱好"/>
    </s-table>
    
	 <s-table :dataSource="state.dataSource" layout="column">
	         <s-table-column prop="id" label="ID"/>
	         <s-table-column prop="name" label="名字"/>
	         <s-table-column prop="age" label="年龄"/>
	         <s-table-column prop="phone" label="电话号码">
	          <template #default="defaultProps">
	               <s-input
	                  :value="defaultProps.value"
	                  name="phone"
	                  :rules="[
	                     { type: 'required', message: '电话号码不能为空' },
	                  ]"
	                  @update="update($event, defaultProps.data.id)"
	               />
	            </template>
	         </s-table-column>
	         <s-table-column prop="hobby" label="爱好"/>
	</s-table>

 </template>

<script lang="ts" setup>
import { reactive } from "vue"
import { STable, STableColumn, SInput } from "@/components"

const state = reactive({
  dataSource: [
    {
      id: 1,
      name: "小明",
      age: 98,
      hobby: "游泳",
      phone: "123456"
    },
    {
      id: 2,
      name: "小红",
      age: 55,
      hobby: "跑步",
      phone: "954623"
    },
    {
      id: 3,
      name: "小黑",
      age: 32,
      hobby: "打羽毛球",
      phone: "654513"
    }
  ]

})

const update = (params:BaseForm, id:number) => {
  state.dataSource = state.dataSource.map((item) => {
    if (item.id === id) {
      return {
        ...item,
        [params.name]: params.value
      }
    } else {
      return item
    }
  })
}
</script>

Types

// Table 方向
type TableLayout = "horizon" | "column";

// Table 支持的头部样式
interface HeaderStyle {
   width?: string;  // 头部宽度
   height?: string; // 头部高度
   textAlign?: TableAlign;  // 头部文本排列方式
}

// 真实运用到dom的头部样式
interface RealHeaderStyle extends HeaderStyle {
   minWidth?: string;
   minHeight?: string;
   lineHeight?: string;
   flex?: number;
   display?: string;
   alignItems?: string;
   justifyContent?: string;
   [index: string]: string | number | undefined;
}

// Table 文本方向
type TableAlign = "left" | "center" | "right"; 


// 每一条数据的格式(根据需要自定义)
type TableCell = {
   id: number | string;
   [index: string]: any;
};

封装

写的有点烂,当时想着只改个layout属性就能改变表格方向,于是在样式的使用上花了很多时间,也写了许多样式的逻辑判断,挺绕。如果有其他更优雅的方案欢迎评论区提出。

STable

<script lang="ts">
import { h, PropType, reactive, watchEffect } from "vue"
import { TableCell, HeaderStyle, TableAlign } from "@types"

export default {
  name: "STable",
  props: {
    // 数据源
    dataSource: {
      type: Array as PropType<TableCell[]>,
      default: () => []
    },
    // 头部样式
    headerStyle: Object as PropType<HeaderStyle>,
    // 表格展示方向
    layout: {
      type: String,
      default: "horizon"
    },
    // 表格体文本排列方式
    align: {
      type: String as PropType<TableAlign>,
      default: "left"
    }
  },

  setup (props, context) {
    if (!context.slots || !context.slots.default) return null
    let slots = reactive<any[]>([])
    
    // 监听父组件传来的属性变化,让子组件跟着变化
    watchEffect(() => {
      if (!context.slots || !context.slots.default) return null
      slots = context.slots.default().map((slot) => ({
        ...slot,
        props: {
          ...props,
          ...slot.props
        }
      }))
    })

	// 根据不同的方向应用不同的样式
    if (props.layout === "column") {
      return () =>
        h(
          "div",
          {
            className: "s-table table-layout-column"
          },
          slots
        )
    } else {
      return () =>
        h(
          "div",
          {
            className: "s-table-wrap"
          },
          [h("div", { className: "s-table table-layout-horizon" }, slots)]
        )
    }
  }
}
</script>

<style lang="less">
	// 一开始用flex布局来处理正常情况下的表格样式(column)
	// 后面发现用在flex在横向时不管用,改用了grid布局,所以写的不是很统一,根据需求改哈~
   .s-table-wrap {
      overflow: auto;
      .s-table {
         border-radius: @bigRadius;
         &.table-layout-horizon {
            display: grid;
            .table-columns {
               display: flex;
               .table-cell {
                  flex: 1;
               }
               // 从第二行开始
               &:nth-child(n + 2) {
                  .table-title,
                  .table-cell {
                     border-right: 1px dotted @borderColor;
                     border-bottom: 1px dotted @borderColor;
                  }
                  .table-title {
                     background-color: @default;
                  }
               }
               &:first-child {
                  .table-title {
                     color: @white;
                  }
                  .table-cell {
                     background-color: @default;
                     border-bottom: 1px dotted @borderColor;
                     &:nth-child(n + 3) {
                        border-left: 1px dotted @borderColor;
                     }
                  }
               }
            }
         }
      }
   }
   .s-table {
      overflow-x: auto;
      border-radius: @bigRadius;
      &.table-layout-column {
         display: flex;
         .table-columns {
            display: flex;
            flex-direction: column;
            // flex-basis: 100px;
            &:nth-child(n + 3) {
               .table-cell {
                  border-top: 1px dotted @borderColor;
               }
            }
            .table-title {
               white-space: nowrap;
               border-bottom: 1px dotted @borderColor;
               background-color: @default;
               position: relative;
            }
            .table-cell {
               flex: 1;
            }
         }
      }
   }
</style>

STableColumn

<template>
    <ul class="table-columns" :key="prop" :style="{...culumnStyle}">
        <li :class="['table-title',textAlign]" :key="prop" :style="{...titleStyle}">{{label}}</li>
        <li :class="['table-cell',textAlign]" v-for="data in dataSource" :key="data.id">
          <slot name="default" :data="data" :value="data[prop]">
            {{data[prop]}}
          </slot>
        </li>
    </ul>
</template>

<script lang='ts'>
import { PropType, ref } from "vue"
import { TableCell, HeaderStyle, RealHeaderStyle, TableLayout, TableAlign } from "@types"

export default {
  props: {
    prop: {
      type: String,
      default: ""
    },
    label: {
      type: String,
      require: true
    },
    width: {
      type: String,
      default: ""
    }, // 列的宽度
    align: {
      type: String as PropType<TableAlign>,
      default: "left"
    }
    
    //继承父组件表格传来的
    dataSource: {
      type: Array as PropType<TableCell[]>,
      default: () => []
    },
    headerStyle: Object as PropType<HeaderStyle>, 
    layout: {
      type: String as PropType<TableLayout>,
      default: "horizon"
    },
  },

  setup (props, context) {
    const titleStyle = ref<RealHeaderStyle>({})
    const culumnStyle = ref({})
    const textAlign = ref(props.align)
    const colWidth = ref(props.width)
    // 头部的默认样式与自定义样式
    const HS = props.headerStyle || undefined
    if (props.layout === "horizon") {
      // 【头左体右】表格
      titleStyle.value.minWidth = "180px" // 默认样式
      if (HS) {
        titleStyle.value = {
          ...titleStyle.value,
          ...HS
        }
        if (HS.width) {
          titleStyle.value.minWidth = HS.width
        }
      }
    } else {
      // 正常方向表格 【头上体下】表格
      if (HS) {
        titleStyle.value = { ...HS }
        if (HS.width) {
          titleStyle.value.minWidth = HS.width // 避免头部的宽度小于内容宽度
        } else {
          // 如果表格没有传头部的宽度,cell自动扩展
          culumnStyle.value.flex = 1
        }
        if (HS.height) {
          titleStyle.value.minHeight = HS.height
          titleStyle.value.lineHeight = HS.height
        }
      } else {
        // 如果表格没有传头部的样式,cell自动扩展
        culumnStyle.value.flex = 1
      }

      // 判断列是否有传宽度
      if (colWidth.value) {
        titleStyle.value.minWidth = colWidth.value // 列的样式覆盖表格的样式
      }
    }

    return {
      titleStyle,
      textAlign,
      culumnStyle
    }
  }
}

</script>

<style lang="less">

.table-cell,.table-title{
  padding: @itemSpace;
}

.table-columns{
    .center{
      text-align: center;
    }
    .left{
      text-align: left;
    }
    .right{
      text-align: right;
    }
}

</style>

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你想使用第三方的 Vue 3 表格组件封装一个表格,可以按照以下步骤进行操作: 1. 安装所需的表格组件。以 Element Plus 为例,你可以使用以下命令进行安装: ``` npm install element-plus ``` 2. 在你的 Vue 3 项目中导入并注册表格组件。在你的 main.js 文件中添加以下代码: ```javascript import { createApp } from 'vue'; import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; import App from './App.vue'; const app = createApp(App); app.use(ElementPlus); app.mount('#app'); ``` 3. 创建一个新的 Vue 组件,用于封装表格。在这个组件中,你可以使用 Element Plus 提供的 Table 组件。 ```vue <template> <el-table :data="tableData"> <el-table-column v-for="column in tableColumns" :key="column.prop" :prop="column.prop" :label="column.label"> </el-table-column> </el-table> </template> <script> export default { data() { return { tableColumns: [ { prop: 'name', label: 'Name' }, { prop: 'age', label: 'Age' }, { prop: 'city', label: 'City' } ], tableData: [ { id: 1, name: 'John Doe', age: 25, city: 'New York' }, { id: 2, name: 'Jane Smith', age: 30, city: 'San Francisco' }, { id: 3, name: 'Bob Johnson', age: 35, city: 'Los Angeles' } ] }; } }; </script> ``` 在这个示例中,我们使用了 Element Plus 的 Table 组件,并通过 `tableColumns` 和 `tableData` 来动态渲染表格的列和数据。 你可以根据具体的表格组件的文档和需求,进一步自定义和扩展你的表格组件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值