1、需求:可以自定义设置表格表头展示哪些内容,如默认展示(图一),点击表格最后一列表格的图标弹出表头的全部字段(图二),经过设置之后(图三)
图一:
图二:
图三:
2、具体实现(因为还没确认表头默认展示和全部展示的数据是否是后台返回,所以自己写了静态数据)
父组件:
html
<a-table
:columns="state.columns"
:row-key="(record) => record.id"
:data-source="state.dataSource"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
>
<template #pt="{ text }">
{{ formatTime(text) }}
</template>
<template #message="text">
<span class="redColor">
<span
>{{ text && text.text ? text.text.substr(0, 4) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(4, 2) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(6, 10) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(16, 4) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(20, 2) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(22, 4) : "" }}
</span>
<span
>{{ text && text.text ? text.text.substr(26, 2) : "" }}
</span>
</span>
<span class="greenColor">
<span
>{{ text && text.text ? text.text.slice(28, -6) : "" }}
</span>
</span>
<span class="blueColor">
<span
>{{ text && text.text ? text.text.slice(-6, -4) : "" }}
</span>
</span>
<span class="yellowColor">
<span>{{
text && text.text
? text.text.substr(text.text.length - 4)
: ""
}}</span>
</span>
</template>
<template #type="{ text }">
{{
text.indexOf("_") == -1
? sfReportLabel[text]
: state.reportLabel[text]
}}
</template>
<template #setTableTitle>
<a-tooltip>
<template #title>设置</template>
<span @click="handleTableHead" class="table-head-btn"><SettingOutlined /></span>
</a-tooltip>
</template>
<template #action="{ text }">
<a-tooltip>
<template #title>操作</template>
<a-popover title="" trigger="click">
<template #content>
<div class="action-box" style="display: flex; flex-direction: column;">
<a-popconfirm
title="Are you sure change this task?"
ok-text="Yes"
cancel-text="No"
>
<a @click="handleEdit(text)">修改</a>
</a-popconfirm>
<!-- <a @click="handleEdit(text)">修改</a> -->
<a>删除</a>
</div>
</template>
<MenuOutlined />
</a-popover>
</a-tooltip>
</template>
</a-table>
<a-modal
class="table-head-modal"
:visible="tableHeadPop"
title="列表视图设置"
:confirmLoading="tableHeadLoading"
:width="750"
@cancel="handleCancel"
>
<table-head-content
ref="tableHeadRef"
:columns="state.setColumns"
></table-head-content>
<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="handleCancel">返回</a-button>
<a-button type="primary" @click="handleOk" class="search-btn">确定</a-button>
</div>
</div>
</template>
</a-modal>
js:
<script setup>
import tableHeadContent from "./components//tableHeadContent.vue";
import { onMounted, reactive, ref, toRaw, getCurrentInstance, computed, nextTick } from "vue";
import moment from "moment";
import {
UpOutlined,
SearchOutlined,
SyncOutlined,
DownOutlined,
SettingOutlined,
MenuOutlined
} from "@ant-design/icons-vue";
import {
getReportTypeList,
getBeforeDate,
getQuery,
formatTime,
} from "./index";
import { request } from "../../../utils/request";
import { Form } from "ant-design-vue";
import { useStore } from 'vuex';
const store = useStore();
const useForm = Form.useForm;
const globalProperties =
getCurrentInstance().appContext.config.globalProperties; // 获取全局挂载
const http = globalProperties.$http;
const sfReportLabel = {
30: "测试报",
32: "遥测站定时报",
33: "遥测站加报报",
34: "遥测站小时报",
35: "遥测站人工置数报",
36: "遥测站图片报",
37: "中心站查询遥测站实时数据",
38: "中心站查询遥测站历史数据",
40: "中心站修改遥测站基本配置表",
42: "中心站修改遥测站运行参数配置表",
49: "修改密码",
4: "设置遥测站时钟",
};
const formData = reactive({
devId: "",
region: undefined,
time: [],
type: undefined,
typeId: undefined,
deviceTypeId: undefined,
});
const state = reactive({
siteTypeList: [],
regionList: [],
reportTypeList: [],
deviceTypeList: [],
reportLabel: {},
dataSource: [],
selectedDate: undefined,
columns: [ // 默认显示
{
title: "测站编码",
dataIndex: "stationCode",
ellipsis: true,
},
{
title: "测站名称",
ellipsis: true,
dataIndex: "stationName",
},
{
title: "设备编码",
ellipsis: true,
dataIndex: "devId",
},
{
title: "上报时间",
dataIndex: "pt",
ellipsis: true,
slots: {
customRender: "pt",
},
sorter: (a, b) => {
return moment(a.pt).valueOf() - moment(b.pt).valueOf()
},
},
{
title: "原始报文",
dataIndex: "message",
width: 340,
slots: {
customRender: "message",
},
},
{
title: "上报类型",
dataIndex: "type",
ellipsis: true,
slots: {
customRender: "type",
},
// filters: [], // 需要筛选时添加
filterMultiple: false, // 不可多选
// onFilter: (value, record) => record.type.includes(value)
},
],
setColumns: [], // 用于传给子组件
});
const determine = ref(false);
const isAll = ref(false);
const loading = ref(false);
const timer = ref(null);
const tableHeadPop = ref(false);
const tableHeadLoading = ref(false);
const tableHeadRef = ref(null);
const pagination = reactive({
current: 1,
pageSize: 10,
pageSizeOptions: ["10", "20", "30", "40", "50"],
showSizeChanger: true, // 显示切换页码
showTotal: () => {
return `总共${pagination.total}条数据`
},
total: 0,
});
state.siteTypeList = computed(()=>store.state.siteTypeList);
state.regionList = computed(()=>store.state.regionList);
state.deviceTypeList = computed(()=>store.state.deviceTypeList);
// 切换页
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(getQuery(formData));
// console.log(sorter); // 等后台参数
};
// 获取表格数据
const getTableData = (formData) => {
loading.value = true;
// 判断最后一项是否已有设置列(因为顺序会打乱,保持设置在最后一列)
if(state.columns.length && (state.columns[state.columns.length -1].key != "setItem")) {
state.columns.push({
key: "setItem", // 不可使用dataIndex, 会导致操作拿不到当前行的数据
width: 60,
slots: {
title: "setTableTitle",
customRender: "action",
},
})
}
request(http.GET_DEVICE_ORG_MSG_LIST, "get", {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...formData,
})
.then((res) => {
loading.value = false;
state.dataSource = res.rows;
pagination.current = res.currentPage;
pagination.total = res.total;
})
.catch(() => {
loading.value = false;
});
};
// 点击动态设置表头
const handleTableHead = () => {
tableHeadPop.value = true;
nextTick(() => {
tableHeadRef.value.setValues();
});
state.setColumns = state.columns;
let num = state.setColumns.length - 1;
state.setColumns = toRaw(state.setColumns).slice(0, num); // 把设置那行去掉
};
// 全选
const checkAll = () => {
nextTick(() => {
tableHeadRef.value.checkAllSon();
})
};
// 全不选
const checkNoAll = () => {
nextTick(() => {
tableHeadRef.value.checkNoAllSon();
})
};
const handleOk = async() => {
let arr = await tableHeadRef.value.getValues();
if(arr.length) {
tableHeadPop.value = false;
state.columns = arr;
getTableFilters(); // 筛选列数据获取
pagination.current = 1;
getTableData(getQuery(formData));
}
};
const handleCancel = () => {
tableHeadPop.value = false;
};
// 限制日期选中(前后6天,即一周)
const disabledDate = (current) => {
toRaw(state.selectedDate)
if (determine.value == true) {
return (
current < moment(state.selectedDate).add(-6, "d") ||
current > moment(state.selectedDate).add(+6, "d")
);
} else {
return;
}
};
// 日期面板变化
const calendarChange = (moment) => {
if (moment.length == 1) {
determine.value = true;
state.selectedDate = moment[0];
} else {
state.selectedDate = undefined;
determine.value = false;
}
};
// 查询
const handleSearch = () => {
pagination.current = 1;
getTableData(getQuery(formData));
};
const rulesRef = ref({});
const { resetFields } = useForm(formData, rulesRef);
// 重置
const resetSearch = () => {
resetFields();
formData.time = [moment(getBeforeDate(7)), moment(getBeforeDate(0))];
formData.region = toRaw(state.regionList)[0].dictLabel;
};
// 初始化下拉数据
const initSel = async () => {
formData.region = (state.regionList)[0].dictLabel; // 默认选中区域
formData.time = [moment(getBeforeDate(7)), moment(getBeforeDate(0))]; // 默认选中时间
let reportRes = await getReportTypeList(http);
state.reportTypeList = reportRes;
getTableFilters();
};
// 获取上报类型的filters
const getTableFilters = () => {
let filters = [];
if (state.reportTypeList && state.reportTypeList.length) {
state.reportTypeList.forEach((i) => {
state.reportLabel[i.dictCode] = i.dictLabel;
let obj = {
text: "",
value: ""
}
state.columns.forEach((ins, index) => {
if(ins.dataIndex == "type") { // 指定是上报类型
obj.text = i.dictLabel;
obj.value = i.dictCode;
filters.push(obj);
state.columns[index].filters = filters;
// state.columns[index].filters.push(obj);
}
})
});
}
}
// 操作列模拟修改
const handleEdit = (text) => {
console.log(text);
};
onMounted(async () => {
await initSel();
getTableData(getQuery(formData));
timer.value = setInterval(() => { // 定时触发
getTableData(getQuery(formData));
}, 300000);
});
</script>
子组件:
<template>
<div class="table-head-content">
<a-checkbox-group
v-model:value="state.checkedList"
name="checkboxgroup"
@change="onChange"
>
<ul class="check-ul">
<li v-for="(item, index) in state.columnsAll" :key="index" class="check-li" :class="{'isChecked': item.checked}">
<a-checkbox :value="item.dataIndex" @change="getCheckOne">
<span class="commonCode checkCode">{{ item.title }}</span>
</a-checkbox>
</li>
</ul>
</a-checkbox-group>
</div>
</template>
<script setup>
import { reactive, toRaw, getCurrentInstance } from "vue";
import { getJsonArrEqual } from "../index";
const globalProperties = getCurrentInstance().appContext.config.globalProperties; // 获取全局挂载
const toast = globalProperties.$toast;
const props = defineProps({
columns: Array,
});
const state = reactive({
checkedList: [],
columnsAll: [
{
title: "测站编码",
dataIndex: "stationCode",
ellipsis: true,
},
{
title: "测站名称",
ellipsis: true,
dataIndex: "stationName",
},
{
title: "设备编码",
ellipsis: true,
dataIndex: "devId",
},
{
title: "上报时间",
dataIndex: "pt",
ellipsis: true,
slots: {
customRender: "pt",
},
sorter: (a, b) => {
return moment(a.pt).valueOf() - moment(b.pt).valueOf()
},
},
{
title: "原始报文",
dataIndex: "message",
width: 340,
slots: {
customRender: "message",
},
},
{
title: "上报类型",
dataIndex: "type",
ellipsis: true,
slots: {
customRender: "type",
},
// filters: [], // 需要筛选时添加
filterMultiple: false, // 不可多选
// onFilter: (value, record) => record.type.includes(value)
},
{
title: "上报类型编码",
dataIndex: "functionCode",
ellipsis: true,
},
]
});
// 初始化
const setValues = () => {
state.checkedList = [];
let newArr = getJsonArrEqual(toRaw(props.columns), state.columnsAll); // 获取相同项
state.columnsAll.forEach(i => { // 初始化checked属性
i.checked = false;
})
state.columnsAll.forEach(item => {
newArr.map(i => {
if(item.dataIndex == i.dataIndex) {
item.checked = true; // 初始化选中的checked值
state.checkedList.push(i.dataIndex);
}
})
});
};
// 选中
const onChange = (check) => {
state.checkedList = check;
};
// 单个选中
const getCheckOne = (e) => {
state.columnsAll.forEach((item, index) => {
if(item.dataIndex == e.target.value) {
state.columnsAll[index].checked = e.target.checked; // 根据选中与否赋值
}
})
};
// 获取选中
const getValues = () => {
return new Promise((resolve, reject) => {
if(state.checkedList.length >= 3) { // 必须选择三项或者三项以上
let arr = [];
state.checkedList.forEach(item => {
state.columnsAll.map(i => {
if(i.dataIndex == item) {
arr.push(i);
}
})
})
resolve(arr);
} else {
toast('必须选中三项或者三项以上喔!')
reject([]);
}
})
};
// 全选
const checkAllSon = () => {
if(state.columnsAll.length) {
state.checkedList = []; // 防止叠加
state.columnsAll.map(i => {
state.checkedList.push(i.dataIndex);
return i.checked = true;
})
}
};
// 全不选
const checkNoAllSon = () => {
state.checkedList = [];
state.columnsAll.map(i => {
return i.checked = false;
})
};
defineExpose({
setValues,
getValues,
checkAllSon,
checkNoAllSon
})
</script>
<style lang="less" scoped>
.table-head-content {
.check-ul {
display: flex;
flex-wrap: wrap;
.check-li {
padding: 2px 8px;
border-radius: 4px;
margin-right: .1rem;
margin-bottom: .1rem;
white-space: nowrap;
::v-deep(.ant-checkbox-wrapper) {
width: 200px;
}
&:hover {
background: @table-head-set-bg;
}
}
.isChecked {
background: @table-head-set-bg;
}
}
};
</style>
3、json数组获取相同项方法:
// 获取两个JSON数组的相同项
export const getJsonArrEqual = (arr1, arr2) => {
var newArr = [], kvIndex = {};
for (var i = 0; i < arr1.length; i++) {
for (var j = 0; j < arr2.length; j++) {
if (arr1[i].dataIndex == arr2[j].dataIndex) {
var item
if (kvIndex[arr1[i].dataIndex] == undefined) {
kvIndex[arr1[i].dataIndex] = newArr.length;
item = {};
for (var attr in arr1[i]) item[attr] = arr1[i][attr];
newArr[kvIndex[arr1[i].dataIndex]] = item;
} else {
item = newArr[kvIndex[arr1[i].dataIndex]];
for (var attr in arr2[j]) item[attr] = arr2[j][attr];
}
}
}
}
return newArr
}
4、后面遇到一个问题,就是 columns中不一定每个项都有dataIndex(会导致数据重叠),所以改为使用title去做唯一性
export const getJsonArrEqual = (arr1, arr2) => {
var newArr = [], kvIndex = {};
for (var i = 0; i < arr1.length; i++) {
for (var j = 0; j < arr2.length; j++) {
if (arr1[i].title == arr2[j].title) {
var item
if (kvIndex[arr1[i].title] == undefined) {
kvIndex[arr1[i].title] = newArr.length;
item = {};
for (var attr in arr1[i]) item[attr] = arr1[i][attr];
newArr[kvIndex[arr1[i].title]] = item;
} else {
item = newArr[kvIndex[arr1[i].title]];
for (var attr in arr2[j]) item[attr] = arr2[j][attr];
}
}
}
}
return newArr
}