vue3 动态配置element 的table
element
A Vue.js 2.0 UI Toolkit for Web
项目地址:https://gitcode.com/gh_mirrors/eleme/element
免费下载资源
·
需求
合并行、合并标题、列宽可调整、列顺序可调整、可以控制列是否显示、列布局可保存、导出excel…
参考效果
代码
引入
npm i xlsx
npm install element-plus --save
table组件
<template>
<div>
<div class="table-btn">
<div class="left-btn">
<slot name="tableBtn" />
</div>
<div class="right-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>
</div>
<el-table @sort-change="sortTableDataChange"
:data="tableData"
style="width: 100%"
ref="exportTableRef"
border
v-loading="state.loading"
:cell-style="tableStyle?.cellStyle"
:header-cell-style="tableStyle?.headerCellStyle"
>
<template v-for="item in tablePropList">
<el-table-column
sortable="custom"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:align="item.align"
:width="item.width == 0 ? '' : item.width"
:fixed="item.fixed"
v-if="item.showable && item.children.length > 0"
>
<template v-for="itemChildren in item.children">
<el-table-column
:key="itemChildren.prop"
sortable="custom"
:prop="itemChildren.prop"
:label="itemChildren.label"
:align="itemChildren.align"
:width="itemChildren.width == 0 ? '' : itemChildren.width"
v-if="itemChildren.showable"
>
</el-table-column>
</template>
</el-table-column>
<el-table-column
sortable="custom"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:align="item.align"
:width="item.width == 0 ? '' : item.width"
:fixed="item.fixed"
v-if="item.showable && item.children.length == 0 && !item.isSlot"
>
<template #default="scope">
{{ scope.row[item.prop] }}
</template>
</el-table-column>
<el-table-column
sortable="custom"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:align="item.align"
:width="item.width == 0 ? '' : item.width"
:fixed="item.fixed"
v-if="item.showable && item.children.length == 0 && item.isSlot"
>
<template #default="scope">
<slot :name="item.prop" v-bind="scope" :msg="scope.row[item.prop]" />
<!-- {{ scope.row[item.prop] }}123 -->
</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="prop" label="API名"> </el-table-column>
<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 v-model="scope.row.width" class="w120" />
</template>
</el-table-column>
<el-table-column prop="date" label="对齐方式">
<template #default="scope">
<el-radio-group v-model="scope.row.align">
<el-radio-button label="left">左对齐</el-radio-button>
<el-radio-button label="center">居中</el-radio-button>
<el-radio-button label="right">右对齐</el-radio-button>
</el-radio-group>
</template>
</el-table-column>
<el-table-column prop="date" label="是否固定">
<template #default="scope">
<el-radio-group v-model="scope.row.fixed">
<el-radio-button label="left" >左侧固定</el-radio-button>
<el-radio-button label="" >不固定</el-radio-button>
<el-radio-button label="right" >右侧固定</el-radio-button>
</el-radio-group>
</template>
</el-table-column>
<el-table-column prop="date" label="是否显示">
<template #default="scope">
<el-radio-group v-model="scope.row.showable">
<el-radio-button :label="true" >显示</el-radio-button>
<el-radio-button :label="false" >隐藏</el-radio-button>
</el-radio-group>
</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%">
<div class="dialog-header">
<div>
<el-button-group class="ml-4">
<el-button type="primary" plain @click="addRulesTable">新增筛选</el-button>
<el-button type="primary" plain @click="resetting()">重置条件</el-button>
</el-button-group>
</div>
<div>
<el-button-group class="ml-4">
<el-button type="primary" plain @click="rulesTableData(0)">保存方案</el-button>
<el-button type="primary" plain @click="rulesTableData(2)">另存方案</el-button>
</el-button-group>
<el-select v-model="selectRulesCount" @change="rulesSelect()" placeholder="选择方案">
<el-option v-for="(item, index) in configurationRules" :key="index" :label="item.name" :value="index" />
</el-select>
</div>
</div>
<div class="over-height">
<el-table :data="tableRulesList" style="width: 100%" row-key="prop" :tree-props="{ children: 'filters' }">
<el-table-column prop="field" label="列名">
<template #default="scope">
<el-select v-model="scope.row.field" @change="rulesPropsChange(scope.row)">
<el-option v-for="item in tablePropsList.props" :key="item.prop" :label="item.label" :value="item.prop" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="operator" label="比较运算符">
<template #default="scope">
<el-select v-model="scope.row.operator" :disabled="scope.row.field == ''" @change="switchType(scope.row, scope.row.operator)">
<el-option label="等于" value="eq" v-show="scope.row.type == 'number' || scope.row.type == 'string' || scope.row.type == 'date'" />
<el-option label="不等于" value="ne" v-show="scope.row.type == 'number' || scope.row.type == 'string' || scope.row.type == 'date'" />
<el-option label="大于" value="gt" v-show="scope.row.type == 'number' || scope.row.type == 'date'" />
<el-option label="大于或等于" value="ge" v-show="scope.row.type == 'number' || scope.row.type == 'date'" />
<el-option label="小于" value="lt" v-show="scope.row.type == 'number' || scope.row.type == 'date'" />
<el-option label="小于或等于" value="le" v-show="scope.row.type == 'number' || scope.row.type == 'date'" />
<el-option label="介于" value="between" v-show="scope.row.type == 'date'" />
<el-option label="不介于" value="notBetween" v-show="scope.row.type == 'date'" />
<el-option label="包含" value="like" v-show="scope.row.type == 'string'" />
<el-option label="不包含" value="notLike" v-show="scope.row.type == 'string'" />
<el-option label="以…开始" value="likeLeft" v-show="scope.row.type == 'string'" />
<el-option label="以…结束" value="likeRight" v-show="scope.row.type == 'string'" />
<el-option label="空" value="isNull" v-show="scope.row.type == 'string'" />
<el-option label="非空" value="isNotNUll" v-show="scope.row.type == 'string'" />
<el-option label="在列表之中" value="in" v-show="scope.row.type == 'number' || scope.row.type == 'string'" />
<el-option label="不在列表之中" value="notIn" v-show="scope.row.type == 'number' || scope.row.type == 'string'" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="value" label="值">
<template #default="scope">
<template v-if="scope.row.operator != 'in' && scope.row.operator != 'notIn'">
<el-input v-if="scope.row.type == 'string'" v-model="scope.row.value" :disabled="scope.row.field == ''" />
<el-input-number
v-if="scope.row.type == 'number'"
v-model="scope.row.value"
:min="0"
:max="1000000"
:disabled="scope.row.field == ''"
/>
<el-date-picker
v-if="
(scope.row.type == 'date' && scope.row.operator == 'between') || (scope.row.type == 'date' && scope.row.operator == 'notBetween')
"
v-model="scope.row.value"
type="daterange"
range-separator="To"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
<el-date-picker
v-if="scope.row.type == 'date' && scope.row.operator != 'between' && scope.row.operator != 'notBetween'"
v-model="scope.row.value"
type="date"
/>
</template>
<el-select
multiple
v-if="
(scope.row.type == 'number' && scope.row.operator == 'in') ||
(scope.row.type == 'number' && scope.row.operator == 'notIn') ||
(scope.row.type == 'string' && scope.row.operator == 'in') ||
(scope.row.type == 'string' && scope.row.operator == 'notIn')
"
v-model="scope.row.value"
>
<el-option v-for="item in scope.row.inList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="logic" label="逻辑运算符">
<template #default="scope">
<el-select v-model="scope.row.logic" :disabled="scope.row.field == ''">
<el-option label="并且" value="and" />
<el-option label="或者" value="or" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="value" label="操作">
<template #default="scope">
<el-tooltip content="嵌套过滤条件" placement="bottom" effect="light">
<el-button type="primary" plain :icon="Plus" @click="addchildrenTable(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="移除当前筛选" placement="bottom" effect="light">
<el-button type="primary" plain :icon="Minus" @click.prevent="deleteRow(scope.$index, scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="rulesTableData(1)">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="dialogFormVisible" title="请为该规则添加一个名字">
<el-input v-model="ruleName" autocomplete="off" />
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="addRulesName">确定</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, Plus, Minus } from '@element-plus/icons-vue';
import { ref } from 'vue';
import { toRefs, defineProps, defineEmits } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table';
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 state: BasicTableProps = reactive<BasicTableProps>({});
// 表单的配置项,根据回传的数据判断是否用哪个值
const { getDataList, currentChangeHandle, sizeChangeHandle, downBlobFile, tableStyle } = useTable(tablePropsList.value.state ?? state);
// 获取当前页的配置表
const localTable = JSON.parse(localStorage.getItem('table'));
const configuration = ref([]);
const configurationRules = ref([]);
if (localTable && localTable[tablePropsList.value.id]) {
configuration.value = localTable[tablePropsList.value.id].configuration;
configurationRules.value = 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右,showable:是否显示 true显示,false隐藏
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,
showable: ec.showable,
inList: ec.inList ? ec.inList : null,
align: ec.align,
fixed: ec.fixed,
children: [],
rules: ec.rules,
type: ec.type,
filterable: ec.filterable,
isSlot: ec.isSlot,
};
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,
showable: e.showable ? e.showable : true,
inList: e.inList ? e.inList : null,
align: e.align ? e.align : 'center',
fixed: e.fixed ? e.fixed : false,
children: [],
rules: {},
type: e.type ? e.type : 'string',
filterable: e.filterable ? e.filterable : false,
isSlot: e.isSlot ? e.isSlot : false,
};
addData.push(obj);
if (e.children) {
switchTableData(e.children, null, obj.children);
}
}
});
};
switchTableData(tablePropsList.value.props, configuration.value, 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 selectRulesItem = ref({
name: '',
rules: [],
});
// 选中缓存的规则
const selectRulesCount = ref();
const rulesSelect = (val) => {
tableRulesList.value = configurationRules.value[selectRulesCount.value].rules;
selectRulesItem.value = configurationRules.value[selectRulesCount.value];
};
const dialogFormVisible = ref(false);
const tableRulesList = ref([]);
// 数据添加筛选规则
const rulesTableData = (type) => {
// type 0 保存并确定 1 确定 2 新建
dialogRulesVisible.value = false;
let rulesList = {
logic: 'and',
field: '',
operator: '',
value: '',
sort: [sortTableDataSort.value],
filters: {},
};
rulesList.filters = tableRulesList.value;
// tablePropList.value.forEach((e) => {
// rulesList.sort.push({
// field: e.prop,
// dir: e.index,
// });
// });
if (type == 2) {
dialogFormVisible.value = true;
}
if (type == 0) {
if (selectRulesItem.value.name) {
configurationRules.value[selectRulesCount.value].rules = tableRulesList.value;
let arr = JSON.parse(localStorage.getItem('table')) || {};
arr[tablePropsList.value.id].rules = configurationRules.value;
localStorage.setItem('table', JSON.stringify(arr));
setTimeout(() => {
let localTable = JSON.parse(localStorage.getItem('table'));
configuration.value = localTable[tablePropsList.value.id].configuration;
configurationRules.value = localTable[tablePropsList.value.id].rules;
}, 100);
} else {
dialogFormVisible.value = true;
}
}
//传递给父组件
emit('tableRules', rulesList);
};
const sortTableDataSort = ref({
field: '',
dir: 'asc',
})
const sortTableDataChange = (column, prop, order) => {
sortTableDataSort.value.field = column.prop
sortTableDataSort.value.dir = column.order=="ascending"?'asc':'desc'
rulesTableData(1)
}
import { ElMessage } from 'element-plus';
const ruleName = ref('');
const addRulesName = () => {
let filteredNumbers = configurationRules.value.filter((ev) => ev.name == ruleName.value);
if (filteredNumbers.length == 0) {
let obj = {
name: ruleName.value,
rules: tableRulesList.value,
};
configurationRules.value.push(obj);
let arr = JSON.parse(localStorage.getItem('table')) || {};
arr[tablePropsList.value.id].rules = configurationRules.value;
localStorage.setItem('table', JSON.stringify(arr));
dialogFormVisible.value = false;
setTimeout(() => {
let localTable = JSON.parse(localStorage.getItem('table'));
configuration.value = localTable[tablePropsList.value.id].configuration;
configurationRules.value = localTable[tablePropsList.value.id].rules;
}, 100);
} else {
ElMessage({
message: '重名,请换个名称',
type: 'warning',
});
}
};
const addRulesTable = (type) => {
tableRulesList.value.push({
logic: 'and',
field: '',
operator: '',
value: '',
fieldCount: tableRulesList.value.length * Math.floor(Math.random() * 10000),
});
};
const rulesPropsChange = (item) => {
let filteredNumbers = tablePropsList.value.props.filter((ev) => ev.prop == item.field);
item.label = filteredNumbers[0].label;
item.prop = filteredNumbers[0].prop;
item.type = filteredNumbers[0].type;
item.filterable = filteredNumbers[0].filterable;
item.showable = filteredNumbers[0].showable;
item.inList = filteredNumbers[0].inList;
item.value = '';
item.operator = '';
switchType(item, item.type);
};
// 新增子项筛选
const addchildrenTable = (item) => {
if (item.hasChildren == null) {
if (!item.filters) {
item.filters = new Array();
}
item.filters.push({
logic: 'and',
field: '',
operator: '',
value: '',
hasChildren: item.fieldCount, // 是否是子项数据 ,以及是哪一条的子项数据
fieldCount: tableRulesList.value.length * Math.floor(Math.random() * 10000),
});
} else {
tableRulesList.value.forEach((e) => {
if (e.fieldCount == item.hasChildren) {
e.filters.push({
logic: 'and',
field: '',
operator: '',
value: '',
hasChildren: e.fieldCount, // 是否是子项数据 ,以及是哪一条的子项数据
fieldCount: tableRulesList.value.length * Math.floor(Math.random() * 10000),
});
}
});
}
};
const deleteRow = (index, item) => {
if (item.hasChildren != null) {
tableRulesList.value.forEach((e) => {
if (e.fieldCount == item.hasChildren) {
e.filters = e.filters.filter((es) => es.fieldCount != item.fieldCount);
}
});
}
if (item.hasChildren == null) {
tableRulesList.value = tableRulesList.value.filter((e) => e.fieldCount != item.fieldCount);
}
};
const resetting = () => {
tableRulesList.value = [];
};
const switchType = (row, type) => {
if (type == 'between' || type == 'notBetween' || type == 'array') {
row.value = new Array();
}
if (type == 'string') {
row.value = new String();
}
if (type == 'number') {
row.value = new Number();
}
};
// 导出功能
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, tablePropsList.value.exportXlsxName ?? '表格数据.xlsx');
};
</script>
<style scoped>
.over-height {
max-height: 60vh;
overflow-y: auto;
}
.el-select {
width: 120px;
}
.el-input {
width: 200px;
}
.w120 {
width: 120px;
}
.table-btn {
display: flex;
justify-content: space-between;
margin-bottom: 2px;
}
.last-table {
width: 94%;
margin: 0 auto;
}
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
}
</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">
<!-- 顶部增加按钮 -->
<template #tableBtn>
<el-button>123</el-button>
<el-button>123</el-button>
<el-button>123</el-button>
</template>
<!-- 该组件可继续添加表格数据 -->
<!-- 如果需要增加操作栏列等,可以按照以下格式添加 -->
<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',
// 导出表格的文件名,非必填
exportXlsxName: 'xlsx-table',
// 非必填,表单的配置项
state: state,
/**
* 必填项
* label为表格名
* prop对应表格的参数
*
* 选填
* width 表格宽度,0自适应,可填数字,默认自适应
* showable 是否显示 0 显示,1不显示,默认0
* align 对其方式 left 左对齐, center居中对其,right:右对齐,默认center
* fixed 是否固定 left 固定左侧,right 固定右侧,空值不固定,默认空
* type 数据类型,默认为空
* filterable 是否可筛选 默认true
* inList 当该属性的 operator 值可能为 in/notIn 时,可以通过配置 inList 来为其增加筛选项,不配置默认为null,并且 operator 值为 in/notIn 时,选择器无选项
*/
props: [
{
label: '姓名',
prop: 'name',
type: 'string',
filterable: true,
showable: true,
},
{
label: '时间',
prop: 'date',
type: 'date',
filterable: true,
showable: true,
},
{
label: '地址',
prop: 'address',
type: 'string',
filterable: true,
showable: true,
inList: [
{
value: 1,
label: '草稿',
},
{
value: 2,
label: '审批中',
},
{
value: 3,
label: '已完成',
},
],
},
{
label: '数量',
prop: 'count',
type: 'number',
filterable: true,
showable: true,
},
{
label: '状态',
prop: 'count',
type: 'number',
filterable: true,
showable: true,
isSlot: true,
},
{
label: '其他地址',
prop: 'address2',
showable: true,
// children 为子数据
children: [
{
label: '地址3',
prop: 'address3',
type: 'string',
filterable: true,
showable: true,
},
{
label: '地址4',
prop: 'address4',
type: 'string',
filterable: false,
showable: true,
},
],
},
],
});
// 接收子组件返回的筛选规则
const tableRules = (val) => {
console.log('rules', val);
};
</script>
<style scoped>
</style>
GitHub 加速计划 / eleme / element
54.06 K
14.63 K
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:1 个月前 )
c345bb45
5 个月前
a07f3a59
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update table.md
* Update transition.md
* Update popover.md 5 个月前
更多推荐
已为社区贡献2条内容
所有评论(0)