jsy-app/components/custom-select-land/custom-select-land.vue
2025-01-16 17:25:51 +08:00

512 lines
14 KiB
Vue

<template>
<u-popup mode="bottom" :show="show" @close="close">
<view class="da-tree">
<scroll-view scroll-y="true" style="height: 90vh;padding-left: 24rpx;">
<view class="da-tree-item" :class="{'is-show': datamap[item.key].show}"
:style="{paddingLeft: (item.level - 1) * indent + 'rpx'}" v-for="item in datalist">
<view v-if="item.children" class="da-tree-item__icon" @click="handleExpandedChange(item)">
<view v-if="datamap[item.key].loading" :class="['da-tree-item__icon--arr','is-loading']"></view>
<view v-else
:class="['da-tree-item__icon--arr','is-expand', {'is-right':!datamap[item.key].expand}]">
</view>
</view>
<view v-else class="da-tree-item__icon"></view>
<view class="iconfont icon-qiye" v-if="item.node?.groupStatus == 1" />
<view class="iconfont icon-gongsi" v-else-if="item.node?.groupStatus == 0" />
<view class="iconfont icon-nongtian" v-else />
<view class="da-tree-item__label" :class="'da-tree-item__label--'+item.checkedStatus"
@click="handleExpandedChange(item)">
<text :class="{'land-active':selectData?.id == item.id}">{{ item.label }}</text>
</view>
<uni-badge type="success" :text="item.node?.deviceCount" v-if="datamap[item.key].isLand" />
</view>
</scroll-view>
</view>
</u-popup>
</template>
<script>
import {
unCheckedStatus,
halfCheckedStatus,
isCheckedStatus,
deepClone,
getAllNodeKeys,
getAllNodes,
logError,
isArray,
isString,
isNumber,
isFunction,
} from './utils'
export default {
name: 'custom-select-land',
emits: ["getLand", "select"],
props: {
/**
* 树的数据
*/
data: {
type: Array,
default: () => [],
},
/**
* 默认选择中的id
*/
defaultSelect: {
type: Object,
default: {},
},
/**
* 默认展开第一级
*/
expandFirst: {
type: Boolean,
default: true,
},
},
data() {
return {
show: false,
indent: 30,
dataRef: [],
datalist: [],
datamap: {},
landmap: {},
selectData: {}
}
},
watch: {
data: {
deep: true,
immediate: true,
handler: function(v) {
this.dataRef = deepClone(v)
setTimeout(() => {
this.initData()
}, 36)
},
},
},
created() {},
methods: {
//打开弹窗
open() {
this.show = true;
if (!this.selectData?.id) {
this.selectData = this.defaultSelect;
}
},
//关闭弹窗
close() {
this.show = false;
},
/**
* 初始化数据结构
*/
initData() {
const data = deepClone(this.dataRef);
this.datalist = [];
this.handleTreeData(data);
// 展开第一个
if (this.expandFirst && this.datalist.length) {
this.handleExpandedChange(this.datalist[0]);
}
// console.log('init datalist', this.datalist)
// console.log('init datamap', this.datamap)
},
/**
* 转换为节点数据
* @param data
* @param parent
* @param level
*/
handleTreeData(data = []) {
const that = this;
function traverse(node, parent = null) {
const children = Array.isArray(node.children) ? node.children : [];
const childrenIds = children.map(child => `c${child.id}`);
node = {
...node,
key: `c${node.id}`,
parent: parent,
level: node.node.level,
keys: childrenIds,
};
that.datalist.push(node);
that.datamap[node.key] = that.createMapItem(node);
if (!parent) {
that.datamap[node.key].show = true;
}
children.forEach(child => {
traverse(child, node.key);
});
}
data.forEach(item => {
if (item.node.parentId > 0) {
traverse(item, `c${item.node.parentId}`)
} else {
traverse(item)
}
});
},
/**
* 创建节点
* @param item
* @param isLand
*/
createMapItem(item, isLand = false) {
return Object.assign({}, item, {
show: false, //显示
expand: false, //打开
isLand: isLand, //是地块
loading: false, //加载
isloaded: false, //上是否加载过数据了(调用过数据接口才算)
});
},
/**
* 点击展开收起
* @param item
*/
async handleExpandedChange(item) {
const {
expand,
loading,
isLand
} = this.datamap[item.key];
if (loading) return;
// 如果是地块,则直接跳出
if (isLand) {
this.selectLand(item)
return;
}
// 不是地块加载地块数据
if (item.parent && !expand) {
await this.loadExpandNode(item)
}
this.checkExpandedChange(this.datamap[item.key], !expand)
this.datamap[item.key].expand = !expand;
},
/**
* 加载异步地块数据
* @param item
*/
async loadExpandNode(item) {
// 如果已经加载过数据,则跳出
if (this.datamap[item.key].isloaded) {
return;
}
this.datamap[item.key].loading = true;
// if (item.lands) return;
// 如果已经有地块数据,则先使用此数据
if (item.lands) {
this.addLand(item, item.lands, false);
}
// 如果没更新过数据,则异步的去更新数据
this.$emit('getLand', item);
},
/**
* 添加地块
* @param item
* @param lands
* @param isloaded 是否接口加载的数据
*/
addLand(item, lands, isloaded = true) {
const that = this;
that.datamap[item.key].loading = false;
if (!lands || !lands.length) {
return;
}
that.datamap[item.key].isloaded = isloaded;
const oldLand = item?.lands || null;
item.lands = lands;
const index = that.datalist.findIndex(it => it.id === item.id);
// 如果存在地块数据,则删除已经存在的
if (oldLand && oldLand.length) {
oldLand.forEach(land => {
if (that.datamap[`l${land.id}`]) {
that.datalist.splice(index + 1, 1);
}
});
}
if (index !== -1) {
let addLand = [];
lands.forEach(land => {
land = {
id: land.id,
label: land.landName,
node: land,
key: `l${land.id}`,
parent: item.key,
level: item.node.level + 1,
keys: [],
};
addLand.push(land);
that.datamap[item.key].keys.push(`l${land.id}`);
let newItem = that.createMapItem(land, true);
newItem.show = that.datamap[item.key].show;
that.datamap[land.key] = newItem;
});
that.datalist.splice(index + 1, 0, ...addLand);
item.keys = that.datamap[item.key].keys;
}
},
// 选中地块数据
selectLand(item) {
this.close();
if (item.id == this.selectData?.id) {
return;
}
this.selectData = item.node;
this.$emit('select', item.node);
uni.$emit('notify-update-land', item.node);
},
/**
* 检查展开状态
* @param item
*/
checkExpandedChange(datamap, isShow) {
const {
keys,
} = datamap;
if (keys?.length) {
keys.forEach(k => {
if (this.datamap[k]) {
this.datamap[k].show = isShow;
if (this.datamap[k].expand) {
this.checkExpandedChange(this.datamap[k], isShow);
}
}
})
}
},
}
}
</script>
<style lang="scss" scoped>
@font-face {
font-family: 'da-tree-iconfont';
/* Project id */
src: url('data:application/octet-stream;base64,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') format('truetype');
}
.da-tree {
width: 100%;
height: 100%;
&-scroll {
width: 100%;
height: 100%;
}
&-item {
display: flex;
align-items: center;
height: 0;
padding: 0;
overflow: hidden;
font-size: 28rpx;
line-height: 1;
visibility: hidden;
opacity: 0;
transition: opacity 0.2s linear;
&.is-show {
height: auto;
padding: 12rpx 24rpx;
visibility: visible;
opacity: 1;
}
&__icon {
display: flex;
align-items: center;
justify-content: center;
width: 40rpx;
height: 40rpx;
overflow: hidden;
&--arr {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 32rpx;
height: 32rpx;
&::after {
position: relative;
z-index: 1;
overflow: hidden;
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
font-family: 'da-tree-iconfont' !important;
font-size: 32rpx;
font-style: normal;
color: #999;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
&.is-expand {
&::after {
content: '\e604';
}
}
&.is-right {
transform: rotate(-90deg);
}
&.is-loading {
animation: IconLoading 1s linear 0s infinite;
&::after {
content: '\e7f1';
}
}
}
}
&__checkbox {
width: 40rpx;
height: 40rpx;
overflow: hidden;
&--left {
order: 0;
}
&--right {
order: 1;
}
&--icon {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40rpx;
height: 40rpx;
&::after {
position: relative;
top: 0;
left: 0;
z-index: 1;
overflow: hidden;
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
font-family: 'da-tree-iconfont' !important;
font-size: 32rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
&.da-tree-checkbox-outline::after {
color: #bbb;
content: '\ead5';
}
&.da-tree-checkbox-checked::after {
color: var(--theme-color, #007aff);
content: '\ead4';
}
&.da-tree-checkbox-indeterminate::after {
color: var(--theme-color, #007aff);
content: '\ebce';
}
&.da-tree-radio-outline::after {
color: #bbb;
content: '\ecc5';
}
&.da-tree-radio-checked::after {
color: var(--theme-color, #007aff);
content: '\ecc4';
}
&.da-tree-radio-indeterminate::after {
color: var(--theme-color, #007aff);
content: '\ea4f';
}
}
&.is--disabled {
cursor: not-allowed;
opacity: 0.35;
}
}
&__label {
flex: 1;
margin-left: 4rpx;
color: #555;
&--2 {
color: var(--theme-color, #007aff);
}
&--append {
font-size: 60%;
opacity: 0.6;
}
}
}
}
@keyframes IconLoading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// 三角箭头
// .da-tree-item__icon {
// display: none;
// }
// icon
.iconfont {
padding-right: 12rpx;
}
.icon-qiye,
.icon-gongsi {
color: #399bfe;
}
.icon-nongtian {
color: #39ac4f;
}
.land-active {
color: #39ac4f;
}
// 数字角标
::v-deep.uni-badge--success {
background-color: #39ac4f !important;
}
</style>