1、封装table组件
// tableBox.vue
<template>
<div class="table-c">
<a-table
:expandIcon="customIcon"
:columns="tableColumns"
:data-source="dataSource"
:pagination="pagination"
:loading="loading"
:rowSelection="rowSelection"
@change="change"
@expand="expand"
>
<template
v-for="i in tableColumns"
:key="i.key"
v-slot:[i.slots?.customRender]="{ record }"
>
<slot :name="i.slots?.customRender" :record="record"></slot>
</template>
<template v-slot:[expandedRowRender]="{ record }">
<slot name="expandedRowRender" :record="record"></slot>
</template>
</a-table>
<a-modal
title="列表设置"
v-model:visible="visible"
:width="800"
:confirmLoading="tableHeadLoading"
:bodyStyle="{
paddingBottom: '10px',
}"
>
<a-checkbox-group v-model:value="tableHead" class="checked-item">
<a-row>
<a-col
:span="5"
v-for="check in columns"
:key="check.key"
:offset="1"
>
<a-checkbox :value="check.key" :disabled="check.key == 'action'">
<span
v-text="check.key == 'action' ? '操作' : check.title"
></span>
</a-checkbox>
</a-col>
</a-row>
</a-checkbox-group>
<template #footer>
<div class="pop-btn-box">
<div class="btn-left">
<a-button @click="checkAll">全选</a-button>
<a-button @click="checkNoAll">全不选</a-button>
</div>
<div class="btn-right">
<a-button @click="handleHeadCancel">返回</a-button>
<a-button type="primary" @click="setConfirm" class="ok-btn"
>确定</a-button
>
</div>
</div>
</template>
</a-modal>
</div>
</template>
<script setup>
import { h, reactive, ref } from "vue";
import { RightOutlined, SettingOutlined } from "@ant-design/icons-vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
const store = useStore();
const props = defineProps({
columns: {
// 弹窗显示的所有项
type: Array,
default: [],
},
pagination: {
type: Object,
default: {
hideOnSinglePage: true,
},
},
rowSelection: {
type: Object,
default: undefined,
},
dataSource: {
type: Array,
default: [],
},
expandedRowRender: {
type: String,
default: "",
},
routerName: {
type: String,
default: "",
},
loading:{
type:Boolean,
default:false
}
});
let tableColumns = ref([]);
let visible = ref(false);
let tableHead = ref([]);
let initTabHead = [];
const tableHeadLoading = ref(false);
const attrsName = ref("");
attrsName.value = props.routerName;
const emit = defineEmits(["change", "expand"]);
//构造表格列
const createColumns = () => {
const arr = props.columns.map((item) => {
let obj = { ...item };
obj.isShow = item.isShow != undefined ? item.isShow : true;
obj.dataIndex = item.key;
!obj.slots && item.isSlot ? (obj.slots = { customRender: item.key }) : "";
item.key === "action"
? (obj.title = h(SettingOutlined, {
class: "set-icon",
onClick: () => {
visible.value = true;
},
}))
: "";
return obj;
});
initTabHead = [...arr]; // 用于后续操作
tableHead.value = arr // 控制全选以及选中的项
.filter((i) => {
if (i.isShow) {
return i;
}
})
.map((t) => t.key);
return arr;
};
let showColumns = createColumns();
showColumns = showColumns.filter((i) => i.isShow == true);
tableColumns.value = showColumns; // 表格显示的所有项,isShow 为true时
const checkAll = () => {
let showArr = [];
initTabHead.forEach((tab) => {
tab.isShow = true;
showArr.push(tab);
});
tableHead.value = showArr
.filter((i) => {
if (i.isShow) {
return i;
}
})
.map((t) => t.key);
};
const checkNoAll = () => {
let showArr = [];
initTabHead.forEach((tab) => {
tab.isShow = false;
if (tab.key == "action") {
tab.isShow = true;
showArr.push(tab);
}
});
tableHead.value = showArr
.filter((i) => {
if (i.isShow) {
return i;
}
})
.map((t) => t.key);
};
//设置显示那些列
const setConfirm = () => {
tableHeadLoading.value = true;
let showArr = [];
let setArr = [];
initTabHead.forEach((tab) => {
tab.isShow = false;
tableHead.value.forEach((el) => {
if (tab.key == el) {
tab.isShow = true;
showArr.push(tab);
setArr.push(el);
}
});
});
tableHeadLoading.value = false;
if (showArr.length < 2) {
message.info("至少选中两项");
} else {
tableColumns.value = showArr;
let obj = Object.assign({}, store.state.tableHeadObj);
obj[attrsName.value] = [...setArr];
store.commit("setTableHeadObj", obj);
message.success("设置成功");
visible.value = false;
}
};
const handleHeadCancel = () => {
createColumns(); // 重置
visible.value = false;
};
//分页改变事件
const change = (pagination, filters, sorter) => {
emit("change", { pagination, filters, sorter });
};
const expand = (expanded, record) => {
emit("expand", { expanded, record });
};
//自定义展开图标
const customIcon = (arg) => {
if (
arg.record.children?.length ||
props.expandedRowRender == "expandedRowRender"
) {
return h(RightOutlined, {
class: arg.record.key + "expand",
style: { transition: "all 0.3s", cursor: "pointer", padding: "5px" },
onclick: async (e) => {
let dom = document.getElementsByClassName(arg.record.key + "expand")[0];
arg.expanded
? (dom.style.cssText = "transform:rotateZ(0)")
: (dom.style.cssText = "transform:rotateZ(90deg)");
arg.onExpand(arg.record, e);
},
});
} else {
return h("span", { style: { display: "inline-block", with: "14px" } });
}
};
</script>
<style lang="less" scoped>
.table-c {
margin-top: 0.08rem;
}
.checked-item {
margin-left: -24px;
}
.ant-checkbox-group {
width: 100%;
}
.ant-checkbox-wrapper {
padding: 2px 8px;
margin-bottom: 0.1rem;
border-radius: 4px;
white-space: nowrap;
width: 100%;
&:hover {
background: @table-head-set-bg;
}
}
.ant-checkbox-wrapper-checked {
background: @table-head-set-bg;
}
.pop-btn-box {
display: flex;
justify-content: space-between;
.btn-right {
.ok-btn {
color: @btn-primary-color;
}
}
}
</style>
2、在main.js中把组件全局上
import {
createApp
} from 'vue';
import App from './App.vue';
import router from './router/index.js';
import store from './store';
import {
changeSize
} from './utils/common.js';
import antd from 'ant-design-vue';
import tableBox from "@/components/tableBox.vue";
import 'ant-design-vue/dist/antd.less';
import './assets/common/style.less';
import 'nprogress/nprogress.css'
changeSize()
const app = createApp(App)
app.component('table-box', tableBox);
app.use(antd).use(router).use(store).mount('#app')
3、在组件中的使用, 因为父子组件的生命周期的原因,所以使用v-if控制子组件的渲染
// index.vue
<table-box
v-if="isShow"
:columns="state.columns"
:dataSource="state.tableData"
:pagination="pagination"
:rowSelection="rowSelection"
@change="handleTableChange"
:routerName="routerName"
>
<template v-slot:expandedRowRender="{ record }">
<span>{{ record }}</span>
</template>
<template #action="{ record }">
<a-popover title="">
<template #content>
<div
class="action-box"
style="display: flex; flex-direction: column"
>
<a @click="handleEdit(true, record)">修改</a>
<a @click="handleDel(record.id)" href="javascript:void(0)"
>删除</a
>
</div>
</template>
<MenuOutlined />
</a-popover>
</template>
</table-box>
<script setup>
import { useRoute } from "vue-router";
const route = useRoute();
const routerName = ref(route.name);
const isShow = ref(false);
const expandedRowRender = ref("expandedRowRender");
const state = reactive({
tableData: [],
columns: [
{
title: "厂商名称",
key: "firmName",
dataIndex: "firmName",
ellipsis: true,
},
{
title: "简称",
key: "abbreviation",
dataIndex: "abbreviation",
ellipsis: true,
},
{
title: "地址",
key: "address",
dataIndex: "address",
ellipsis: true,
},
{
title: "联系人",
key: "liaison",
dataIndex: "liaison",
},
{
title: "联系电话",
key: "liaisonPhone",
dataIndex: "liaisonPhone",
ellipsis: true,
},
{
key: "action",
isSlot: true,
slots: {
customRender: "action",
},
},
],
selectedRowKeys: [],
});
const getTableData = (formData) => {
for (let i = 0; i < 12; i++) {
state.tableData.push({
id: i,
key: i,
firmName: "江苏汇水创",
abbreviation: "汇水创",
address: "高塘石",
liaison: "陈辉",
liaisonPhone: "1371507698",
});
}
};
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
state.selectedRowKeys = selectedRowKeys;
console.log(
`selectedRowKeys: ${selectedRowKeys}`,
"selectedRows: ",
selectedRows
);
},
};
// 切换页
const handleTableChange = (page, filters, sorter) => {
pagination.current = page.current;
pagination.pageSize = page?.pageSize;
// console.log(filters); // 选中值为数组(单选直接拿第一项,多选把数组转化成字符串拼接 - 看后台需要什么数据结构)
if (filters.type) {
// 必须判断,未操作切换页会报错(无type属性)
formData.type = filters.type[0];
}
getTableData(formData);
};
onMounted(async () => {
// 判断是否已经设置过表头,有则使用
if (
sessionStorage.tableHeadObj &&
JSON.parse(sessionStorage.tableHeadObj)[routerName.value]
) {
let arr = JSON.parse(sessionStorage.tableHeadObj)[routerName.value];
for (let i = 0; i < state.columns.length; i++) {
if (arr.indexOf(state.columns[i].key) == -1) {
state.columns[i].isShow = false;
}
}
isShow.value = true;
} else {
isShow.value = true;
}
getTableData(formData);
});
</script>
4、表头数据vuex + sessionStorage (key为路由的名称 – 唯一性)
// store.js
import {
createStore
} from "vuex";
export default createStore({
state: {
tableHeadObj: sessionStorage.tableHeadObj ? JSON.parse(sessionStorage.tableHeadObj): {}
},
mutations: {
setTableHeadObj(state, obj) {
state.tableHeadObj = obj;
sessionStorage.tableHeadObj = JSON.stringify(state.tableHeadObj);
}
}
})
5、效果: