vue3.x版本,将table进行了封装。使用起来更方便了。但是,有时候我们需要将一组信息显示到一列中。所以我将其进行了简单的二次改造。支持table-column自定义。
table改造代码
<template>
<div class="table-container">
<el-table
:data="data"
:border="setBorder"
v-bind="$attrs"
row-key="id"
stripe
style="width: 100%"
v-loading="config.loading"
@selection-change="onSelectionChange"
>
<el-table-column type="selection" :reserve-selection="true" width="30" v-if="config.isSelection" />
<el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo" />
<el-table-column
v-for="(item, index) in setHeader"
:key="index"
show-overflow-tooltip
:prop="item.key"
:width="item.colWidth"
:label="item.title"
>
<template v-slot="scope">
<template v-if="item.isCustom">
<slot :name="item.key" :row="scope.row"/>
</template>
<template v-else>
<template v-if="item.type === 'image'">
<el-image
:style="{ width: `${item.width}px`, height: `${item.height}px` }"
:src="scope.row[item.key]"
:zoom-rate="1.2"
:preview-src-list="[scope.row[item.key]]"
preview-teleported
fit="cover"
/>
</template>
<template v-else>
{{ scope.row[item.key] }}
</template>
</template>
</template>
</el-table-column>
<el-table-column label="操作" min-width="80" v-if="config.isOperate">
<template v-slot="scope">
<div>
<slot name="other" :row="scope.row"/>
<el-button v-if="config.isEdit" text type="primary" @click="onEditRow(scope.row)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="onDelRow(scope.row)" v-if="config.isDel">
<template #reference>
<el-button text type="danger">删除</el-button>
</template>
</el-popconfirm>
</div>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" />
</template>
</el-table>
<div class="table-footer mt15" v-if="!config.hideFooter">
<el-pagination
v-model:current-page="state.page.pageNumber"
v-model:page-size="state.page.pageSize"
:pager-count="5"
:page-sizes="[10, 20, 30]"
:total="config.total"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
>
</el-pagination>
<div class="table-footer-tool">
<SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" @click="onImportTable" />
<SvgIcon name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" />
<el-popover
placement="top-end"
trigger="click"
transition="el-zoom-in-top"
popper-class="table-tool-popper"
:width="300"
:persistent="false"
@show="onSetTable"
>
<template #reference>
<SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" />
</template>
<template #default>
<div class="tool-box">
<el-tooltip content="拖动进行排序" placement="top-start">
<SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
</el-tooltip>
<el-checkbox
v-model="state.checkListAll"
:indeterminate="state.checkListIndeterminate"
class="ml10 mr1"
label="列显示"
@change="onCheckAllChange"
/>
<el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" />
<el-checkbox v-model="getConfig.isSelection" class="ml12 mr1" label="多选" />
</div>
<el-scrollbar>
<div ref="toolSetRef" class="tool-sortable">
<div class="tool-sortable-item" v-for="v in header" :key="v.key" :data-key="v.key">
<i class="fa fa-arrows-alt handle cursor-pointer"></i>
<el-checkbox v-model="v.isCheck" size="default" class="ml12 mr8" :label="v.title" @change="onCheckChange" />
</div>
</div>
</el-scrollbar>
</template>
</el-popover>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="netxTable">
import { reactive, computed, nextTick, ref } from 'vue';
import { ElMessage } from 'element-plus';
import printJs from 'print-js';
import table2excel from 'js-table2excel';
import Sortable from 'sortablejs';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import '/@/theme/tableTool.scss';
// 定义父组件传过来的值
const props = defineProps({
// 列表内容
data: {
type: Array<EmptyObjectType>,
default: () => [],
},
// 表头内容
header: {
type: Array<EmptyObjectType>,
default: () => [],
},
// 配置项
config: {
type: Object,
default: () => {},
},
// 打印标题
printName: {
type: String,
default: () => '',
},
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['delRow', 'pageChange', 'sortHeader']);
// 定义变量内容
const toolSetRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
page: {
pageNumber: 1,
pageSize: 10,
},
selectlist: [] as EmptyObjectType[],
checkListAll: true,
checkListIndeterminate: false,
});
// 设置边框显示/隐藏
const setBorder = computed(() => {
return props.config.isBorder ? true : false;
});
// 获取父组件 配置项(必传)
const getConfig = computed(() => {
return props.config;
});
// 设置 tool header 数据
const setHeader = computed(() => {
return props.header.filter((v) => v.isCheck);
});
// tool 列显示全选改变时
const onCheckAllChange = <T>(val: T) => {
if (val) props.header.forEach((v) => (v.isCheck = true));
else props.header.forEach((v) => (v.isCheck = false));
state.checkListIndeterminate = false;
};
// tool 列显示当前项改变时
const onCheckChange = () => {
const headers = props.header.filter((v) => v.isCheck).length;
state.checkListAll = headers === props.header.length;
state.checkListIndeterminate = headers > 0 && headers < props.header.length;
};
// 表格多选改变时,用于导出
const onSelectionChange = (val: EmptyObjectType[]) => {
state.selectlist = val;
};
// 删除当前项
const onDelRow = (row: EmptyObjectType) => {
emit('delRow', row);
};
// 编辑当前项
const onEditRow = (row: EmptyObjectType) => {
emit('editRow', row);
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.page.pageSize = val;
emit('pageChange', state.page);
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.page.pageNumber = val;
emit('pageChange', state.page);
};
// 搜索时,分页还原成默认
const pageReset = () => {
state.page.pageNumber = 1;
state.page.pageSize = 10;
emit('pageChange', state.page);
};
// 导出
const onImportTable = () => {
if (state.selectlist.length <= 0) return ElMessage.warning('请先选择要导出的数据');
table2excel(props.header, state.selectlist, `${themeConfig.value.globalTitle} ${new Date().toLocaleString()}`);
};
// 刷新
const onRefreshTable = () => {
emit('pageChange', state.page);
};
// 设置
const onSetTable = () => {
nextTick(() => {
const sortable = Sortable.create(toolSetRef.value, {
handle: '.handle',
dataIdAttr: 'data-key',
animation: 150,
onEnd: () => {
const headerList: EmptyObjectType[] = [];
sortable.toArray().forEach((val: string) => {
props.header.forEach((v) => {
if (v.key === val) headerList.push({ ...v });
});
});
emit('sortHeader', headerList);
},
});
});
};
// 暴露变量
defineExpose({
pageReset,
});
</script>
<style scoped lang="scss">
.table-container {
display: flex;
flex-direction: column;
.el-table {
flex: 1;
}
.table-footer {
display: flex;
.table-footer-tool {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
i {
margin-right: 10px;
cursor: pointer;
color: var(--el-text-color-regular);
&:last-of-type {
margin-right: 0;
}
}
}
}
}
</style>