<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)" v-if="i.isShowAll === false ? i.isShowAll : true" > 全部 </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 || ''" :selectable=" (rowData, rowIndex) => isSelectable(rowData, rowIndex, item) " > <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", }, // 是否显示过滤的全部选项 // isShowAll: { // type: Boolean, // default: true, // }, }, 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 }) ); }; const isSelectable = (row, index, item) => { if (item.type === "selection") { if (row[item.attrs.checkAtt]) { return true; } } else { return true; } }; return { getParent, getPagination, renderTypeList, getActionList, getAttrsValue, getValue, getRenderValue, getMatchRenderFunction, isFunction, handlePageChange, handleSizeChange, getHeaderActions, stopBubbles, handleNativeClick, keywrod, searchFn, searchItemSelct, selectConfigCopy, isSelectable, }; }, }); </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>