效果
使用场景
vue2下自定义表格表头配置: 列排序,显示/隐藏等。确保表头以配置项的形式加载,这样表格才能对修改后的配置作响应
思路
1、表格使用render
函数加载(如有疑问可私信),通过类似如下的columns
配置表头
columns: [
{ label: '姓名', prop: 'name', width: '160', fixed: true },
{ label: '性别', prop: 'sex', align: 'center', width: '160', fixed: false },
{ label: '证件类别', prop: 'cardType', align: 'center', width: '160', fixed: false },
{ label: '证件号', prop: 'idCard', align: 'center', width: '260', fixed: false },
{ label: '手机号', prop: 'mobile', align: 'center', width: '160', fixed: false },
{ label: '标签', prop: 'tag', align: 'center', width: '160', fixed: false },
{ label: '操作', prop: 'manage', align: 'center', disControl: true },
]
2、通过element-ui 的 el-dropdown
组件完成配置项的展示动作
<el-dropdown trigger="click" :hide-on-click="false">
<span class="el-icon-setting" style="font-size: 25px"></span>
<template slot="dropdown">
<el-dropdown-menu>
<JSelectionItem :arr.sync="rows" @item-click="itemClickHandle">
<template v-slot:default="{item,level}">
<div class="item-custom">
{{item.id}}
</div>
</template>
<template v-slot:tool="{item,level}">
<span class="el-icon-check"
v-if="item.visible"
style="size: 20px;margin-top: 5px">
</span>
</template>
</JSelectionItem>
</el-dropdown-menu>
</template>
</el-dropdown>
3、使用vue.draggable
组件完成拖拽效果,具体应用可参考draggable
4、树形结构往往需要递归加载,我选择使用组件递归而非在render中通过js递归去完成配置组件的加载,因为使用render过程中碰到拖拽动画失效的问题,没有思路去解决。
代码
使用树形配置项的组件
<template>
<div>
<el-dropdown trigger="click" :hide-on-click="false">
<span class="el-icon-setting" style="font-size: 25px"></span>
<template slot="dropdown">
<el-dropdown-menu>
<JSelectionItem :arr.sync="rows" @item-click="itemClickHandle">
<template v-slot:default="{item,level}">
<div class="item-custom">
{{item.id}}
</div>
</template>
<template v-slot:tool="{item,level}">
<span class="el-icon-check"
v-if="item.visible"
style="size: 20px;margin-top: 5px">
</span>
</template>
</JSelectionItem>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script>
import JSelectionItem from "./JSelectionItem";
export default {
name: "JHeadManage",
components: { JSelectionItem },
data() {
return {
rows: []
}
},
created() {
this.getResource()
},
methods: {
itemClickHandle(item) {
item.visible = !item.visible
console.log(item)
},
getResource() {
const vm = this
//测试用数据只有一个根节点 "0" 方便构建测试用数据树
const data = [
{ id: '1', parentId: '0' },
{ id: '2', parentId: '0' },
{ id: '2-0', parentId: '2' },
{ id: '1-0', parentId: '1' },
{ id: '1-1', parentId: '1' },
{ id: '1-1-0', parentId: '1-1' },
{ id: '1-1-0-0', parentId: '1-1-0' },
{ id: '1-1-0-1', parentId: '1-1-0' },
{ id: '1-2', parentId: '1' },
{ id: '1-2-0', parentId: '1-2' },
{ id: '1-2-1', parentId: '1-2' },
]
//1) 简单处理数据用于自定义渲染; checked: indeterminate:
data.forEach((d, index) => {
d.visible = true //是否选中
d.isIndeterminate = false//是否是半选状态
d.a = 'a' + index
})
vm.rows = vm.makeTree(data, 'id', 'parentId', '0')
// vm.rows = data
},
/**
* 构建树,与复选逻辑无关
* @param data
* @param idMark
* @param pIdMark
* @param rootId
* @returns {*}
*/
makeTree(data, idMark, pIdMark, rootId) {
//转化为字典,id为键值,并添加根节点对象
let nodeDict = {};
(nodeDict[rootId] = { children: [] })[idMark] = rootId;
data.forEach(n => (nodeDict[n[idMark]] = n).children = []);
data.forEach(d => nodeDict[d[pIdMark]]?.children?.push(d))
return nodeDict[rootId].children;
}
}
}
</script>
<style scoped>
.item-custom {
display: inline-flex;
padding: 10px 0;
margin-right: auto;
}
</style>
树型配置项组件
定义了两个插槽,一个用来显示主要内容一个用来放置对选项的操作
<template>
<ul class="list" :style="{paddingLeft:level==0?'10px':'0'}">
<draggable v-model="locArr"
:animation="100"
handle=".tip"
@start="onStart"
@end="onEnd">
<transition-group>
<li class="item" v-for="(c,i) in locArr" :key="i">
<div class="item-inner" :style="{paddingLeft:level*20+'px'}" @click.stop="itemClick(c)">
<span class="tip el-icon-more-outline"></span>
<slot v-bind:item="c" v-bind:level="level"></slot>
<div class="item-tool">
<slot name="tool" v-bind:item="c" v-bind:level="level"></slot>
</div>
</div>
<template v-if="c.children&&c.children.length>0">
<JSelectionItem :arr.sync="c.children" :level="level+1" @item-click="itemClick">
<template v-slot:default="{item,level}">
<slot v-bind:item="item" v-bind:level="level"></slot>
</template>
<template v-slot:tool="{item,level}">
<slot name="tool" v-bind:item="item" v-bind:level="level"></slot>
</template>
</JSelectionItem>
</template>
</li>
</transition-group>
</draggable>
</ul>
</template>
<script>
import draggable from 'vuedraggable'
import JSelectionItem from "@/views/demo/JSelectionItem";
export default {
name: "JSelectionItem",
components: {
JSelectionItem, draggable
},
data() {
return {}
},
computed: {
locArr: {
get() {
return this.arr
},
set(val) {
this.$emit('update:arr', val)
}
},
},
props: {
arr: {
type: Array,
default: () => []
},
level: {
type: Number,
default: 0,
}
},
methods: {
onStart() {
},
onEnd() {
},
itemClick(item) {
this.$emit('item-click', item)
}
}
}
</script>
<style scoped>
.list {
width: 100%;
margin: 0;
padding: 0;
}
.item {
padding: 0;
width: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.item-inner {
display: flex;
width: 100%;
position: relative;
justify-content: space-between;
border-bottom: 1px solid #e5e5e5;
user-select: none;
}
.item-inner:hover {
/*font-weight: bold;*/
}
.item-tool {
display: flex;
position: absolute;
align-items: center;
right: 10px;
}
.tip {
transform: rotate(-90deg);
cursor: move;
font-size: 20px;
user-select: none;
}
</style>