index.vue
<script setup>
import { onMounted, ref, nextTick, onUnmounted } from "vue";
import TableTemplate from "./tableTemplate.vue";
const props = defineProps({
tableData: {
type: Array,
default: () => [],
},
tableConfig: {
type: Object,
default: () => {},
},
tableColumnConfig: {
type: Array,
require: true,
default: () => [],
},
minusPart: {
type: Number,
default: () => 0,
},
minTableHeight: {
type: Number,
default: () => 0,
},
});
// 表格高度
const tableHeight = ref(0);
const getTableHeight = () => {
nextTick(() => {
tableHeight.value = window.innerHeight - props.minusPart;
});
};
const xTable = ref();
// 导出子组件Ref
defineExpose({
xTable,
});
// 组件加载完成
onMounted(() => {
getTableHeight();
window.addEventListener("resize", getTableHeight);
});
// 组件移除
onUnmounted(() => {
window.removeEventListener("resize", getTableHeight);
});
</script>
<template>
<div
class="x-table"
:style="minTableHeight ? `height: calc(100% - ${minTableHeight}px)` : ''"
>
<el-table
ref="xTable"
:data="tableData"
v-bind="$attrs"
:max-height="tableHeight"
>
<!-- 多选列 -->
<template
v-if="tableConfig.selection"
:align="selectionAlign"
:header-align="selectionHeaderAlign"
>
<el-table-column
type="selection"
:width="tableConfig.selectionWidth || 40"
:fixed="tableConfig.selectionFixed || null"
></el-table-column>
</template>
<!-- 序号列 -->
<template
v-if="tableConfig.index"
:align="indexAlign"
:header-align="indexHeaderAlign"
>
<el-table-column
type="index"
:width="tableConfig.indexWidth || 50"
:label="tableConfig.indexLabel || '序号'"
:fixed="tableConfig.indexFixed || null"
></el-table-column>
</template>
<!-- 循环列 -->
<template v-for="(item, index) in tableColumnConfig">
<!-- 试验插槽 -->
<el-table-column
v-if="item.scope"
:label="item.label || ''"
:width="item.width || ''"
:min-width="item.minWidth || ''"
:align="item.align || 'left'"
:header-align="item.headerAlign || 'left'"
>
<template #default="scope">
<slot
:name="item.scope"
:data="{ scope: scope.row, index: scope.$index }"
></slot>
</template>
</el-table-column>
<TableTemplate
:tableColumConfig="item"
v-bind="$attrs"
v-else="!item.scope"
></TableTemplate>
</template>
</el-table>
</div>
</template>
<style lang="scss">
.x-table {
.cell {
padding: 0 10px !important;
line-height: 35px !important;
}
th {
background: rgb(240, 242, 245) !important;
font-size: 13px;
color: #333333;
padding: 6px 0 !important;
}
.x-table-template {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-start;
.x-table-input {
input {
text-align: center;
}
}
}
}
</style>
tableTemplate.vue
<script setup>
import { onMounted, ref, computed } from "vue";
import xTableRender from "./xTableRender.vue";
import { useRoute } from "vue-router";
const props = defineProps({
tableColumConfig: {
type: Object,
default: () => {},
},
});
const exData = computed(() => {
return window.localStorage.getItem("exData") || "";
});
const route = useRoute();
const renderItem = computed(() => {
return function (templateList, row) {
if (!Array.isArray(templateList)) {
return [];
}
return templateList.filter((i) => {
return i.showItem
? i.showItem({
row: row,
route: route,
exData: exData.value,
})
: true;
});
};
});
const emit = defineEmits();
const emitFunc = (method, row) => {
emit(method, row);
};
onMounted(() => {});
</script>
<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"
>
<template #default="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}`]"
clearable
: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"
:sc="scope"
:row="scope.row"
:render="i.render(scope.row, that)"
:rederStyle="i.style(scope.row)"
></xTableRender>
</template>
<template v-else-if="i.type === 'renderLink'">
<span
v-for="(_x, _xindex) in i.renderLink(scope.row)"
:key="_xindex"
>
<el-link
@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 }}
{{
_xindex != i.renderLink(scope.row).length - 1
? i.renderFill || ";"
: ""
}}
</el-link>
</span>
</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>
<el-tooltip
placement="top-start"
:disabled="
scope.row[`${tableColumConfig.prop}`] == 0 ||
(scope.row[`${tableColumConfig.prop}`] &&
scope.row[`${tableColumConfig.prop}`].toString().length < 20) ||
!scope.row[`${tableColumConfig.prop}`]
"
>
<div slot="content">
<span style="max-width: 800px">{{
scope.row[`${tableColumConfig.prop}`]
}}</span>
</div>
<div>
{{ scope.row[`${tableColumConfig.prop}`] }}
</div>
</el-tooltip>
</template>
<!-- 可能存在的子集 -->
<template v-if="tableColumConfig.children">
<tableTemplate
v-for="(i, __index) in tableColumConfig.children"
:key="__index"
:tableColumConfig="i"
v-bind="$attrs"
></tableTemplate>
</template>
</template>
</el-table-column>
</template>
<style lang="scss"></style>
xTableReder.vue
<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>
例子:
vue 文件
const { tableHeaderConfig, tableColumnConfig } = useConfig()
const { tableData, tableConfig, tableHeaderForm, pageInfo, dialogTypeObj, reset, commonSubmit } = useTableCommon()
<XTable
:table-column-config="tableColumnConfig"
:table-data="tableData"
:table-config="tableConfig"
:min-table-height="100"
:minus-part="100"
@publicOne="publicOne"
@dele="dele"
@edit="edit"
class="mb-2"
>
<template #testScope="{ data }">{{ data }}</template>
</XTable>
useConfig.js hooks
export default () => {
const tableHeaderConfig = [
{
type: 'input',
prop: 'vehicleBrand',
label: '品牌:',
labelWidth: '50',
width: 160
},
{
type: 'input',
prop: 'registrationNo',
label: '车牌号:',
labelWidth: '60',
width: 160
},
{
type: 'button',
text: '查询',
method: 'search'
},
{
type: 'button',
text: '重置',
btnType: 'default',
method: 'reset'
}
]
const tableColumnConfig = [
{
label: '车牌号',
prop: 'licensePlate'
},
{
label: '设备SN码',
prop: 'sn'
},
{
label: '车辆VIN',
prop: 'vin'
},
{
scope: 'testScope',
label: 'testScope'
},
{
label: '在线状态',
template: [
{
type: 'span',
span: (row) => {
const list = ['离线', '在线']
return list[row.a]
}
}
]
},
{
label: '操作',
template: [
{
type: 'link',
method: 'publicOne',
text: '发布'
},
{
type: 'link',
method: 'dele',
assemblyType: 'danger',
text: '删除'
},
{
type: 'link',
method: 'edit',
text: '编辑'
}
]
}
]
return {
tableHeaderConfig,
tableColumnConfig
}
}
useTableCommon.js hooks 代码
import { ElMessage, ElMessageBox } from 'element-plus'
export default function () {
// 表格数组对象
const tableData = ref([{ a: 1 }])
// 表格配置
const tableConfig = ref({})
// 表头对象
const tableHeaderForm = ref({})
// 分页对象
const pageInfo = reactive({
pageNum: 1,
total: 100,
pageSize: 20
})
// 表头重置
const reset = () => {
tableHeaderForm.value = {}
pageInfo.pageNum = 1
}
// 弹窗控制器
const dialogVisible = ref(false)
// 弹窗综合对象
const dialogTypeObj = reactive({
visible: false,
type: 0,
form: {}
})
const commonSubmit = ({
text = '提示信息',
title = '提示',
confirmBtn = '确认',
cancelBtn = '取消',
status = 'warning'
}) => {
return new Promise((resolve) => {
ElMessageBox.confirm(text, title, {
confirmButtonText: confirmBtn,
cancelButtonText: cancelBtn,
type: status
}).then(() => {
resolve()
})
})
}
return {
tableData,
tableConfig,
tableHeaderForm,
pageInfo,
dialogVisible,
dialogTypeObj,
reset,
commonSubmit
}
}