SmartStorage/uni_modules/peng-tree/components/peng-tree/peng-tree.vue

377 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template xlang="wxml">
<view class="peng-tree">
<view class="peng-tree-mask" :class="{'show':showTree}" @tap="_maskClick"></view>
<view class="peng-tree-cnt" :class="{'show':showTree}">
<view class="peng-tree-bar">
<view class="peng-tree-bar-cancel" :style="{'color':cancelColor}" hover-class="hover-c" @tap="_cancel">
取消
</view>
<view class="peng-tree-bar-title" :style="{'color':titleColor}">{{title}}</view>
<view class="peng-tree-bar-confirm" :style="{'color':confirmColor}" hover-class="hover-c"
@tap="_confirm">确定</view>
</view>
<view class="peng-tree-view">
<scroll-view class="peng-tree-view-sc" :scroll-y="true">
<block v-for="(item, index) in treeList" :key="index">
<view class="peng-tree-item" :style="[{
paddingLeft: item.rank*15 + 'px',
zIndex: item.rank*-1 +50
}]" :class="{
show: item.show,
last: item.lastRank,
showchild: item.showChild
}">
<view class="peng-tree-label" @tap.stop="_treeItemTap(item, index)">
<image class="peng-tree-icon"
:src="item.lastRank ? lastIcon : item.showChild ? currentIcon : defaultIcon">
</image>
{{item.name}}
</view>
<view class="peng-tree-check" @tap.stop="_treeItemSelect(item, index)"
v-if="selectParent?true:item.lastRank">
<view class="peng-tree-check-yes" v-if="item.checked" :class="{'radio':!multiple}"
:style="{'border-color':confirmColor}">
<view class="peng-tree-check-yes-b" :style="{'background-color':confirmColor}">
</view>
</view>
<view class="peng-tree-check-no" v-else :class="{'radio':!multiple}"
:style="{'border-color':confirmColor}"></view>
</view>
</view>
</block>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "peng-tree",
props: {
range: {
type: Array,
default: function() {
return []
}
},
idKey: { //字段key值
type: String,
default: 'id'
},
nameKey: { //字段value值
type: String,
default: 'name'
},
allKey: { //冗余字段
type: String,
default: 'value'
},
title: { //头
type: String,
default: ''
},
multiple: { // 是否可以多选
type: Boolean,
default: true
// default: true
},
cascade: { // 是否级联选择
type: Boolean,
default: false
// default: true
},
selectParent: { //是否可以选父级
type: Boolean,
default: true
},
maskClick: { //点击遮罩层是否关闭
type: Boolean,
default: true
},
confirmColor: { // 确定按钮颜色
type: String,
default: '' // #007aff
},
cancelColor: { // 取消按钮颜色
type: String,
default: '' // #757575
},
titleColor: { // 标题颜色
type: String,
default: '' // #757575
},
currentIcon: { // 展开时候的ic
type: String,
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0QTM0MzQ1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0QTM0MzU1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRDRBMzQzMjVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRDRBMzQzMzVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PidwepsAAAK0SURBVHja7JxbTsJAFIYHww7ciStgCeoGvGxAiOsgURegoL5720AXYLiIr0aJviq3Zx3PhIEnKG3ndtr+f3KixrSUj/ZjzjClIqUUiFm2gAAQAREQEUAEREAERAQQAREQAREBREAEREBEEqa67h9RFDWllDv0awWYlqlQHmu1WjMRRMoV1QFttA12y3xRtdNczq8EsE4/f8FumX2q77ROvNXk8UGMEKdUz6tYJHljaZAbuyUH+UR1to5BEohTuqwPCeS4pAA/qY6o/kyHOAMCeRK3owJnj+rH1jjxhqpVsstaebCz6TmnHWyXyY+xHjSBWBY/bvSgadtXBj9u9KCN3rnIfkzkQVsTEEX0Y2IP2oKo/HhMICcFAThUcwVZNGU6FdbX/XURzkbVF4+ybGhjPrFdgP66QdXNurGtSdk6Xdb9nAJ8oDo3OQlsQZzkdPw41ONBo6vI5scDefRjZg+6gpg3Pxp50CXEvPjR2IOuIXL3oxUPuobI3Y9WPOgDIlc/WvOgL4iL/vqFCcD7LH0xB4hj7cfQ/fWH9qCT+FhG0tN+DBk1PzjOM0SVllixcsBT1AvYc/kAPhc0hRg/3uvxoCgKRN9+dOrBUBB9+9GpB0NC9OVH5x4MDdG1H714kANEV3705kEOEBf9dcPi/lQnsuvLg1wgSu3Ha0v7Uh4MMgUXeuG71H407a+VBy9CPQkOdw+MtB+nGbd/D+FBbhBNxo9SjwcngJjNj0E9yBFiFj8G9SBXiGn8GNyDnCEm8SMLD3KHGOdHNh7kDjHOj2w8mAeIi/5arX+c6b/fxHz9oADEdGdjR/fXCw/OOB5oVfCOgnepz8IB14PMw03jCmTE+QBx5z0gAmKSqK9OUF+hcAeIhu/QYr4Qie8rjW83hhMBERARQAREQAREBBABERCLnH8BBgA+TQI7U4t53AAAAABJRU5ErkJggg=='
},
defaultIcon: { // 折叠时候的ic
type: String,
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAACE0lEQVR4Xu3c200DMRCF4XEltJAOkEugA+ggpUAHoQMqiFMCdEAJUMEiS4mEELlIO7bPOeN9i6K1rG/952myyea1WiCtXmEuYBPR4RBMxInoIOCwhOtJLKVszWyXc/5y2BvNEq6I+/3+kFK6M7OHnPM7jcLKjbZAvD/uaZtzflm5P4rbWyJWgDcze1LPuzVihfxUz7sH4ilJ2bx7Isrm3RtRMu8RiHJ5j0SUyXs0okTeCIj0eSMh0uaNhkiZNyIiXd7IiDR5oyNS5M2ACJ83EyJs3myIkHkzIsLlzYwIkzc7IkTeCojD81ZCHJa3GuKQvBURu+etjNgtb3XELnlHQGyedyTEZnlHQ2ySd0RE97wjI7rlHR3RJe+JeIrbLOecD6ePpZQ6W1kn2epo4MUrPOKyLN8ppYq1+y1VStncOjIdGnFZlo+U0uOtWOeOY2TE12Ouq//pEA7xXL7XfvcufR8K0Svfv6CREN3yDYfYIt9QiK3yjYTYLF95xB75SiP2ylcZsVu+cogj8pVCHJWvEuKwfOkREfKlRkTJlxkRJl86RMR8qRBR82VChM0XHpEhX2hElnyREWnyhUNkzBcKkTVfJETafIcjKuQ7FFEl35GIMvl2R1TMtyuiar49EWXzbY5oZpv/hibXTF2h3+s60FRKeT6+3TjMS3nrA3ZFRD8xrfY3ER1kJ+JEdBBwWGKeRAfEH1wS5WFZSDB/AAAAAElFTkSuQmCC'
},
lastIcon: { // 没有子集的ic
type: String,
default: ''
}
},
data() {
return {
showTree: false,
treeList: [],
}
},
computed: {},
methods: {
//点击遮罩层
_maskClick() {
if (this.maskClick) {
this._hide()
this.$emit("cancel", []);
}
},
_show() {
this.showTree = true
},
_hide() {
this.showTree = false
},
_cancel() {
this._hide()
this.$emit("cancel", []);
},
_confirm() {
// 处理所选数据
let rt = [];
this.treeList.forEach((v, i) => {
if (this.treeList[i].checked) {
rt.push({
id: this.treeList[i].id,
name: this.treeList[i].name,
value: this.treeList[i].value
})
}
})
this._hide()
this.$emit("confirm", rt);
},
// 重置数据
_reTreeList() {
this.treeList.forEach((v, i) => {
this.treeList[i].checked = v.orChecked
})
},
//开始加载数据
_initTree(range = this.range) {
this.treeList = [];
this._renderTreeList(range);
},
//扁平化树结构
_renderTreeList(list) {
list.forEach(item => {
this.treeList.push({
id: item[this.idKey],
name: item[this.nameKey],
value: item[this.allKey],
parentId: [], // 父级id数组
sonarrId: [], //子级id数组
rank: 0, // 层级
showChild: false, //子级是否显示
show: true, // 自身是否显示
checked: false, //是否选中
children: 0
})
})
},
//选中/取消数据
_treeItemSelect(item, index) {
item.checked = !item.checked
this._fixMultiple(index)
//开启级联并且在多选状态下和父级可选的状态下
if (this.cascade && this.multiple && this.selectParent) {
//给子集孙集....同步选择状态
this.treeList.forEach((childItem, i) => {
if (childItem.parentId.includes(item.id)) {
childItem.checked = item.checked
}
})
//在子集选中之后同步父级状态
if (item.rank > 0) {
//如果是选中状态,选择性同步 父级状态
if (item.checked) {
let i = item.parentId.length - 1
//倒序遍历父级数组
for (i; i >= 0; i--) {
//获取父级下标
const nextIndex = this.treeList.findIndex(itemT => itemT.id === item.parentId[i])
//从父级数据中取到所有同级id数组
const obj = this.treeList[nextIndex].sonarrId
//在所有同级选中的情况下type等于true
let type = true
//遍历同级id数组
obj.forEach((childItem, i) => {
//获取同级数据下标
const ziIndex = this.treeList.findIndex(itemT => itemT.id === childItem)
//判断同级是否选中
if (!this.treeList[ziIndex].checked) {
//只要有一个没选中type改为false
type = false
}
})
//遍历万所有同级之后,如果同级全都选中,则网父级同步状态
if (type) {
this.treeList[nextIndex].checked = true
} else {
//反之取消父级状态
//其实父级在现在的情况下本来就应该是false加else纯属以防万一
this.treeList[nextIndex].checked = false
}
}
} else {
//子集取消状态,同时取消所有父级的选中状态
item.parentId.forEach((childItem, i) => {
const nextIndex = this.treeList.findIndex(itemT => itemT.id === childItem)
this.treeList[nextIndex].checked = false
})
}
}
}
},
// 处理单选多选
_fixMultiple(index) {
if (!this.multiple) {
// 如果是单选
this.treeList.forEach((v, i) => {
if (i != index) {
this.treeList[i].checked = false
} else {
this.treeList[i].checked = true
}
})
}
},
// 点击
_treeItemTap(item, index) {
if (item.lastRank === true) {
//点击最后一级时触发事件
this.treeList[index].checked = !this.treeList[index].checked
this._fixMultiple(index)
return;
}
let id = item.id;
item.showChild = !item.showChild;
if (item.showChild) {
//子集数据加载过则不在填充
if (item.children > 0) {
this.treeList.forEach((childItem, i) => {
//隐藏所有子级
if (childItem.parentId.includes(item.id) && item.parentId.length + 1 ==
childItem
.parentId.length) {
childItem.showChild = false;
childItem.show = true
}
})
return
}
const range = this.range
// 找到当前元素
const own = this.getOwn(id, range)
//子集数据
const checkedChildren = own.children
if (checkedChildren && checkedChildren.length > 0) {
item.children = checkedChildren.length
} else {
item.children = checkedChildren
}
//如果接口返回值为空认为是末级
if (!checkedChildren || checkedChildren.length <= 0) {
item.lastRank = true
if (!this.cascade) {
this.treeList[index].checked = !this.treeList[index].checked
this._fixMultiple(index)
}
return;
}
// 子元素插入的索引位置
const nextIndex = this.treeList.findIndex(itemT => itemT.id === item.id)
const newRank = item.rank + 1
let parentId = []
if (item.parentId.length > 0) {
parentId = JSON.parse(JSON.stringify(item.parentId))
}
parentId.push(item.id)
let sonarrId = []
checkedChildren.forEach(itemC => {
const childObj = {
id: itemC[this.idKey],
name: itemC[this.nameKey],
value: itemC[this.allKey],
parentId: parentId, // 父级id数组
sonarrId: [], //子级id数组
rank: item.rank + 1, // 层级
showChild: false, //子级是否显示
show: true, // 自身是否显示
checked: this.cascade ? item.checked : false, //是否选中
lastRank: false, //是否末级
children: 0
}
sonarrId.push(childObj.id)
if (!this.treeList.some(itemT => itemT.id === itemC[this.idKey])) {
this.treeList.splice(nextIndex + 1, 0, childObj)
}
})
item.sonarrId = sonarrId
} else {
//隐藏子集
this.treeList.forEach((childItem, i) => {
//隐藏所有子级
if (childItem.parentId.includes(item.id)) {
childItem.showChild = false;
childItem.show = false
}
})
}
},
getOwn(id, arr) {
//利用foreach循环遍历
arr.forEach((item) => {
//判断递归结束条件
if (item[this.idKey] == id) {
// 存储数据到空数组
this.returnedItem = item
} else if (item.children != null) //判断chlidren是否有数据
{
//递归调用
this.getOwn(id, item.children);
}
})
return this.returnedItem
},
},
watch: {
range(list) {
this._initTree(list);
},
multiple() {
if (this.range.length) {
this._reTreeList();
}
},
selectParent() {
if (this.range.length) {
this._reTreeList();
}
},
},
mounted() {
this._initTree();
}
}
</script>
<style scoped>
@import "./style.css";
</style>