vue3 hooks实现element-plus 表格分页组件

3,059 阅读2分钟

来个一个全新的小项目,vue3出来这么久了,可以实践起来!

对于一个后台管理系统来说,表格组件是不可缺少的。面对大量的表格,就得尽可能的复用组件。让我们提升技术的同时更好的摸鱼。嘿嘿。

话不多说,开干!

首先默认已经安装了element-plus,封装好了统一的axios,因为用上了typescript(刚学的),用的不好轻喷

table组件

  <div class="table">
    <el-table ref="TableComponents"
      :header-cell-style="{background:'#F7F7F7',color:'#333'}"
      :size="size" :border="border" :data="tableData"
      :tree-props="treeProps"
      :fit="fit"
      :max-height="maxHeight"
      :row-key="rowKey"
      :highlight-current-row="true"
      :automatic-dropdown="false"
      @selection-change="handleSelectionChange"
      >
        <template v-for="item in columns" :key="item.prop">
          <slot :name="item.slot" v-if="item.slot&&item[dataKey.label]"></slot>
          <el-table-column  :prop="item[dataKey.prop]" show-overflow-tooltip :label="item[dataKey.label]" v-if="item[dataKey.label]&&!item.slot" :formatter="item.formatter" :min-width="item.width">
              
          </el-table-column>
          <el-table-column  type="selection" width="55" v-if="!item[dataKey.label]&&!item.slot" :selectable="checkSelectable"></el-table-column>
        </template>
        <slot name="operation"></slot>
    </el-table>
    <el-pagination
      v-if="pageIsShow&&pageTotal>10" class="pagination"
      :size="size" :page-sizes="pageSizes" :page-size="pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="pageTotal" :current-page="currentPage"
      @size-change="handleSizeChange" @current-change="handleCurrentChange"
    >
    </el-pagination>
  </div>
</template>
<script lang="ts">
import { defineComponent, watch } from 'vue'

export default defineComponent({
  name:'table',
  props:{
    loading:{
      type:Boolean,
      default:true
    },
    tableData:{
      type:Array,
      default:[] //表格数据
    },
    columns:{
      type:Array,
      default:()=>[]//表格列
    },
    pageIsShow:{
      type:Boolean,
      default:true //是否显示分页
    },
    pageSizes:{
        type:Array,
        default:()=>[10, 15, 20,30,50,100] //智能分页选项
    },
    pageSize:{
        type:Number,
        default:()=>10 //默认每页条数
    },
    pageTotal:{
        type:Number,
        default:()=>0 //总条数
    },
    border:{
        type:Boolean,
        default:true //默认有边框
    },
    stripe:{
        type:Boolean,
        default:true //是否为斑马纹 table(默认有)
    },
    fit:{
        type:Boolean,
        default:true //默认自动撑开
    },
    size:{
        type:String,
        default:'small'
    },
    treeProps:{
        type:Object,
        default:()=>{}
    },
    rowKey:{
        type:String||Function,default:()=>''
    },
    dataKey:{
        type:Object,
        default:()=>{
            return {label:'label',prop:'prop'}
        }
    },
    currentPage:{
        type:Number,
        default:()=>1 //当前页码
    },
    height:{
        type:String,
        default:'55px' 
    },
    maxHeight:{
      type:Number,
      default:()=> {
        return document.body.offsetHeight-210
      }
    }
  },
  setup(props, ctx) {
    const { emit } = ctx;
    const handleSelectionChange = (val: any) => emit('selection-change', val);
    const handleSizeChange = (val: number) => emit('size-change', val);
    const handleCurrentChange = (val: number) => emit('current-change', val);
    const handleClickActiveLink = ($event: MouseEvent, row: any) => emit('get-row-info', $event, row);
    const checkSelectable = (row: any) => row.name !== 'None';

    return {
      handleSelectionChange,
      handleSizeChange,
      handleCurrentChange,
      handleClickActiveLink,
      checkSelectable
    }
  },
})
</script>
<style lang="scss" scoped>
.table{
  .pagination{
    margin-top: 10px;
    text-align: right;
    padding-left: 20px;
  }
}
</style>

然后是hooks.ts

import { onMounted, reactive } from 'vue'
import { HttpListQuery, IdParam } from '@/@types/index' //这个是定义的泛型
/**

export interface HttpListQuery {
  pageNo: number
  pageSize: number
  [key: string]: any
}

export interface IdParam {
  id: number
}
export interface HttpResponse {
  code: number
  status: number
  statusText: string
  result: any
}
**/
import CommonService from '@/api/common' //这几个是统一封装的方法
class CommonService {
  //获取列表
  static async getTableList(url:string,data:HttpListQuery): Promise<HttpResponse> {
    return Axios(url,{
      method: 'post',
      headers: {
        'Content-Type': 'application/json'
      },
      data
    })
  }

  static async deleteItem(url:string,data:IdParam): Promise<HttpResponse> {
    return Axios(url,{
      method: 'post',
      headers: {
        'Content-Type': 'application/json'
      },
      data
    })
  }
}

**/
import { successMessage } from "@/utils/message";//消息提示
interface Params{
  searchUrl: string //搜索api地址
  deleteUrl: string //删除的api地址
  searchData: any //搜索的数据
}
const useTable = (options:Params) => {
  let { searchUrl, deleteUrl, searchData} = options

 //搜索数据滴响应
  const searchState = reactive({ 
    searchData,
    pageNo: 1,
    pageSize: 10,
  })
 //返回数据滴响应
  const dataState = reactive({
    tableData: [],
    total:0
  })

//获取表格数据
  const getTableList = async (Params:HttpListQuery) => { 
    let { code, result } = await CommonService.getTableList(searchUrl, Params)
    if(code === 200) {
      dataState.tableData = result.records
      dataState.total = result.total
    }
  }
//引入生命周期,执行搜索
  onMounted(()=>{
    getTableList({...searchState})
  })

  //删除某一行数据
  const deletaItem = async (deleteId:IdParam) => {
    let { code, result} = await CommonService.deleteItem(deleteUrl,deleteId)
    if(code === 200) {
      successMessage('删除成功')
      getTableList(searchState)
    }
  }
//调整每一页多少行数据
  const sizeChange = (val:number) => {
    searchState.pageNo = 1;
    searchState.pageSize = val;
    getTableList(searchState)
  }
//翻页的
  const currentChange = (val:number) =>{
    searchState.pageNo = val;
    getTableList(searchState)
  }


  return {
    searchState,
    dataState,
    getTableList,
    deletaItem,
    sizeChange,
    currentChange
  }


}

export default useTable;

基础的完成了,接下来就是在组件中调用,映入组件和hooks

//模板
<Table :columns="columns" :tableData="tableData" :page-total="total" @size-change="sizeChange" @current-change="currentChange">
      <template #operation>
        <el-table-column label="操作" fixed="right" :width="150">
          <template v-slot="scope">
            <div class="table-operation">
              <span type="text" class="text-btn" >
                <el-popconfirm
                  confirmButtonText='好的'
                  cancelButtonText='不用了'
                  icon="el-icon-info"
                  iconColor="red"
                  title="确定删除选中xxx吗?"
                  @confirm="delete(scope.row.id)"
                >
                <template #reference>
                  <span>删除</span>
                  </template>
                </el-popconfirm>
              </span>
            </div>
          </template>
            
        </el-table-column>
      </template>
      
    </Table>

//ts

<script lang="ts">
//定义表格响应数据的
export default defineComponent({
components:{Table},
setup() {
const tableState = reactive({
        searchParams: {
          typeName: '',
          typeCode:''
        },
        columns: [
            {prop: 'XXX', label: 'XXX',width:180},
            {prop: 'XXX', label: 'XXX'},
            {label: '操作', slot:'operate'}
        ]
        
    })
    const params = {
      searchUrl: 'XXX',
      deleteUrl:'XXX',
      searchData: tableState.searchParams
    }
    
const {
      getTableList, 
      deletaItem,
      sizeChange,
      currentChange,
      searchState,
      dataState,
    } = useTable(params) //使用hooks

   //给搜索按钮加上的事件
    const getPageList = ():void => {
      getTableList({...searchState})
    }
   //删除方法
    const delete = async (id:number) => {
      deletaItem({id})
    }
    return {
      ...toRefs(tableState),
      ...toRefs(searchState),
      ...toRefs(dataState),
      getPageList,
      sizeChange,
      currentChange,
      delete

    }

}

})

</script>

第一次发文,不当之处见谅。技术有限,请多多指教!