<template> <div class="header-actions" v-if="getActionList.length != 0"> <div class="overflow-box"> <el-button v-for="(item, index) in getActionList" :key="index" :type="item.type || 'primary'" :plain="item.plain || false" v-bind="getHeaderActions(item)" @click="item.action" :disabled="item.disabled || false" > {{ item.text }} </el-button> </div> </div> <div class="table-list-container by-table"> <!-- v-if="!hideHeader" --> <header v-if="false" class="header"> <h2>{{ title }}</h2> </header> <div class="by-search" v-if="!hideSearch"> <div style="display: flex;"> <div class="by-dropdown" v-for="i in selectConfigCopy" :key="i.prop" style="margin-right:10px"> <div class="by-dropdown-title"> {{ i.label }}<i class="el-icon-caret-bottom el-icon--right"></i> </div> <ul class="by-dropdown-lists"> <li @click="searchItemSelct('all',i)">全部</li> <li v-for="j in i.data" :key="j.value" @click="searchItemSelct(j,i)">{{ j.label }}</li> </ul> </div> </div> <div style="display: flex"> <el-input placeholder="请输入关键字" suffix-icon="search" size="mini" v-model="keywrod" @keyup.enter="searchFn" > </el-input> <el-button type="primary" style="margin-left: 10px" size="default" @click="searchFn" >搜索</el-button > <div class="more-icon"><i class="el-icon-wind-power"></i></div> <div class="more-icon"> <i class="el-icon-notebook-2"></i> </div> </div> </div> <component :is="containerTag"> <div class="filter-form-container"> <slot /> </div> <el-table ref="hocElTable" v-loading="loading" :data="source" v-if="!hideTable" style="width: 100%" v-bind="$attrs" v-on="tableEvents" row-key="id" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > <el-table-column v-for="(item, index) in config" :key="index" v-bind="getAttrsValue(item)" :type="item.type || ''" > <template #default="scope" v-if="!item.type"> <slot :name="item.attrs.slot" :item="scope.row" v-if="item.attrs.slot"> 插槽占位符 </slot> <div v-else-if="isFunction(getValue(scope, item))"> <component :is=" renderTypeList[getMatchRenderFunction(item)] .target " :cell-list="getValue(scope, item)()" :row="scope.row" :parent="getParent" @click=" ($event) => { handleNativeClick( getAttrsValue(item), $event, item ) } " /> </div> <div v-else> {{ getValue(scope, item) }} </div> </template> </el-table-column> </el-table> <el-row v-if="!hidePagination" class="table-pagination" justify="end" type="flex" > <el-pagination background layout="total, sizes, prev, pager, next, jumper" :current-page="getPagination.pageNum" :page-size="getPagination.pageSize" :total="getPagination.total" @size-change="handleSizeChange" @current-change="handlePageChange" /> </el-row> </component> </div> </template> <script> import { isFunction as isFn, isBoolean } from './type' import ElementsMapping from './ElementsMapping' import ComponentsMapping from './ComponentsMapping' import { computed, defineComponent, getCurrentInstance, ref } from 'vue' import expand from './expand' export default defineComponent({ name: 'Table', components: { ElementsMapping, ComponentsMapping, }, props: { hideSearch: { type: Boolean, default: false, }, hideTable: { type: Boolean, default: false, }, //顶部搜索下拉配置 selectConfig:{ type: Array, default() { return [] }, }, // 获取表格元数据时携带的参数 filterParams: { type: Object, default() { return {} }, }, // 表格加载 loading loading: { type: Boolean, default: false, }, // 表格名称 title: { type: String, default: '', }, // 表格元数据 source: { type: Array, required: true, default() { return [] }, }, searchConfig: { type: Object, default() { return { keyword: '', } }, }, // 指定外层容器的渲染组件 containerTag: { type: String, default: 'div', }, // 是否隐藏表头 hideHeader: { type: Boolean, default: false, }, // 是否隐藏分页 hidePagination: { type: Boolean, default: false, }, // 分页配置 pagination: { type: Object, default() { return {} }, }, // 表格配置文件 config: { type: Array, default() { return [] }, }, // 表头右上方的按钮组 actionList: { type: Array, default() { return [{ text: '', action: () => {} }] }, }, // element table 原生事件 tableEvents: { type: Object, default() { return {} }, }, searchKey: { type: String, default: 'keyword', }, }, setup(props) { const { proxy } = getCurrentInstance() const keywrod = ref('') const selectConfigCopy = computed(()=>{ return props.selectConfig.map((item)=>{ item.labelCopy = item.label return item }) }) console.log(selectConfigCopy) const getAttrsValue = (item) => { const { attrs } = item const result = { ...attrs, } delete result.prop return result } const renderTypeList = ref({ render: {}, renderHTML: { target: 'elements-mapping', }, renderComponent: { target: 'components-mapping', }, }) const getParent = computed(() => { return proxy.$parent }) const getPagination = computed(() => { const params = { pageNum: 1, pageSize: 10, total: 0, } return Object.assign({}, params, props.pagination) }) const getActionList = computed(() => { return props.actionList .slice() .reverse() .filter((it) => it.text) }) const getValue = (scope, configItem) => { const prop = configItem.attrs.prop const renderName = getMatchRenderFunction(configItem) const renderObj = renderTypeList.value[renderName] if (renderObj && isFunction(configItem[renderName])) { return renderObj.target ? getRenderValue(scope, configItem, { name: renderName, type: 'bind', }) : getRenderValue(scope, configItem) } return scope.row[prop] } const getRenderValue = ( scope, item, fn = { name: 'render', type: 'call' } ) => { const prop = item.attrs.prop const propValue = prop && scope.row[prop] scope.row.$index = scope.$index const args = propValue !== undefined ? propValue : scope.row return item[fn.name][fn.type](getParent.value, args) } // 匹配 render 开头的函数 const getMatchRenderFunction = (obj) => { return Object.keys(obj).find((key) => { const matchRender = key.match(/^render.*/) return matchRender && matchRender[0] }) } const isFunction = (fn) => { return isFn(fn) } const searchFn = (val) => { console.log(props) proxy.$emit( 'getList', Object.assign(props.filterParams, { [props.searchKey]: keywrod.value }) ) } const handlePageChange = (val) => { proxy.$emit( 'getList', Object.assign(props.filterParams, { pageNum: val }) ) } const handleSizeChange = (val) => { proxy.$emit( 'getList', Object.assign(props.filterParams, { pageSize: val }) ) } const getHeaderActions = (item) => { return { ...item.attrs, } } const stopBubbles = (e) => { const event = e || window.event if (event && event.stopPropagation) { event.stopPropagation() } else { event.cancelBubble = true } } const handleNativeClick = ({ isBubble }, e,item) => { // 考虑到单元格内渲染了组件,并且组件自身可能含有点击事件,故添加了阻止冒泡机制 // 若指定 isBubble 为 false,则当前单元格恢复冒泡机制 if (isBoolean(isBubble) && !isBubble) return stopBubbles(e) } //下拉搜索相关 const searchItemSelct = ((item,i) => { if(item == 'all') { i.label = '全部' proxy.$emit( 'getList', Object.assign(props.filterParams, { [i.prop]: '' }) ) return } i.label = item.label console.log(item,i) proxy.$emit( 'getList', Object.assign(props.filterParams, { [i.prop]: item.value }) ) }) return { getParent, getPagination, renderTypeList, getActionList, getAttrsValue, getValue, getRenderValue, getMatchRenderFunction, isFunction, handlePageChange, handleSizeChange, getHeaderActions, stopBubbles, handleNativeClick, keywrod, searchFn, searchItemSelct, selectConfigCopy } }, }) </script> <style> .table-list-container th { color: #333 !important; } .by-table td .el-button+.el-button{ margin-left: 0!important; } .by-table td .el-button{ background: none!important; margin: 0!important; padding:8px 6px!important; } </style> <style lang="scss" scoped> .by-search { display: flex; justify-content: space-between; margin-bottom: 10px; } .by-dropdown { width: 106px; position: relative; text-align: left; height: 32px; z-index: 100; padding:0 10px; transition: all 0.5s ease; cursor: pointer; line-height: 32px; .by-dropdown-title { font-size: 14px; background-color: #fff; } ul { position: absolute; left: 0; right: 0; top: 32px; padding: 0; margin: 0; z-index: 100; display: none; li { list-style: none; font-size: 12px; height: 30px; padding: 0 10px; } li:hover { background-color: #eff6ff; color: #0084ff; } } } .by-dropdown::before { display: block; width: 1px; content: ' '; position: absolute; height: 14px; top: 8px; background-color: #ddd; right: 0; z-index: 101; } .by-dropdown:hover { background: #ffffff; border-radius: 2px 2px 2px 2px; opacity: 1; ul { background: #ffffff; box-shadow: 0px 2px 16px 1px rgba(0, 0, 0, 0.06); border-radius: 2px 2px 2px 2px; opacity: 1; display: block; text-align: left; } } .header-actions { flex: 1; overflow-x: auto; padding: 20px; background: #fff; margin-bottom: 20px; .overflow-box { :deep() .el-button:nth-child(1) { margin-left: 10px; } } } .table-list-container { background: #fff; padding: 13px 20px 20px; .table-pagination { padding-top: 20px; } .header { display: flex; padding-bottom: 20px; } .el-table { :deep() th { font-size: 14px; } :deep() td { font-size: 14px; } } } </style>