目录
前言:
最近在项目中遇到新的需求,客户需要能够自己配置对应的表头并打印。第一想法就是利用穿梭框去完成对表头的自定义配置。但是因为element-plus中的穿梭框满足不了我想要的功能,只能去单独封装一个穿梭框。
一、封装穿梭框
穿梭框主要是为了获取到选择字段的数组,通过这个数组在表头中渲染完成自定义表头配置。勾选后将所勾选的字段放入选择字段数组中,再通过sort对数组进行排序
源码:
<template>
<div class="shuttle">
<!-- 左边列表 -->
<div class="shuttle-box">
<div class="shuttle-box-title">
<div> <span>未选择字段</span></div>
<div class="index-num">{{itemLeft.length}}</div>
</div>
<div class="shuttle-box-list">
<el-checkbox-group v-model="checkLeft">
<el-checkbox style="display: block;" v-for="(vo,inx) in itemLeft" :label="inx" :key="inx">{{vo.name}}</el-checkbox>
</el-checkbox-group>
</div>
</div>
<!-- 左右操作按钮 -->
<div class="shuttle-click" v-if="isEdit">
<span @click="goLeft">←</span>
<span @click="goRight">→</span>
</div>
<!-- 右边列表 -->
<div class="shuttle-box">
<div class="shuttle-box-title">
<div>已选择字段</div>
<div class="index-num">{{itemRight.length}}</div>
</div>
<div class="shuttle-box-list">
<el-checkbox-group v-model="checkRight">
<el-checkbox style="display: block;position: relative;" v-for="(vo,inx) in itemRight" :label="inx" :key="inx">
<span v-if="isEdit" style=" width: 40px;margin-right: 5px;
display: inline-block;"> <el-button size="medium" type="text" style="margin-left: 5px; " icon="el-icon-top" v-if="inx>0" @click="moveUp(inx)"> </el-button>
<el-button size="medium" type="text" style="margin-left:5px; " icon="el-icon-bottom" v-if="inx<itemRight.length-1" @click="moveDown(inx)"></el-button>
</span>
{{vo.name}}
<el-popover placement="right" :width="400" trigger="click" v-if="vo.field=='Supporting'">
<template #reference>
<el-button size="medium" type="text" style="margin-left: 8px;color: #ff9800;">设置比例</el-button>
</template>
<table class="mytable">
<tr>
<th>配套比例</th>
<th v-if="isEdit">
<el-button type="text" @click="addData(vo)">添加</el-button>
</th>
</tr>
<tr v-for="(item, index) in vo.subfield" :key="index">
<td>
<el-input v-model="item.Name" clearable></el-input>
</td>
<td v-if="isEdit">
<el-button type="text" @click="addData(vo,index)">添加</el-button>
<el-button type="text" @click="removeData(vo,index)">删除</el-button>
</td>
</tr>
</table>
</el-popover>
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, getCurrentInstance, watch } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
export default {
props: {
itemLeft: {
type: Array,
default: () => {
return [];
},
},
itemRight: {
type: Array,
default: () => {
return [];
},
},
isEdit: {
type: Boolean,
default: () => {
return true;
},
},
},
setup(props, context) {
const state = reactive({
checkLeft: [],
checkRight: [],
sortIndex: 0,
});
const addData = (item: any) => {
if (item.subfield.length > 3) {
ElMessage.error('最多只能添加4条数据!');
return;
}
item.subfield.push({ ID: 'Supporting' + item.subfield.length, Name: '' });
};
const removeData = (item: any, index: any) => {
item.subfield.splice(index, 1);
};
const goLeft = () => {
//数组排序
state.checkRight.forEach((item) => {
//将itemRight对应索引的数据移动到左边去
props.itemLeft.push(props.itemRight[item]);
//移除
props.itemRight.splice(item, 1);
});
//清空
state.checkLeft = [];
state.checkRight = [];
sortIndex();
};
const goRight = () => {
//数组排序
state.checkLeft.forEach((item) => {
//将itemLeft对应索引的数据移动到右边去
props.itemRight.push(props.itemLeft[item]);
//移除
props.itemLeft.splice(item, 1);
});
//清空
state.checkLeft = [];
state.checkRight = [];
};
const sortIndex = () => {
props.itemLeft.sort(function (a, b) {
return a.id - b.id;
});
};
//上移
const moveUp = (index: any) => {
var item = JSON.parse(JSON.stringify(props.itemRight[index]));
var itemPre = JSON.parse(JSON.stringify(props.itemRight[index - 1]));
props.itemRight.splice(index, 1, itemPre);
props.itemRight.splice(index - 1, 1, item);
};
//下移
const moveDown = (index: any) => {
var item = JSON.parse(JSON.stringify(props.itemRight[index]));
var itemNext = JSON.parse(JSON.stringify(props.itemRight[index + 1]));
props.itemRight.splice(index, 1, itemNext);
props.itemRight.splice(index + 1, 1, item);
};
return {
moveDown,
moveUp,
addData,
removeData,
sortIndex,
goLeft,
goRight,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.mytable th {
padding: 0px 10px;
}
.shuttle {
width: 800px;
padding: 50px 0;
display: flex;
justify-content: space-between;
//整个穿梭框
.shuttle-box {
width: 260px;
height: 420px;
border: 1px solid #ddd;
//标题
.shuttle-box-title {
background: #f5f7fa;
padding: 0 20px;
height: 40px;
line-height: 40px;
display: flex;
justify-content: space-between;
.index-num {
color: #909399;
font-size: 12px;
font-weight: 400;
}
}
//列表
.shuttle-box-list {
padding: 20px;
height: 380px;
overflow: auto;
//一个列表item
.shuttle-box-item {
line-height: 2;
}
}
}
//左右穿梭按钮
.shuttle-click {
padding-top: 60px;
cursor: pointer;
display: flex;
align-content: space-around;
flex-direction: column;
flex-wrap: wrap;
justify-content: flex-start;
span {
padding: 10px;
display: inline-block;
background: #409eff;
color: #ffffff;
margin: 10px;
text-align: center;
}
}
}
</style>
二、引入穿梭框
引入后在页面中使用,注意使用时需要传入可选字段和已选字段
三、自定义表头
将穿梭框的已选字段数组对表头进行遍历渲染,即可完成对表头的自定义配置。我这里用的是原生表格,如果用element的表格需要数据绑定的话可以通过props属性进行绑定。
<table class="mytable">
<tr>
<th v-if="selectfield.length !== 0">序号</th>
<th v-for="element in selectfield" :key="element.id">{{ element.name }}</th>
</tr>
<tr v-for="(item, index) in data" :key="index">
<td v-if="selectfield.length !== 0">{{ index + 1 }}</td>
<td v-for="eitem in selectfield" :key="eitem.id">{{ item[eitem.field] }}</td>
</tr>
</table>
四、效果图