1、封装el-table组件。
<template>
<div ref="table-box" class="table-content-box">
<div>
<el-table
:data="data"
:stripe="stripe"
:size="size"
:empty-text="emptyText"
:height="newHeight || height"
:max-height="maxHeight"
v-bind="tableConfig"
@selection-change="selectionLineChangeHandle"
ref="zwTable"
:tree-props="treeProps"
:expand-row-keys="expandRowKeys"
:row-key="rowKey"
:default-expand-all="defaultExpandAll"
>
<el-table-column v-if="expand" type="expand" fixed="left">
<template slot-scope="{ row, $index }">
<slot name="expand" :row="row" :index="$index"></slot>
</template>
</el-table-column>
<template v-for="item in columns">
<template v-if="item.show">
<!-- 渲染 使用插槽 -->
<template v-if="item.useSlot">
<el-table-column
v-if="item.prop === 'action'"
:row-key="item.prop"
:key="item.prop"
:min-width="item.minWidth"
:align="item.align || 'center'"
:fixed="item.fixed || 'right'"
:show-overflow-tooltip="false"
v-bind="item"
>
<!-- 作用域 插槽 -->
<template slot-scope="{ row, column, index }">
<slot
name="columns"
:row="{ ...row, ...item }"
:column="column"
:index="index"
:value="row[item.prop]"
:prop="item.prop"
>
</slot>
</template>
</el-table-column>
<el-table-column
v-else-if="item.numSort"
:row-key="item.prop"
:key="item.prop"
:min-width="item.minWidth"
:align="item.align || 'center'"
:show-overflow-tooltip="false"
v-bind="item"
:sort-method="
(a, b) => {
return a[item.prop] - b[item.prop];
}
"
>
<!-- 作用域 插槽 -->
<template v-if="item.useTooltip" slot="header">
<el-tooltip
:content="item.content"
placement="top"
:open-delay="200"
>
<span
><i
class="el-icon-info"
style="color: #409eff; margin-right: 5px"
></i
>{{ item.label }}</span
>
</el-tooltip>
</template>
<template slot-scope="{ row, column, index }">
<slot
name="columns"
:row="{ ...row, ...item }"
:column="column"
:index="index"
:value="row[item.prop]"
:prop="item.prop"
>
</slot>
</template>
</el-table-column>
<el-table-column
v-else
:row-key="item.prop"
:key="item.prop"
:min-width="item.minWidth"
:align="item.align || 'center'"
:show-overflow-tooltip="false"
v-bind="item"
>
<!-- 作用域 插槽 -->
<template v-if="item.useTooltip" slot="header">
<el-tooltip
:content="item.content"
placement="top"
:open-delay="200"
>
<span
><i
class="el-icon-info"
style="color: #409eff; margin-right: 5px"
></i
>{{ item.label }}</span
>
</el-tooltip>
</template>
<template slot-scope="{ row, column, index }">
<slot
name="columns"
:row="{ ...row, ...item }"
:column="column"
:index="index"
:value="row[item.prop]"
:prop="item.prop"
>
</slot>
</template>
</el-table-column>
</template>
<!-- 渲染 操作 -->
<template v-else-if="item.actions && item.actions.length > 0">
<el-table-column
:row-key="item.prop"
:key="item.prop"
:align="item.align || 'center'"
:fixed="item.fixed || 'right'"
:show-overflow-tooltip="false"
v-bind="item"
>
<template slot-scope="{ row }">
<template v-for="newItem in item.actions">
<template
v-if="
newItem.mold === 'delete' || newItem.mold === 'twice'
"
>
<el-popconfirm
:title="newItem.title || '这一段内容确定删除吗?'"
:key="newItem.text"
@confirm="newItem.click(row)"
@cancel="() => {}"
>
<template slot="reference">
<el-tooltip
class="item"
effect="dark"
:content="newItem.text"
placement="top"
>
<el-button
style="margin-left: 8px; color: red"
:size="newItem.size || 'small '"
icon="el-icon-delete"
v-bind="newItem"
>删除</el-button
>
</el-tooltip>
</template>
</el-popconfirm>
</template>
<template v-else>
<el-tooltip
class="item"
effect="dark"
:content="newItem.text"
placement="top"
:key="newItem.text"
>
<el-button
v-if="newItem.text == '详情'"
@click="newItem.click(row)"
:type="newItem.type || primary"
:size="newItem.size || 'small '"
:icon="'el-icon-view'"
v-bind="newItem"
>{{ $t("xiang-qing") }}</el-button
>
<el-button
v-else-if="newItem.text == '修改'"
@click="newItem.click(row)"
:type="newItem.type || primary"
:size="newItem.size || 'small '"
:icon="'el-icon-edit-outline'"
v-bind="newItem"
>{{ $t("xiu-gai") }}</el-button
>
<el-button
v-else
@click="newItem.click(row)"
:type="newItem.type || primary"
:size="newItem.size || 'small '"
:icon="newItem.icon || 'el-icon-cherry'"
v-bind="newItem"
>{{ newItem.text }}</el-button
>
</el-tooltip>
</template>
</template>
</template>
</el-table-column>
</template>
<!-- 渲染 其他 -->
<template v-else>
<el-table-column
v-if="item.numSort"
:type="item.type"
:row-key="item.prop"
:min-width="item.minWidth"
:key="item.prop"
:align="item.align || 'center'"
:fixed="item.fixed"
:show-overflow-tooltip="false"
v-bind="item"
:sort-method="
(a, b) => {
return a[item.prop] - b[item.prop];
}
"
>
<template v-if="item.useTooltip" slot="header">
<el-tooltip
:content="item.content"
placement="top"
:open-delay="200"
>
<span
><i
class="el-icon-info"
style="color: #409eff; margin-right: 5px"
></i
>{{ item.label }}</span
>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
v-else
:type="item.type"
:row-key="item.prop"
:min-width="item.minWidth"
:key="item.prop"
:align="item.align || 'center'"
:fixed="item.fixed"
:show-overflow-tooltip="false"
v-bind="item"
>
<template v-if="item.useTooltip" slot="header">
<el-tooltip
:content="item.content"
placement="top"
:open-delay="200"
>
<span
><i
class="el-icon-info"
style="color: #409eff; margin-right: 5px"
></i
>{{ item.label }}</span
>
</el-tooltip>
</template>
</el-table-column>
</template>
</template>
</template>
</el-table>
</div>
<!--分页区域-->
<div class="table-content-foot" v-if="paging">
<div></div>
<div class="pagination">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div>
</div>
</div>
</template>
<script>
import { scrollTo } from "@/utils/scroll-to";
export default {
name: "TablePlus",
props: {
columns: {
type: Array,
default: () => [],
},
data: {
// 表格数据
type: Array,
default: () => [],
},
total: {
required: true,
type: Number,
},
page: {
type: Number,
default: 1,
},
limit: {
type: Number,
default: 20,
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50, 200];
},
},
// 移动端页码按钮的数量端默认值5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7,
},
layout: {
type: String,
default: "total, sizes, prev, pager, next, jumper",
},
background: {
type: Boolean,
default: true,
},
autoScroll: {
type: Boolean,
default: false,
},
selectIsShow: {
// 选择框是否显示
type: Boolean,
default: false,
},
idxIsShow: {
// 序列号是否显示
type: Boolean,
default: true,
},
height: {
type: [Number, String],
default: "calc(100vh - 15rem)",
},
maxHeight: {
type: [Number, String],
default: "100%",
},
emptyText: {
// 无数据时显示文字
type: String,
default: "暂无数据",
},
stripe: {
// 是否为斑马纹 table
type: Boolean,
default: false,
},
size: {
// Table 的尺寸
type: String,
default: "medium",
},
tableConfig: {
// 更多table 组件的配置
type: Object,
default: () => {
return {
headerCellStyle: {
height: "52px",
background: "#f8f7f8",
color: "#54585A",
fontWeight: 500,
fontSize: "14px",
fontFamily: "font-family: Source Han Sans CN, Source Han Sans CN",
padding: "0",
},
rowStyle: {
height: "72px ",
fontFamily: "font-family: Source Han Sans CN, Source Han Sans CN",
fontWeight: 400,
fontSize: "14px",
color: "#2B2C31",
},
cellStyle: {
padding: "0",
fontSize: "14px",
},
};
},
},
paging: {
// 分页
type: Boolean,
default: true,
},
treeProps: {
// 树形表格配置
type: Object,
default: () => {
return {};
},
},
expandRowKeys: {
// 展开行
type: Array,
default: () => [],
},
rowKey: {
// 行key
type: String,
default: "id",
},
defaultExpandAll: {
// 默认展开全部
type: Boolean,
default: false,
},
expand: {
// 是否可展开
type: Boolean,
default: false,
},
},
data() {
return {
// 复选框选中的行
selectRows: [],
// 内部高度,动态计算
innerHeight: 0,
// 行编辑开启的行索引集合
editRowsKey: [],
// 行编辑的编辑数据,key值为行索引
editRowsData: {},
// 按钮区域的高度 TODO: 暂时写死
buttonHeight: 42,
newHeight: 0,
rowBackground: {
background: "#ffff66",
},
on_view: require("@/assets/operate_icons/on_view.png"),
on_edit: require("@/assets/operate_icons/on_edit.png"),
on_delete: require("@/assets/operate_icons/on_delete.png"),
on_other: require("@/assets/operate_icons/on_other.png"),
};
},
updated() {
// 解决表格抖动
this.$refs.zwTable.doLayout();
setTimeout(() => {
this.$forceUpdate();
}, 5);
},
computed: {
currentPage: {
get() {
return this.page;
},
set(val) {
this.$emit("update:page", val);
},
},
pageSize: {
get() {
return this.limit;
},
set(val) {
this.$emit("update:limit", val);
},
},
},
methods: {
// 当选择项发生变化时会触发该事件
selectionLineChangeHandle(val) {
this.$emit("selectionLineChangeHandle", val);
},
// 对外暴露table实列
createTable() {
this.$emit("createTable", this.$refs.zwTable);
},
// 设置某个选中
toggleRowSelection(val) {
this.$refs.zwTable.toggleRowSelection();
},
// 设置全选
toggleAllSelection() {
this.$refs.zwTable.toggleAllSelection();
},
handleSizeChange(val) {
this.$emit("pagination", { page: this.currentPage, limit: val });
if (this.autoScroll) {
scrollTo(0, 800);
}
},
handleCurrentChange(val) {
this.$emit("pagination", { page: val, limit: this.pageSize });
if (this.autoScroll) {
scrollTo(0, 800);
}
},
},
};
</script>
<style lang="scss" scoped>
.table-content-box {
.table-content-foot {
margin-top: 10px;
display: flex;
justify-content: space-between;
}
}
</style>
2、创建一个mixins文件夹,新建一个TablePlusMixin.js ,这是混入js文件,放在vue里。这里的接口是保存自定义表到服务器的,也可以不要。
import{ listColumn, addColumn, updateColumn } from "@/api/column.js"
export const TablePlusMixin = {
data() {
return {
columns: [], // 后台显示
columnsHide: [], // 后台不显示
columnProp: [], //服务器获取列表的prop
url: this.$route.path,
userId: this.$store.state.user.userId,
id: null,
allColumnProp:[],
};
},
watch: {
columnsHide: {
handler(newV, oldV) {
this.columns.forEach((item) =>{
if(newV.includes(item.prop)){
item.show = true
}else{
item.show = false
}
});
},
immediate: true,
deep: true,
},
},
created(){
this.getColumnProp()
},
methods: {
// 改变列表位置
changeListIdx({ oldIndex, newIndex }) {
let obj = this.columns.splice(oldIndex, 1);
this.$nextTick(() => {
this.columns.splice(newIndex, 0, obj[0]);
});
},
updateColumnsHide(val){
this.columnsHide = val
},
//获取表头
async getColumnProp(){
this.loading = true
let params = {
url: this.url,
userId: this.userId
}
this.allColumnProp = this.columnsList.map(item => item.prop)
await listColumn(params).then( res => {
let tempres = res.rows[0]
if(res.rows.length == 0){
this.columnProp = this.allColumnProp
}else{
this.id = tempres.id
let obj = JSON.parse(tempres.columnName)
let check = []
for( let key in obj){
if(obj[key] === 1){
check.push(key)
}
}
this.columnProp = check //已经选中的prop
this.columnsList = this.sortList(obj ,this.columnsList)
}
this.loading = false
}).catch(err => {
this.columnProp = this.allColumnProp
this.loading = false
})
this.columns = this.columnsList.map( item =>{
if(this.columnProp.includes(item.prop)){
return {
...item,
show: true
}
}else{
return {
...item,
show: false
}
}
})
},
//编辑表头
async updateColumnProp(){
let columnName = {}
this.columnProp = this.columns.map(item => {
if(item.show){
columnName[item.prop] = 1
return item.prop
}
else{
columnName[item.prop] = 0
}
})
let params3= {
id: this.id,
url: this.url,
userId: this.userId,
columnName: JSON.stringify(columnName),
}
await addColumn(params3)
},
// 排序 list按照此顺序排序数组, needSortList要排序的对象数组, 按照prop属性升序排序
sortList(obj, needSortList, prop = 'sortId') {
let list = Object.keys(obj)
needSortList.forEach( item => {
let sortId = list.indexOf(item.prop)
if(sortId === -1){
item.sortId = needSortList.length
}else{
item.sortId = sortId
}
})
return needSortList.sort((a, b) => {
return a.sortId - b.sortId;
});
},
},
};
<template>
<div class="toolBar ml10">
<el-button
v-if="isFilter"
size="small"
:icon="iconData"
@click="toggleSearch()"
>高级筛选</el-button
>
<el-tooltip
v-if="isRefresh"
class="item"
effect="dark"
content="刷新"
placement="top"
>
<el-button
size="small"
circle
icon="el-icon-refresh"
@click="refresh()"
/>
</el-tooltip>
<el-tooltip
v-if="columnsList.length > 0"
class="item"
effect="dark"
content="显隐列"
placement="top"
>
<el-button size="small" icon="el-icon-setting" @click="open = !open" />
</el-tooltip>
<el-dialog
v-dialogDrag
:close-on-click-modal="false"
:title="title"
width="864px"
:visible.sync="open"
append-to-body
@opened="handleDialogOpened"
>
<el-card>
<div slot="header" class="clearfix">
<span>表头(拖动以调换排序顺序)</span>
</div>
<el-checkbox-group
v-model="value"
@change="dataChange"
ref="el-checkbox"
>
<el-checkbox
v-for="(item, idx) in columnsList"
:label="item.prop"
:key="idx"
>{{ item.label }}</el-checkbox
>
</el-checkbox-group>
<div style="height: 40px; padding-top: 20px">
注:点击确定将表头设置保存于服务器
</div>
</el-card>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submit">确定</el-button>
<el-button @click="cancel">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import Sortable from "sortablejs";
export default {
name: "RightToolbar",
data() {
return {
// 显隐数据
value: [],
// 弹出层标题
title: "表单设置",
// 是否显示弹出层
open: false,
oldIndex: "", //旧的排序
newIndex: "", //新的排序
// checkOptions: [], //全部选项
iconData: "el-icon-arrow-down",
};
},
props: {
showSearch: {
type: Boolean,
default: true,
},
columnsList: {
type: Array,
default: () => [],
},
isFilter: {
//是否显示高级筛选按钮
type: Boolean,
default: true,
},
isRefresh: {
//是否显示刷新按钮
type: Boolean,
default: false,
},
checkOptions: {
type: Array,
default: () => [],
},
search: {
type: Boolean,
default: true,
},
gutter: {
type: Number,
default: 10,
},
},
computed: {
style() {
const ret = {};
if (this.gutter) {
ret.marginRight = `${this.gutter / 2}px`;
}
return ret;
},
},
watch: {
checkOptions() {
this.value = this.checkOptions;
},
},
methods: {
// 显隐搜索
toggleSearch() {
if (this.iconData == "el-icon-arrow-down") {
this.iconData = "el-icon-arrow-up";
} else {
this.iconData = "el-icon-arrow-down";
}
this.$emit("update:showSearch", !this.showSearch);
},
// 刷新
refresh() {
this.$emit("queryTable");
},
// 勾选改变
dataChange() {
this.$emit("updateColumnsHide", this.value);
},
Sortable() {
// const aa = document.querySelector('.el-checkbox-group')
const aa = this.$refs["el-checkbox"].$el;
new Sortable(aa, {
group: {
name: "shared",
pull: [false],
},
handle: ".el-checkbox",
draggable: ".el-checkbox",
animation: 150,
onEnd: (evt) => {
console.log(evt);
const { oldIndex, newIndex } = evt;
this.$emit("changeListIdx", { oldIndex, newIndex });
},
});
},
// 取消按钮
cancel() {
this.open = false;
this.value = this.checkOptions;
},
//确认按钮
submit() {
// this.dataChange()
// this.$emit('changeListIdx', { oldIndex: this.oldIndex, newIndex: this.newIndex })
this.$emit("updateColumnProp");
this.open = false;
},
handleDialogOpened() {
this.Sortable();
},
},
};
</script>
<style lang="scss" scoped>
.toolBar {
position: relative;
display: inline-block;
}
.el-checkbox {
padding: 10px 10px;
}
</style>
4、零件准备好了,开始组装使用。
<template>
<div class="app-container">
<right-toolbar
:isFilter="false"
:columnsList="columnsList"
:checkOptions="columnProp"
@updateColumnsHide="updateColumnsHide"
@changeListIdx="changeListIdx"
@updateColumnProp="updateColumnProp"
></right-toolbar>
<TablePlus
ref="tableplus"
v-loading="loading"
:columns="columns"
:data="accountList"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
:page-sizes="[10, 20, 30, 50, 200]"
@pagination="getList"
>
<!-- 作用域插槽 -->
<template #columns="scope">
<!-- scope.prop: 属性名 -->
<!-- scope.value:属性值 -->
<!-- scope.row:所有属性 -->
<template v-if="scope.prop === 'accessoryCategoryId'">
<AccessoryTypeTag
:value="scope.row.accessoryCategoryId"
></AccessoryTypeTag>
</template>
<template v-if="scope.prop === 'action'">
<el-button
size="small"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['accessory:account:edit']"
:disabled="!checkExample(scope.row.createBy)"
>{{ $t("xiu-gai") }}</el-button
>
<el-button
size="small"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['accessory:account:remove']"
:disabled="!checkExample(scope.row.createBy)"
>{{ $t("shan-chu") }}</el-button
>
</template>
</template>
</TablePlus>
</div>
</template>
<script>
import { TablePlusMixin } from "@/mixins/TablePlusMixin";
export default {
name: "Account",
mixins: [TablePlusMixin],
data() {
return {
//label是表头名称,prop是对应的字段,useSlot是使用插槽,其他的都和el-table对应。
columnsList: [
{
label: this.$t("xu-hao"),
prop: "index",
type: "index",
align: "center",
width: "50px",
fixed: "left",
},
{
label: this.$t("pei-jian-bian-hao-0"),
prop: "accessoryCode",
minWidth: "140px",
align: "center",
sortable: true, // 开启排序
sortOrders: ["ascending", "descending", null],
},
{
label: this.$t("pei-jian-fen-lei-0"),
prop: "accessoryCategoryId",
minWidth: "180px",
align: "center",
sortable: true,
sortOrders: ["ascending", "descending", null],
useSlot: true,
},
{
label: this.$t("dan-wei-0"),
prop: "unit",
minWidth: "80px",
align: "center",
},
{
width: "130px",
prop: "action",
label: "操作",
useSlot: true,
},
],
};
},
computed: {
onSearchChange() {
if (this.showSearch) {
return "calc(100vh - 17rem)";
} else {
return "calc(100vh - 12rem)";
}
},
},
created() {
countVisits({
client: 0,
pageId: 2141,
pageName: "配件台账",
});
},
methods: {
},
};
</script>
<style lang="scss" scoped>
</style>
5、 效果展示。