- 父子数据表格
- 父级选中携带全部子级
- 子级非全选,父级脱离选中
- 父子可单独进行排序
<template>
<div>
<el-table ref="SortTableTree" :default-expand-all="isEdit"
:header-cell-style="{ background: '#E8EFFC', color: '#333333', height: '56px' }" v-loading="tableLoading"
element-loading-background="rgba(255, 255, 255, 0.7)" :data="sortTableData" border style="width: 100%"
:row-class-name="tableRowClassName" :row-key="getRowKeys" :span-method="arraySpanMethod"
:tree-props="{ children: 'children' }" @expand-change="handleExpandChange" @select="handletableSelect"
@select-all="handletableSelectAll" @selection-change="handletableSelectionChange">
<template slot="empty">
<el-empty :image-size="100" description="暂无数据"></el-empty>
</template>
<el-table-column type="selection" width="55"> </el-table-column>
<el-table-column align="left" prop="label" label="项目分类" width="230">
<template slot-scope="{ row,$index }">
<span>{{ row.label }}</span>
<div class="sortIcon" v-if="!row.parentType && row.testType">
<el-input :disabled="isEdit" style="width: 50px; padding: none !important;" v-model.number="row.itemIndex"
@blur="toSortItem(row)"></el-input>
</div>
</template>
</el-table-column>
<el-table-column align="left" prop="testTypeIndex" label="排序" width="50">
<template slot-scope="{ row,$index }">
<div v-if="row.parentType && row.label !== '其它'">
<el-input style="width: 50px;" :disabled="row.isDisable || isEdit"
v-model.number="row.parentIndex"></el-input>
</div>
<span v-if="row.label === '其它'">{{ row.parentIndex }}</span>
</template>
</el-table-column>
<el-table-column v-for="(item, index) in tableList" :key="item.label" :min-width="item.width"
:show-overflow-tooltip="false" :fixed="item.fixed" align="center">
<template slot="header">{{ item.label }}</template>
<template slot-scope="{ row,$index }">
<div v-if="item.type == '操作' || item.type == 'slot'">
<slot :row="row" :label="item" :$index="$index" />
</div>
<!-- 显示类型字典 -->
<span v-else-if="item.dicts">
{{ dict.dict[item.dicts][row[item.prop]]?.label }}
</span>
<span v-else>{{ row[item.prop] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid';
export default {
props: ['detailData', 'editState', 'tableList', 'tableLoading', 'isEdit'],
dicts: ['item_export_show_range'], //显示类型字典
data() {
return {
num: 10000,
expands: [],
sortTableData: [
{
children: [
{
id: 123,
name:'kkk'
}
],
label:"一级1.1",
parentIndex:6,
parentType:true,
pid:"cb175c38-0c04-4db0-a678-5261289f2c54"
},
{
children: [
{
id: 1234,
name: 'kkk'
}
],
label: "一级1.2",
parentIndex: 61,
parentType: true,
pid: "cb175c38-0c04-4db0-a678-ewda8847123"
}
],
};
},
mounted() {
},
methods: {
//获取当前选中
getSelection() {
return this.$refs["SortTableTree"].selection;
},
// 获取row的key值
getRowKeys(row) {
return row.id || row.pid;
},
// 合并
arraySpanMethod({ row, column, rowIndex, columnIndex }) {
// 合并岗位
if (row.parentType) {
if (columnIndex === 2) {
return {
rowspan: 1,
colspan: 20
}
}
}
},
// 设置失效行的class
tableRowClassName({ row, rowIndex }) {
if (!row.type && row.takeEffect === false) {
return 'warning-row';
}
return '';
},
// 子排序
toSortItem(row) {
this.$request({
url: this.$baseApi.MAINTASKLIST.itemIndexOrder,
method: 'PUT',
data: [{
"id": row.id,
"itemIndex": parseFloat(row.itemIndex) || 0
}]
}).then(res => {
this.sortTableData.forEach(o => {
o.children.sort((a, b) => a.itemIndex - b.itemIndex)
})
}, err => {
this.$message.error("失败!")
})
},
//handleExpandChange 控制父级禁用
handleExpandChange(row, isExpend) {
this.$set(row, 'isDisable', isExpend)
},
// 当用户手动勾选数据行的 Checkbox 时触发的事件
handletableSelect(selection, row) {
if (row.children) { //只对有子节点的行响应
// if (row.isChecked === undefined) row.isChecked = true
if (!row.isChecked) { //由行数据中的元素isChecked判断当前是否被选中
this.traverseChildNodes(row.children, this.$refs.SortTableTree, true)
row.isChecked = true; //当前行isChecked标志元素切换为false
} else {
this.traverseChildNodes(row.children, this.$refs.SortTableTree, false)
row.isChecked = false;
}
}
// 处理父级选择逻辑
// tableSelect 字段是 handletableSelectionChange 回调回来的
// 如果不用 this.$nextTick 无法获取到正确数据
this.$nextTick(() => {
// 递归寻找当前子节点的父级节点
let getParentNode = function (data, id) {
for (let i in data) {
if (data.hasOwnProperty(i)) {
if (data[i].id === id) {
return [data[i]]
}
if (data[i].children) {
let node = getParentNode(data[i].children, id);
if (node !== undefined) {
return node.concat(data[i])
}
}
}
}
}
// 设置checkbox半选择状态
// 此处的传入的id就是匹配表格id字段的
let updateCheckboxIndeterminate = function (id, isIndeterminate) {
setTimeout(() => {
// el-table不支持checkbox半选 目前方案是遍历dom获取行节点设置样式
let elementsRow = document.getElementsByClassName("el-table__row");
for (let i = 0; i < elementsRow.length; i++) {
let element = elementsRow.item(i);
// tips: 通过其他手段设置行内row-id的 以下代码需要修改
let childNode = element.childNodes.item(1); // row-key 字段在dom中的索引 第二列=1
if (childNode.innerText === `${id}`) {
let td = element.childNodes.item(0) // 获取要设置半选中状态的checkbox
let div = td.firstChild; // <div class="cell">
let label = div.firstChild; // <label class="el-checkbox">
let span = label.firstChild; // <span class="el-checkbox__input">
if (isIndeterminate)
span.classList.add("is-indeterminate") // 设置半选中状态
else
span.classList.remove("is-indeterminate") // 取消半选中状态
}
}
}, 1)
}
let parentNode = getParentNode(this.$refs.SortTableTree.data, row.id)
// > 1 说明节点有子节点
if (parentNode.length > 1) {
// 提取兄弟节点ids
let siblingNodeIds = parentNode[1].children.map(item => item.id)
// 提取全选所选择项的ids
let checkedIds = this.tableSelect.map(item => item.id)
// 获取兄弟行选择数
let siblingNodeCheckedIds = siblingNodeIds.filter(id => checkedIds.indexOf(id) !== -1)
if (siblingNodeCheckedIds.length === siblingNodeIds.length) {
// 兄弟节点选择数===兄弟节点数 全选
updateCheckboxIndeterminate(parentNode[1].id, false)
parentNode[1].isChecked = true
this.$refs.SortTableTree.toggleRowSelection(parentNode[1], true)
} else if (siblingNodeCheckedIds.length === 0) {
// 兄弟节点选择数===0 全不选
parentNode[1].isChecked = false
this.$refs.SortTableTree.toggleRowSelection(parentNode[1], false)
updateCheckboxIndeterminate(parentNode[1].id, false)
} else if (siblingNodeCheckedIds.length < siblingNodeIds.length) {
parentNode[1].isChecked = true
this.$refs.SortTableTree.toggleRowSelection(parentNode[1], true)
for (let i = 0; i < parentNode.length; i++) {
if (i > 0) updateCheckboxIndeterminate(parentNode[i].id, true)
}
}
}
})
},
// 当用户手动勾选全选 Checkbox 时触发的事件
handletableSelectAll(selection) {
this.$refs.SortTableTree.data.map(items => {
if (items.children && items.children.length > 0) {
if (!items.isChecked) {
this.$refs.SortTableTree.toggleRowSelection(items, true);
items.isChecked = true
this.traverseChildNodes(items.children, this.$refs.SortTableTree, true)
} else {
this.$refs.SortTableTree.toggleRowSelection(items, false);
items.isChecked = false;
this.traverseChildNodes(items.children, this.$refs.SortTableTree, false)
}
} else {
items.isChecked = !items.isChecked;
}
})
},
// 当选择项发生变化时会触发该事件
handletableSelectionChange(selection) {
this.tableSelect = selection
},
// 递归设置子节点
traverseChildNodes(children, ref, selected) {
children.map(item => {
ref.toggleRowSelection(item, selected);
item.isChecked = selected;
if (item.children) {
this.traverseChildNodes(item.children, ref, selected)
}
})
},
}
};
</script>
<style lang='scss' scoped>
.hand {
cursor: pointer;
font-size: 18px;
}
.sortIcon {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
::v-deep {
.el-table__row--level-0 {
background-color: #f7f9fc;
}
.el-input--small .el-input__inner {
padding: 0 5px !important;
text-align: center;
}
.el-table__row--level-1 {
background-color: #fff;
.el-checkbox__input {
margin-left: 15px;
}
}
.el-dialog {
margin: 5vh auto !important;
}
.el-table::before {
height: 0px;
}
.el-table__row>td {
border: none;
}
}
</style>