element-ui 表格hook 及相关组件

使用例子

<template>
  <div>
    <Searchbar :searchBarConf='searchBarConf'/>
    <Table :tableConf='tableConf'>
         <div slot="btns" slot-scope="slotProps" v-bind="slotProps.itemProps">
          <el-button type='text' @click="onEditor(slotProps)" > 编辑 </el-button>
        </div>
    </Table>
    <Pagination />
  </div>
</template>

<script>
import { reactive, computed, toRefs, provide } from '@vue/composition-api'
import {
  tableConf,
  searchBarConf,
  confHook
} from './model'
import { delPop, reqSuccessTip, reqFailTip, asyncVessel } from '@/utils/tool'
import * as api from './api'
import {
  tableHook,
  injectMark,
  Pagination,
  Searchbar,
  Table
} from '@/components/manage-table/index.js'

    
 const tableConf = {
    props: {

    },
    columns: [
        { label: '用户id', prop: 'id', align: 'center', sortable: true },
        { label: '用户昵称', prop: 'realName', align: 'center' },
        { label: '参团数', prop: 'joinGroupNum', align: 'center', sortable: true },
        { label: '订货数', prop: 'buyProductNum', align: 'center', sortable: true },
        { label: '操作', prop: 'btns', align: 'center', width: 80, slot: 'btns' }
    ]
  }
 
const searchBarConf = [

    {
      col: 6,
      cmp: 'el-input',
      cmpProps:{
          size:'small',
          placeholder: '请输入完整的手机号码'
      },
      formItemProp: {
        label: '手机号码',
        prop: 'phone'
      },
    },

  ]
 
export default {
  components:{
    Pagination,
    Searchbar,
    Table
  },

  setup(){
      
    // 创建state
    const tableState = tableHook( api.tableList )
    
    // 注入组件依赖
    provide(injectMark, tableState)
    
    // 表格更新
    const updateTable = tableState.onSearchNoQuery
    const onEditor = (row) => {
        console.log(row);
        updateTable()
    }
    
    return {

      tableConf,
      searchBarConf,
     
    }
  }

 
}
</script>

tableHook实现

/**
  * author: huhaiguo
  * date: 2019-51-26 10:12
  * summary: 表格功能逻辑
  */
import { reactive, computed, toRefs, ref, watch } from '@vue/composition-api'
import cloneDeep from 'lodash/cloneDeep'


import { 
  asyncVessel
} from '@/utils/tool'

export const injectMark = 'MANAGE_TABLE' 

/**
 * 
 * @param { Function } update 表格数据请求接口 
 * @param { Object } initQuery 搜素条初始参数
 * @param { Object } options 设置项 
 */
export default function tableHook( update, initQuery={}, options={}){

  // 默认配置
  const defOptions = {
    pageSize: 10,
    totalCount: 0,
    currentPage: 1,
    tableList: [],
  }

  const state = reactive(Object.assign({}, defOptions, options))
  
  // 表格对象,用于获取对象上的方法
  const tableEle = ref(null)

  // 设置搜索条参数
  const setQuery = (initQuery) => reactive({
    pageSize: computed(() => state.pageSize),
    pageNo: computed(() => state.currentPage),
    ...cloneDeep(initQuery),
  })
  
  
  // 重置搜索条参数
  let query = ref(setQuery(initQuery))
   

  // 向外开放查询参数
  watch(() => query.value, () => state.localCopyQuery&&state.localCopyQuery(query.value))
  
  // 搜索条重置
  const resetQuery = () => query.value = setQuery(initQuery)

  // 请求表格数据
  async function onSearch(params=query.value){
    
    const [ res, err ] = await asyncVessel(update(params)) 

    if(err){
      console.error('TABLE_ERROR: ', err)
      return
    }
    
    const { result, totalCount } = res
    state.tableList = result
    state.totalCount = totalCount
    
  }

  // 分页选择
  const onCurrentChange = num => {
    state.currentPage = num
    onSearch()
  }
  
  // 重置表格数据
  const restTable = () => {
    resetQuery()
    onCurrentChange(1)
  }


  // 初始请求
  restTable()

  return {
    query,
    tableEle,
    restTable,
    resetQuery,
    onSearch,
    onSearchNoQuery: () => onSearch(),
    onCurrentChange,
    ...toRefs(state)
    
  }
  
}

搜索条

<template>
<div style="margin-bottom: 40px">
    <el-card shadow="hover" body-style="padding: 0">
        <DefTableTopbar @search='onSearchHandler' @reset='onResetHandler' />
        <div style="padding: 20px; padding-bottom: 2px">
            <Form :itemsConf='searchBarConf' :model='query' />
        </div>
    </el-card>
</div>
</template>

<script>
import DefTableTopbar from './searchbar-btns'
import Form from '../form-container/index.vue'
import { toRefs, inject } from '@vue/composition-api'
import { injectMark } from './table-hook'

export default {

    components: {
        DefTableTopbar,
        Form
    },
    
    props: {

        searchBarConf:{
            default(){return {}}
        },
        
    },
    
    setup(){
        const allState = inject(injectMark)
    
        const onSearchHandler = () => {
            allState.currentPage = 1
            allState.onSearch()
        }

        const onResetHandler = () =>{
            allState.restTable()
        }
        
        return {
            onSearchHandler,
            onResetHandler,
            ...toRefs(allState)
        }
    }

}
</script>

分页

<template>
  <div style='margin-top: 20px'>
   <el-card bodyStyle="padding: 10px 20px" shadow='hover'>
        <Pagination
        @current-change='onCurrentChange'
        :total='totalCount' 
        :pageSize='pageSize' 
        :currentPage='currentPage' >
        </Pagination>
    </el-card>
  </div>
</template>

<script>
import { toRefs, inject } from '@vue/composition-api'
import { injectMark } from './table-hook'

const Pagination = ctx => {

  const _defaultConf = {

    ['layout']: ['total', 'prev', 'pager', 'next',  'jumper'].join(','),
    ['current-page']: '',
    ['page-sizes']: [10],
    ['page-size']: 10,
    ['total']: 0,
    ['currentPage']: 1,
    
  }

  if(ctx.props){
    ctx.props = Object.assign({}, _defaultConf, ctx.props)  
  }

  return (
    <el-pagination style={{padding:0, marginTop:0}} { ...ctx } on={ ctx.listeners } ></el-pagination>
  )
   
} 

export default {

    components: {
      Pagination
    },

    setup(){
        const allState = inject(injectMark)
        return toRefs(allState)
    }

}

</script>

表格

import CadTopBar from '@/components/card-top-bar'
import { inject } from '@vue/composition-api'
import { injectMark } from './table-hook'

const Table = {

    props:{
        tableConf:{
            default(){return {}}
        },
       
    },

    setup(props){
        const allState = inject(injectMark)
        
        return {
            tableList: allState.tableList 
        }
    },

    render(){

        const { tableList, tableConf } = this
        
        // 表格列
        const tableColumns = tableConf.columns.map((item, index) =>{

            // child 插槽
            if(item.child){

                const Cmp = item.child
                const scopedSlots = {
                    default: slotprops => [
                        <Cmp slotprops={slotprops} on={ item.on } attrs={ item } ></Cmp>
                    ]
                }
                
                return <el-table-column  key={index} props={item} scopedSlots={scopedSlots} ></el-table-column>
            }

            // slot 插槽
            if(item.slot){

                const Cmp = this.$scopedSlots[ typeof item.slot === 'string' ? item.slot : item.prop]
                
                if(!Cmp) return 
                
                const scopedSlots ={
                    default: slotprops => [
                        Cmp(slotprops),
                    ]
                }

                return <el-table-column key={index} props={item} scopedSlots={scopedSlots} ></el-table-column>
                
            }
            
            // 默认column
            return <el-table-column key={index} props={item} ></el-table-column>
            
        })

        // topbar 插槽
        let Topbar = this.$slots['table-top-bar']

        return (

            <el-card body-style='padding: 0' shadow='hover' >

                <CadTopBar title='数据列表'>
                    { Topbar }
                </CadTopBar>
            
                <el-table ref='tableEle' data={ tableList } listeners={ tableConf.on } attrs={ tableConf.props }  >
                    { tableColumns }
                </el-table>
    
            </el-card>
            
        )
        
    }
    
}


export default Table
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容