效果图
因项目采用的是Vue2,所以这个功能目前采用的是Vue2的写法。 Vue3请自行修改扩展代码;或收藏关注帖子,后续Vue3项目如有用到会在本帖子更新修改。
安装vuedraggable(拖拽插件)
cnpm i vuedraggable
先说用法,下方附全源码
引入自定义表头组件
import indicatorTable from "@/components/indicatorTable/index.vue";
使用:(传参说明已在下方标识)
ref="rois" :defaultArr="columns" :cardDataProp="cardDataProp" cacheKeyProp="keyROI" @propData="propsTableHander" currenKey="ROT" /> props参数说明:(均为必传字段) // ref:用于调用子组件方法。 // columns:表头数据,例如: [{ prop: "cost_platform", label: "广告金", }] // cardDataProp:可选表头复选框,列如: cardDataProp: [ { title: "指标", // 每一项的分类title标题,详见第一张效果图 checkboxes: [...columns], // columns这个就是上面的一样 }, ], // cacheKeyProp:储存的key名,名字自定义来,避免缓存的key一样就行,列如: cacheKeyProp="keyROI" // propData:回调方法,用于更新表头,接受函数,直接表头columns数据 = 参数即可 // currenKey:保存的指标key,避免缓存的key一样就行。 页面table使用方法,需用循环: v-loading="loading" :data="tableList" border @sort-change="tableSort" :height="tableHeight" ref="tableRef" > v-for="item in columns" :prop="item.prop" :label="item.label" :width="item.width" align="center" sortable="custom" :show-overflow-tooltip="true" > 上面表格的参数不用多说了吧,除非你不会前端! 附源码(拿来直接用!只要参数没问题!) 如遇到报错、不显示等问题,一定是参数不对!已自测 无任何报错或警告信息! 如需要Vue3版本,自行开发或私信,有空定会帮助! 新建组件直接复制: 新增自定义指标 v-for="(item, index) in pointerArr" :key="index" :class="currenPointIndex == index ? 'active-li' : ''" @click="pointClick(item, index)" > {{ item.title }} class="el-icon-edit" @click.stop.prevent="pointIndexHander(item, 'edit')" > class="el-icon-delete" @click.stop.prevent="pointIndexHander(item, 'delete')" > v-for="(item, index) in cardData" :key="index" class="box-card" > {{ item.title }} v-model="item.selectedAll" @change="handleSelectAll(item)" :indeterminate="isIndeterminate(item)" style="float: right" >全选 > ref="checkboxGroup" v-model="selectedCheckboxes" > v-for="(checkbox, idx) in item.checkboxes" :key="idx" :label="checkbox.label" >{{ checkbox.label }} > v-if="selectedCheckboxes.length > 0" v-model="selectedCheckboxes" animation="300" > {{ item }}
import draggable from "vuedraggable";
export default {
name: "indicatorTable",
components: {
draggable,
},
props: {
// 默认指标
defaultArr: {
type: Array,
required: true,
},
// 可选指标
cardDataProp: {
type: Array,
required: true,
},
// 存储指标key
cacheKeyProp: {
type: String,
required: true,
},
// 存储的索引key名
currenKey: {
type: String,
required: true,
},
},
data() {
return {
// 弹窗show
dialogVisible: false,
// 全部指标数组
cardData: this.cardDataProp,
// 勾选指标
selectedCheckboxes: [],
// 弹框title
openTitle: "添加",
// 下拉指标列表
pointerArr: [],
// 获取当前编辑item
editItem: null,
// 传出去的prop数组
emitArr: null,
// 当前选择的指标
currenPointIndex: null,
};
},
computed: {
Local() {
return {
get(key) {
const value = localStorage.getItem(key);
if (value == "[]") {
return null;
} else {
return value !== null ? JSON.parse(value) : null;
}
},
set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key) {
localStorage.removeItem(key);
},
};
},
// 指标指定label排序
sortColumns() {
return function (data, sort) {
if (data) {
return data.sort(
(a, b) => sort.indexOf(a.label) - sort.indexOf(b.label)
);
}
};
},
// 获取选指标label
filterCheckbox() {
return function (data, isSort = false, sortData) {
if (data) {
let filteredCheckboxes = [];
this.cardData.forEach((item) => {
item.checkboxes.forEach((checkbox) => {
if (data.arrayCheck.includes(checkbox.label)) {
filteredCheckboxes.push(checkbox);
}
});
});
// 获取后是否排序
if (isSort) {
return this.sortColumns(filteredCheckboxes, sortData);
} else {
return filteredCheckboxes;
}
}
};
},
},
created() {
// this.Local.remove("displayType");
this.getPointData("init");
},
methods: {
// 存储key索引
storeSetCurrentIndex(type = "set") {
if (type === "set") {
const getIndexObj = this.Local.get("pointIndex") || {};
getIndexObj[this.currenKey] = this.currenPointIndex;
this.Local.set("pointIndex", getIndexObj);
} else {
return this.Local.get("pointIndex") || {};
}
},
// 选择当前指标
pointClick(row, index) {
if (this.currenPointIndex != index) {
this.currenPointIndex = index;
// 存储当前点击指标index
this.storeSetCurrentIndex("set");
const checkData = this.filterCheckbox(
row,
true,
this.pointerArr[this.currenPointIndex].arrayCheck
);
this.$emit("propData", checkData);
}
},
// 扩展方法-倍数ROI处理
// roiDisposeFn() {
// const getPonit = this.Local.get(this.cacheKeyProp);
// const displayType = this.Local.get("displayType");
// const prointArrItem = this.pointerArr[this.currenPointIndex];
// const updatedArray = prointArrItem.arrayCheck.map((item) => {
// if (
// displayType == 2 &&
// item.startsWith("ROI") &&
// !item.includes("倍数")
// ) {
// return item + "倍数";
// } else if (displayType != 2 && item.includes("倍数")) {
// return item.replace("倍数", "");
// }
// return item;
// });
// const labelCheckBoxAll = this.filterCheckbox({
// arrayCheck: updatedArray,
// }).map((item) => item.label);
// if (prointArrItem.arrayCheck !== labelCheckBoxAll) {
// getPonit[this.currenPointIndex].arrayCheck = labelCheckBoxAll;
// this.Local.set(this.cacheKeyProp, getPonit);
// this.pointerArr[this.currenPointIndex].arrayCheck = labelCheckBoxAll;
// }
// },
// 获取-更新指标
getPointData(type) {
const getPonit = this.Local.get(this.cacheKeyProp);
if (getPonit) {
this.pointerArr = getPonit;
this.currenIndexNob();
const prointArrItem = this.pointerArr[this.currenPointIndex];
this.roiDisposeFn();
const checkData = this.filterCheckbox(
this.pointerArr[this.currenPointIndex],
true,
prointArrItem.arrayCheck
);
if (checkData) {
this.$emit("propData", checkData);
}
} else if (!getPonit && type !== "init") {
// 如果是空
this.Local.remove(this.cacheKeyProp);
this.$emit("propData", []);
} else {
// 如果默认的
if (this.defaultArr && type === "init" && this.pointerArr.length <= 0) {
const arrs = JSON.parse(JSON.stringify(this.defaultArr));
const labelsArray = arrs.map((item) => item.label);
this.currenIndexNob();
this.pointerArr.push({
title: "默认指标",
arrayCheck: labelsArray,
});
const prointArrItem = this.pointerArr[this.currenPointIndex];
const checkData = this.filterCheckbox(
prointArrItem,
true,
labelsArray
);
this.$emit("propData", checkData);
}
}
},
// 编辑-删除指标
pointIndexHander(item, type) {
if (type === "edit") {
this.openTitle = "编辑";
this.selectedCheckboxes = item.arrayCheck;
this.editItem = item;
this.dialogVisible = true;
} else {
const itemToDelete = this.pointerArr.find(
(ls) => ls.title === item.title
);
if (itemToDelete) {
const indexToDelete = this.pointerArr.indexOf(itemToDelete);
if (indexToDelete > -1) {
this.pointerArr.splice(indexToDelete, 1);
this.Local.set(this.cacheKeyProp, this.pointerArr);
// 删除当前行更新,否则不更新
if (indexToDelete === this.currenPointIndex) {
this.getPointData();
} else {
this.currenIndexNob();
}
}
}
}
},
// 全选当前指标
handleSelectAll(item) {
item.checkboxes.forEach((checkbox) => {
const checkboxIndex = this.selectedCheckboxes.indexOf(checkbox.label);
if (item.selectedAll && checkboxIndex === -1) {
this.selectedCheckboxes.push(checkbox.label);
} else if (!item.selectedAll && checkboxIndex !== -1) {
this.selectedCheckboxes.splice(checkboxIndex, 1);
}
});
},
// 全选状态判断
isIndeterminate(item) {
const selectedLabels = this.selectedCheckboxes;
const allLabels = item.checkboxes.map((checkbox) => checkbox.label);
const selectedCount = selectedLabels.filter((label) =>
allLabels.includes(label)
).length;
item.selectedAll = selectedCount === allLabels.length;
return selectedCount > 0 && selectedCount < allLabels.length;
},
// 指定索引
currenIndexNob() {
const getIndexObj = this.storeSetCurrentIndex("get");
this.currenPointIndex = getIndexObj[this.currenKey];
if (!this.currenPointIndex) {
this.currenPointIndex = 0;
} else {
if (this.pointerArr.length <= 1) {
this.currenPointIndex = 0;
} else {
this.currenPointIndex = getIndexObj[this.currenKey] || 0;
}
}
this.storeSetCurrentIndex("set");
},
// 添加指标
addPointerSubmit() {
this.dialogVisible = false;
this.emitArr = this.filterCheckbox(
{
arrayCheck: this.selectedCheckboxes,
},
true,
this.selectedCheckboxes
);
const dataItem = {
title: "",
arrayCheck: this.selectedCheckboxes,
};
if (this.openTitle === "添加") {
this.$prompt("请输入指标名", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputValidator: (value) => {
if (!value) {
return "不能为空!";
}
},
beforeClose: (action, instance, done) => {
if (action === "confirm") {
const isDuplicate = this.pointerArr.some(
(item) => item.title === instance.inputValue
);
if (isDuplicate) {
this.$message.error("已存在相同指标名");
return false;
} else {
done();
}
} else {
done();
}
},
}).then(({ value }) => {
dataItem.title = value;
if (this.pointerArr && Array.isArray(this.pointerArr)) {
const updatedData = [...this.pointerArr, dataItem];
this.Local.set(this.cacheKeyProp, updatedData);
} else {
const newData = [dataItem];
this.Local.set(this.cacheKeyProp, newData);
}
this.$emit("propData", this.emitArr);
this.getPointData();
});
} else {
const editIndex = this.pointerArr.findIndex(
(item) => item.title === this.editItem.title
);
if (editIndex !== -1) {
(dataItem.title = this.editItem.title),
(this.pointerArr[editIndex] = dataItem);
this.Local.set(this.cacheKeyProp, this.pointerArr);
}
this.$emit("propData", this.emitArr);
}
},
// 新增自定义指标
addUserDefinedIndicators() {
this.openTitle = "添加";
this.selectedCheckboxes = [];
this.dialogVisible = true;
},
},
};
.indicator-all-box {
float: right;
margin-right: 5px;
}
.indicator-list {
ul {
padding: 0;
li {
padding: 10px 0;
border-bottom: 1px solid #e1e1e1;
}
}
.flex-indicator-item {
display: flex;
justify-content: space-between;
padding: 0 15px;
.right-indicator {
i {
padding-left: 10px;
display: inline-block;
font-size: 16px;
cursor: pointer;
}
}
}
}
.box-card {
margin-bottom: 10px;
}
.el-divider {
margin: 10px 0;
}
.rihgt-all-check {
float: right;
padding: 3px 0;
}
.el-checkbox {
margin-bottom: 10px;
}
.customize-indicator-data-container {
display: flex;
min-height: 60vh;
.card-checkbox-content-left {
max-height: 600px;
overflow-y: scroll;
flex: 1;
}
.sort-view-dx {
width: 300px;
margin-left: 15px;
.sort-row {
height: 60vh;
overflow-y: scroll;
p {
background-color: #fdfdfd;
height: 32px;
line-height: 32px;
border: 1px solid #ebebeb;
padding: 0 10px;
margin: 5px 0;
&:hover {
cursor: move;
}
}
}
}
}
.active-li {
background-color: #efefef;
}
上方注释扩展方法说明: 比如你上方有筛选条件需要关联切换的,拿我自己的例子,见顶部ROI区域 他筛选条件有一个ROI、ROI倍数的筛选。然后字段展示是ROI123456…等,是循环的数量。切换ROI倍数的时候 表头原有的ROI需要变成ROI倍数 以及prop也一样要变化。 列如顶部ROI附加复选框的方法:
this.cardDataProp[1] = {
title: "ROI指标",
checkboxes: Array.from(
{ length: this.queryParams.displayNum },
(_, i) => ({
prop: `roi${i + 1}${
this.queryParams.displayType == 1 ? "_rate" : ""
}`,
label: `ROI${i + 1}${
this.queryParams.displayType == 2 ? "倍数" : ""
}`,
})
),
};
筛选条件选择切换displayType类型后调用 this.$refs.rois.getPointData("init"); 刷新表头
以上根据了选项displayType变化label和prop 但又是属于同一个label表头 只是字段不一样的 或者要用循环的,可采用这种方式,扩展方法自己研究…估计没有其他人需要用这个扩展的,就注释了,不用的可以删掉!
感谢你的阅读,如对你有帮助请收藏+关注! 只分享干货实战和精品,从不啰嗦!!! 如某处不对请留言评论,欢迎指正~ 博主可收徒、常玩QQ飞车,可一起来玩玩鸭~
推荐链接
发表评论