需求
合并行、合并标题、列宽可调整、列顺序可调整、可以控制列是否显示、列布局可保存、导出excel…
参考效果
代码
引入
npm i xlsx
npm install element-plus --save
table组件
<template>
<div>
<div class="table-btn">
<el-tooltip content="高级筛选" placement="bottom" effect="light">
<el-button :icon="Search" circle @click="dialogRulesVisible = true" />
</el-tooltip>
<el-tooltip content="修改table" placement="bottom" effect="light">
<el-button :icon="EditPen" circle @click="dialogVisible = true" />
</el-tooltip>
<el-tooltip content="导出xlsx" placement="bottom" effect="light">
<el-button :icon="Download" circle @click="exportBtn" />
</el-tooltip>
</div>
<el-table :data="tableData" style="width: 100%" ref="exportTableRef">
<template v-for="item in tablePropList">
<el-table-column
sortable
:key="item.prop"
:prop="item.prop"
:label="item.label"
:align="item.align"
:width="item.width == 0 ? '' : item.width"
v-if="item.isShow == 0"
:fixed="item.fixed"
>
<template v-if="item.children.length > 0">
<template v-for="itemChildren in item.children">
<el-table-column
:key="itemChildren.prop"
sortable
:prop="itemChildren.prop"
:label="itemChildren.label"
:align="itemChildren.align"
:width="itemChildren.width == 0 ? '' : itemChildren.width"
v-if="itemChildren.isShow == 0"
>
</el-table-column>
</template>
</template>
</el-table-column>
</template>
<!-- 插槽 -->
<slot />
</el-table>
<!-- 把这个改成表格 -->
<el-dialog v-model="dialogVisible" title="修改表格" width="80%">
<div class="over-height">
<el-table :data="tablePropList" style="width: 100%" row-key="prop">
<el-table-column prop="date" label="表格名">
<template #default="scope">
<el-input v-model="scope.row.label" />
</template>
</el-table-column>
<el-table-column prop="date" label="序列">
<template #default="scope">
<el-input-number v-model="scope.row.index" :min="0" :max="100" />
</template>
</el-table-column>
<el-table-column prop="date" label="宽度(0为自适应)">
<template #default="scope">
<el-input-number v-model="scope.row.width" :min="0" :max="1000" />
</template>
</el-table-column>
<el-table-column prop="date" label="对齐方式">
<template #default="scope">
<el-select v-model="scope.row.align">
<el-option label="左对齐" value="left" />
<el-option label="居中" value="center" />
<el-option label="右对齐" value="right" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="date" label="是否固定">
<template #default="scope">
<el-select v-model="scope.row.fixed">
<el-option label="左侧固定" value="left" />
<el-option label="右侧固定" value="right" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="date" label="是否显示">
<template #default="scope">
<el-select v-model="scope.row.isShow">
<el-option label="显示" value="0" />
<el-option label="隐藏" value="1" />
</el-select>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="sortTableData(0)"
>保存并确定</el-button
>
<el-button type="primary" @click="sortTableData(1)">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="dialogRulesVisible" title="高级筛选" width="80%">
<el-button-group class="ml-4">
<el-button type="primary" plain @click="rulesTableData(0)"
>保存方案</el-button
>
<el-button type="primary" plain>另存方案</el-button>
<el-button type="primary" plain>重置条件</el-button>
</el-button-group>
<div class="over-height">
<div v-for="item in tablePropList" :key="item.prop">
<!-- 允许筛选且显示状态下出现筛选列表 -->
<template v-if="item.isSwitch && item.isShow">
<el-form :inline="true" :model="item" class="demo-form-inline">
<el-form-item>
<el-select v-model="item.rules.a" placeholder="表格">
<el-option
:label="propItem.label"
:value="propItem.prop"
v-for="propItem in tablePropList"
:key="propItem.prop"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="item.rules.a" placeholder="并且">
<el-option label="并且" value="0" />
<el-option label="或" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="item.rules.b" placeholder="等于">
<template v-if="item.propType == 'string'">
<el-option label="等于" value="0" />
<el-option label="包含" value="1" />
<el-option label="大于" value="2" />
<el-option label="小于" value="3" />
</template>
<template v-else-if="item.propType == 'number'">
<el-option label="等于" value="0" />
<el-option label="大于" value="2" />
<el-option label="小于" value="3" />
</template>
<template v-else>
<el-option label="等于" value="0" />
<el-option label="大于" value="2" />
<el-option label="小于" value="3" />
</template>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="item.rules.value" />
</el-form-item>
<el-form-item>
<el-button :icon="Close" circle @click="exportBtn" />
</el-form-item>
</el-form>
</template>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="rulesTableData(1)">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
// npm i xlsx
// npm install element-plus --save
import { EditPen, Download, Search, Close } from "@element-plus/icons-vue";
import { ref } from "vue";
import { toRefs, defineProps, defineEmits } from "vue";
const emit = defineEmits(["tableRules"]);
// 弹窗布尔值
const dialogVisible = ref(false);
const dialogRulesVisible = ref(false);
// 接收值
const props = defineProps({
//子组件接收父组件传递过来的值
tableData: Array,
tablePropsList: Object,
});
//使用父组件传递过来的值
const { tableData, tablePropsList } = toRefs(props);
// 获取当前页的配置表
const localTable = JSON.parse(localStorage.getItem("table"));
var configuration = [];
var configurationRules = [];
if (localTable) {
configuration = localTable[tablePropsList.value.id].configuration;
configurationRules = localTable[tablePropsList.value.id].rules;
} else {
// 没有的话就新建一个保存
let obj = {
[tablePropsList.value.id]: {
configuration: [],
rules: [],
},
};
localStorage.setItem("table", JSON.stringify(obj));
}
const tablePropList = ref([]);
// 处理数据(原始传入数据,本地存储的数据,导出的数据)
const switchTableData = (tableData, localStorageData, addData) => {
// prop: 表格绑定字段, label:当前名, index:表格当前序列,width:表格当前宽度,align: 对齐方式 left左 center中 right右,isShow:是否显示 0显示,1隐藏
tableData.forEach((e, index) => {
if (localStorageData && localStorageData.length > 0) {
localStorageData.forEach((ec) => {
if (ec.prop == e.prop) {
let obj = {
prop: ec.prop,
label: ec.label,
index: ec.index,
width: ec.width,
isShow: ec.isShow,
align: ec.align,
fixed: ec.fixed,
children: [],
rules: ec.rules,
propType: ec.propType,
isSwitch: ec.isSwitch,
};
addData.push(obj);
if (e.children) {
switchTableData(e.children, ec.children, obj.children);
}
}
});
} else {
let obj = {
prop: e.prop,
label: e.label,
index: index,
width: e.width ? e.width : 0,
isShow: e.isShow ? e.isShow : "0",
align: e.align ? e.align : "center",
fixed: e.fixed ? e.fixed : "",
children: [],
rules: {},
propType: e.propType ? e.propType : "string",
isSwitch: e.isSwitch ? e.isSwitch : true,
};
addData.push(obj);
if (e.children) {
switchTableData(e.children, null, obj.children);
}
}
});
};
switchTableData(tablePropsList.value.props, configuration, tablePropList.value);
// 给数组排序
const sortTableData = (type) => {
// type 0 保存并确定 1 确定
tablePropList.value.sort(function (a, b) {
return a.index - b.index; // 根据升序排序
});
dialogVisible.value = false;
if (type == 0) {
let arr = JSON.parse(localStorage.getItem("table")) || {};
arr[tablePropsList.value.id].configuration = tablePropList.value;
localStorage.setItem("table", JSON.stringify(arr));
}
};
// 数据添加筛选规则
const rulesTableData = (type) => {
// type 0 保存并确定 1 确定
dialogRulesVisible.value = false;
let rulesList = [];
tablePropList.value.forEach((e) => {
rulesList.push(e.rules);
});
if (type == 0) {
let arr = JSON.parse(localStorage.getItem("table")) || {};
arr[tablePropsList.value.id].rules = configurationRules;
localStorage.setItem("table", JSON.stringify(arr));
}
//传递给父组件
emit("tableRules", rulesList);
};
// 导出功能
import * as XLSX from "xlsx";
const exportTableRef = ref(null);
const exportBtn = () => {
const tableDom = exportTableRef.value?.$el;
if (!tableDom) {
return;
}
const wb = XLSX.utils.table_to_book(tableDom);
XLSX.writeFile(wb, "表格数据.xlsx");
};
</script>
<style scoped>
.over-height {
max-height: 60vh;
overflow-y: auto;
}
.el-select,
.el-input {
width: 200px;
}
.table-btn {
float: right;
margin-bottom: 2px;
}
.last-table {
width: 94%;
margin: 0 auto;
}
</style>
demo
<template>
<div>
<h2>直接使用</h2>
<!--
tableData 是渲染表单的原始数据,
tablePropsList是表单的规则数据,
tableRules 是表单返回的筛选数据,数据在tableRules(val)方法中取返回值 val
-->
<tableVue
:tableData="tableData"
:tablePropsList="tablePropsList"
@tableRules="tableRules"
/>
<h2>添加操作等栏目</h2>
<tableVue :tableData="tableData" :tablePropsList="tablePropsList">
<!-- 该组件可继续添加表格数据 -->
<!-- 如果需要增加操作栏列等,可以按照以下格式添加 -->
<el-table-column fixed="right" label="Operations" width="120">
<template #default="scope">
<el-button link type="primary" size="small">
Remove{{ scope.$index }}
</el-button>
</template>
</el-table-column>
</tableVue>
</div>
</template>
<script setup>
import { ref } from "vue";
import tableVue from "./components/table.vue";
// 定义的数据
const tableData = ref([
{
id:1,
date: "2016-05-03",
name: "Aom1",
address: "No. 189, Grove St, Los Angeles",
count: 1,
address3: "address3",
address4: "address4",
},
{
id:2,
date: "2016-05-02",
name: "BTom2",
address: "No. 189, Grove St, Los Angeles",
count: 1,
address3: "address3",
address4: "address4",
},
{
id:3,
date: "2016-05-04",
name: "CTom",
address: "No. 189, Grove St, Los Angeles",
count: 1,
address3: "address3",
address4: "address4",
},
{
id:4,
date: "2016-05-01",
name: "DTom",
address: "No. 189, Grove St, Los Angeles",
count: 1,
address3: "address3",
address4: "address4",
},
]);
// 需要定义关键字和关键字的label
const tablePropsList = ref({
// id 唯一值
id: "text-table",
/**
* 必填项
* label为表格名
* prop对应表格的参数
*
* 选填
* width 表格宽度,0自适应,可填数字,默认自适应
* isShow 是否显示 0 显示,1不显示,默认0
* align 对其方式 left 左对齐, center居中对其,right:右对齐,默认center
* fixed 是否固定 left 固定左侧,right 固定右侧,空值不固定,默认空
* propType 数据类型,默认为空
* isSwitch 是否可筛选 默认true
*/
props: [
{ label: "姓名", prop: "name", propType: "string", isSwitch: true, isShow: "0"},
{ label: "时间", prop: "date", propType: "string", isSwitch: true, isShow: "0" },
{ label: "地址", prop: "address", propType: "string" , isSwitch: true, isShow: "0"},
{ label: "数量", prop: "count", propType: "number", isSwitch: true, isShow: "0" },
{
label: "其他地址",
prop: "address2",
// children 为子数据
children: [
{ label: "地址3", prop: "address3" , propType: "string", isSwitch: true, isShow: "0" },
{ label: "地址4", prop: "address4" , propType: "string", isSwitch: false , isShow: "0"},
],
},
],
});
// 接收子组件返回的筛选规则
const tableRules = (val) => {
console.log("rules", val);
};
</script>
<style scoped>
</style>