- index.vue
- TableTemplate.vue
- xTableRender.vue
-- 1.index--
//index.vue
<template>
<div
class="x-table"
:style="minTableHeight ? `height: calc(100% - ${minTableHeight}px)` : null"
>
<el-table
ref="xTable"
v-bind="$attrs"
v-on="$listeners"
:data="tableData"
:max-height="tableMaxHeight ? tableMaxHeight : tableHeight"
:min-height="`200px`"
@selection-change="selectionChange"
:style="{ '--line': cellLineNumber }"
>
<!-- 多选列 -->
<template v-if="tableConfig.selection" align="left">
<el-table-column
type="selection"
:width="tableConfig.selectionWidth || 40"
:fixed="tableConfig.selectionFixed || null"
:reserve-selection="tableConfig.reserveSelection || false"
:selectable="
tableConfig.selectable ||
function () {
return true;
}
"
></el-table-column>
</template>
<!-- 序号列 -->
<template v-if="tableConfig.index">
<el-table-column
type="index"
:width="tableConfig.indexWidth || 50"
:label="tableConfig.indexLabel || '序号'"
:fixed="tableConfig.indexFixed || null"
:sortable="tableConfig.sortable ? tableConfig.sortable : false"
></el-table-column>
</template>
<template v-for="item in tableColumnConfig">
<!-- 动态插槽 -->
<el-table-column
v-if="item.scope && !item.noShow"
:key="item.scope"
:label="item.label || ''"
:width="item.width || ''"
:min-width="item.minWidth || ''"
:align="item.align || 'left'"
:header-align="item.headerAlign || 'left'"
>
<template slot-scope="scope">
<slot
:name="item.scope"
:data="{ scope: scope.row, index: scope.$index }"
/>
</template>
</el-table-column>
<!-- 循环列 -->
<TableTemplate
v-else-if="!item.scope && !item.noShow"
:key="item.label"
:table-colum-config="item"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
<!-- 提供一个尾插槽插槽 -->
<slot />
</el-table>
</div>
</template>
<script>
import TableTemplate from "./TableTemplate.vue";
export default {
name: "XTable",
props: {
tableData: {
type: Array,
default: () => [],
},
tableConfig: {
type: Object,
default: () => {},
},
tableColumnConfig: {
type: Array,
require: true,
default: () => [],
},
minusPart: {
type: Number,
default: () => 0,
},
minTableHeight: {
type: Number,
default: () => 0,
},
cellLineNumber: {
type: Number,
default: () => 2,
},
tableMaxHeight: {
type: Number,
default: () => 0,
},
},
data() {
return {
tableHeight: 0,
};
},
mounted() {
this.getTableHeight();
window.addEventListener("resize", this.getTableHeight);
},
beforeMount() {
window.removeEventListener("resize", this.getTableHeight);
},
methods: {
getTableHeight() {
this.$nextTick(() => {
this.tableHeight = window.innerHeight - this.minusPart;
});
},
selectionChange(val) {
this.$emit("selectionChange", val);
},
},
components: { TableTemplate },
};
</script>
<style lang="scss">
.x-table {
.cell {
padding: 0 10px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box; //作为弹性伸缩盒子模型显示。
-webkit-box-orient: vertical; //设置伸缩盒子的子元素排列方式--从上到下垂直排列
-webkit-line-clamp: var(--line); //显示的行
}
th {
background: rgb(240, 242, 245);
}
.x-table-template {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-start;
.x-table-input {
input {
text-align: center;
}
}
}
.el-link {
font-size: 13px;
}
.is-disabled {
color: #c0c4cc !important;
}
}
</style>
--2.TableTemplate.vue--
// 为了实现多级表头而做的子组件的再封装
<template>
<el-table-column
:label="tableColumConfig.label || ''"
:width="tableColumConfig.width || ''"
:min-width="tableColumConfig.minWidth || ''"
:prop="tableColumConfig.prop"
:align="tableColumConfig.align || 'left'"
:header-align="tableColumConfig.headerAlign || 'left'"
:fixed="tableColumConfig.fixed || null"
:sortable="tableColumConfig.sortable || false"
:render-header="
tableColumConfig.renderHeader ? tableColumConfig.renderHeader : null
"
>
<template slot-scope="scope">
<!-- 循环可能存在的template数组 -->
<div
class="x-table-template"
v-if="
tableColumConfig.template &&
Array.isArray(tableColumConfig.template) &&
!tableColumConfig.children
"
:style="tableColumConfig.alignStyle || ''"
>
<div
v-for="(i, _index) in renderItem(
tableColumConfig.template,
scope.row
)"
:key="_index"
>
<!-- 按钮项 -->
<template v-if="i.type === 'button'">
<el-button
:type="i.assemblyType || 'primary'"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
>
{{ i.text }}
</el-button>
</template>
<!-- icon项 -->
<template v-else-if="i.type === 'icon'">
<el-link
:type="i.assemblyType || 'primary'"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
>
<!-- icon的样式 具体看 https://element.eleme.cn/#/zh-CN/component/icon -->
<i :class="i.iconClass"></i>
</el-link>
</template>
<!-- input输入框 -->
<template v-else-if="i.type === 'input'">
<el-input
class="x-table-input"
v-model="scope.row[`${tableColumConfig.prop}`]"
:type="i.assemblyType || 'text'"
:placeholder="i.placeholder || '请输入'"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
@blur="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
></el-input>
</template>
<!-- 文字方面的 -->
<template v-else-if="i.type === 'link'">
<el-link
:type="i.assemblyType || 'primary'"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
>
{{ i.text }}
</el-link>
</template>
<!-- 非超链接 -->
<template v-else-if="i.type === 'span'">
<span
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
:style="i.spanStyle ? i.spanStyle(scope.row) : ''"
>
{{ i.span(scope.row) }}
</span>
</template>
<template v-else-if="i.type === 'render'">
<XTableRender
v-bind="$attrs"
v-on="$listeners"
:sc="scope"
:row="scope.row"
:render="i.render(scope.row, that)"
:rederStyle="i.classStyle ? i.classStyle(scope.row) : ''"
@sss="sss"
></XTableRender>
</template>
<template v-else-if="i.type === 'renderLink'">
<el-link
v-for="(_x, _xindex) in i.renderLink(scope.row)"
:key="_xindex"
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
linkIndex: _xindex,
})
"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
:type="i.assemblyType ? i.assemblyType() : 'primary'"
:style="i.linkStyle"
>
{{ _x }}
</el-link>
</template>
<!-- 自定义值 -->
<el-link
@click="
emitFunc(i.method || 'defaultMethod', {
row: scope.row,
index: scope.$index,
additionalParams: i.addParams || '',
})
"
v-if="i.supplement"
:type="i.suppleType ? i.suppleType(scope.row) : ' '"
:disabled="
i.disabled
? i.disabled({
row: scope.row,
route: $route,
exData: exData,
})
: false
"
>{{ i.supplement(scope.row) }}
</el-link>
</div>
</div>
<template v-else>
{{ scope.row[`${tableColumConfig.prop}`] }}
</template>
</template>
<!-- 可能存在的子集 -->
<template v-if="tableColumConfig.children">
<xtable-template
v-for="(i, __index) in tableColumConfig.children"
:key="__index"
:tableColumConfig="i"
v-bind="$attrs"
v-on="$listeners"
></xtable-template>
</template>
</el-table-column>
</template>
<script>
import XTableRender from "./XTableRender.vue";
export default {
name: "xtable-template", //必须存在
props: {
tableColumConfig: {
type: Object,
default: () => {},
},
},
computed: {
that() {
return this;
},
exData() {
return window.localStorage.getItem("exData") || "";
},
// 是否渲染模板内的template
renderItem() {
return function (templateList, row) {
if (!Array.isArray(templateList)) {
return [];
}
return templateList.filter((i) => {
return i.showItem
? i.showItem({
row: row,
route: this.$route,
exData: this.exData,
})
: true;
});
};
},
},
methods: {
emitFunc(method, row) {
this.$emit(method, row);
},
},
components: { XTableReder },
};
</script>
<style lang="scss" scoped></style>
--3.XTableRender--
<script>
export default {
functional: true,
props: {
row: {
type: Object,
required: true,
},
render: {
type: Function,
required: true,
},
sc: {
type: Object,
required: true,
},
rederStyle: {
type: String,
require: true,
},
},
render: (h, ctx) => {
const arr = [];
const params = {
row: ctx.props.row,
index: ctx.props.sc.$index,
};
const VNode = ctx.props.render(h, params);
arr.push(VNode);
return h("div", { class: ctx.props.rederStyle }, arr);
},
};
</script>
使用示例:
const XTableColumnConfig = [
{
props: "zw", // el-table-column 的 props
width: "200", // 该列的宽度
minWidth: "120", // 该列的最小宽度 按理说 width 和 min-width 是互斥的
label: "列名", // 列名
align: "left", // [left,right,center] 列内文本对齐方式 如果在XTableConfig 设置了全局的 config ,该属性不会生效
headerAlign: "left", //[left,right,center] 表头的对齐方式 如果在XTableConfig 设置了全局的 config ,该属性不会生效
fixed: true, //[true,false] 是否开启fixed 就是是否浮动,与 el-table-column 属性一样
sortable: false, //[true,false] 是否启用排序功能,与 el-table-column 属性一样
renderHeader: null, //一个render 函数,是否使用render 函数来渲染表头
noShow: false, //[true,false] 该列是否显示
alignStyle:'',//行内文本或者组件的排序方式 默认是 justify-content: flex-start; gap:10px ; flex-wrap: wrap;
template:[ //这里的 template 是一个数组,其中的内容是可循环的
// 几乎是定死没有变化的情况下的元素且它的列表深度只有一层
{
type:'button', //[button、icon、input、link] 该组件的类型
assemblyType:'default', //[primary,warning,error,default] 该组件的状态
disabled:false, //[false,true] 是否禁用
method:'event' //String 事件名可以自己定义书写 组件点击或者失去焦点的时候触发的事件,可以在 <XTable> 上获得响应
text:'hh',//String 一般情况下是内填充的文本
},
// 以下介绍不是定死,会存在变化的元素
{
type:"span", //文本元素 可根据某一个值变化
spanStyle:(row)=>'',//该文本的 CSS 样式, row 是行对象
methods:'spanClick',// 事件名可自己定义书写 此事件是文本被点击后触发的事件
span:(row)=>'我是文本',// row 可获得行对象,通过返回值可写入该行内,动态改变或者获取更深层次的
},
{
supplement:(row)=>'我是自定义的占位链接',// 该组件没有 type 可自定义可变化的 link
disabled:(row)=>false,//是否禁用
method:'supplementClick',// 链接点击后触发的事件
suppleType:(row)=>'primary' //该链接的状态
},
]
},
// 该对象介绍自定义插槽
{
label:'我是自定义插槽',//插槽的列名
slot:'mySlot',//插槽的自定义 name 名
noShow:false,//自定义插槽是否可见
}
];
// 配置对象 如无特殊需求,该对象可以是一个空对象 如不传给 XTable 会报错
const tableConfig = {
selection:true,//是否开始多选列
reserveSelection:true,//开启的多选列是否是采用唯一值(换页也唯一)
selectable:selectable,//该行是否可选,返回false 不可选
selectionFixed:true,//序号列是否粘性居左
selectionWidth:40,//多选列的宽度
index:true,//是否开启序号列
indexLabel:'序号',//序号列的列名
indexWidth:40,//序号列的宽度
sortable:true,//序号列是否开启排序
}
//!请注意 XTable 的必传参数 tableData tableConfig tableColumnConfig
//如何使用 XTable 以下是一个示例
<XTable
class="mb-2"
border
:key="tableKey"
:table-data="tableData"
:tableConfig="tableConfig"
:table-column-config="XTableColumnConfig"
@toDetailed="toDetailed"
>
<template slot="mySlot" slot-scope="{ data: data }">
<div style="width: 100%; height: 100%" class="flex flex-center">
<el-dropdown
class="avatar-container"
trigger="click"
placement="bottom"
@command="handleAction"
>
<el-link class="action-icon" icon="el-icon-setting" />
<el-dropdown-menu>
<el-dropdown-item
icon="el-icon-turn-off"
:command="['bind', data.scope]"
>绑定</el-dropdown-item
>
<el-dropdown-item
icon="el-icon-edit"
:command="['edit', data.scope]"
>修改</el-dropdown-item
>
<el-dropdown-item
icon="el-icon-delete"
:command="['deleteOne', data.scope]"
>删除</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</XTable>
data(){
return {
tableKey: 2233,
tableData:[]
}
},
methods:{
selectable(row) {
const find = this.tableData.find((e) => e.dtcCode === row.dtcCode);
if (find) {
return false;
}
return true;
},
}