<!--
 树形结构数据展示
 @Author: mosowe
 @Date:2023-05-11 11:17:58
-->
<template>
	<view class="" v-if="treeData.length">
		<view class="mosowe-tree-list-component" v-for="(item, index) in treeData" :key="index">
			<view class="mosowe-tree-title-wrap">
				<view class="mosowe-tree-title-icon">
					<view class="icon" v-if="item[children] && item[children].length" :class="{
              open: showChidren.includes(item[nodeKey]),
              close: !showChidren.includes(item[nodeKey])
            }" @click="toggleChildrens(item[nodeKey])"></view>
				</view>
				<view class="mosowe-tree-title-selection" :class="{
            disabled: item.disabled,
            'part-select':
              item[children] &&
              treeToArray(JSON.parse(JSON.stringify(item[children])), children).filter((ci) =>
                selectionList.includes(ci[nodeKey])
              ).length > 0 &&
              !selectionList.includes(item[nodeKey]),
            'all-select': selectionList.includes(item[nodeKey])
          }" v-if="multiple" @click="selectionClick(item)"></view>
				<view class="mosowe-tree-title-name" :class="{'mosowe-tree-title-name-disable':!item.enable}" @click.stop="nodeClick(item)">
					{{ item[text] }}
				</view>
			</view>
			<template v-if="item[children] && item[children].length">
				<template v-if="showChidren.indexOf(item[nodeKey]) > -1">
					<view class="mosowe-tree-children-wrap">
<!-- 						<my-mosowe-tree-list v-model="selectionList" :treeData="item[children]" :text="text" :children="children"
							:multiple="multiple" :nodeKey="nodeKey" :defaultShowChildren="defaultShowChildren"
							:textClickToggle="textClickToggle" :accordion="accordion" :index="index + 1" :isOnceShow="onceShow"
							@nodeClick="nodeClick" @childCancel="childCancel(item)" @childChoosed="childChoosed(item)">
									{{ node[text] }}
						</my-mosowe-tree-list> -->
						<view style="text-align: left;line-height: 70rpx;" class="">
							<span  style="font-weight: 400;display: inline-block;padding: 0rpx 20rpx;border: 1rpx solid #bdbdbe;margin: 10rpx 20rpx;font-size: 30rpx;border-radius: 10rpx;" v-for="(iitem,iindex) in item[children]" class="">
								{{iitem.text}}
							</span>
						</view>
					</view>
				</template>
			</template>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'my-mosowe-tree-list',
		emits: ['nodeClick', 'childCancel', 'childChoosed', 'update:modelValue'],
		props: {
			modelValue: {
				type: Array,
				default: () => []
			},
			value: {
				type: Array,
				default: () => []
			},
			treeData: {
				// 数据
				type: Array,
				default: () => []
			},
			text: {
				// 显示的文案标题字段
				type: String,
				default: 'text'
			},
			nodeKey: {
				// 目标字段
				type: String,
				default: 'value'
			},
			children: {
				// 子集字段
				type: String,
				default: 'childrens'
			},
			multiple: {
				// 显示多选框
				type: Boolean,
				default: false
			},
			textClickToggle: {
				// 标题点击触发子集列表显隐
				type: Boolean,
				default: false
			},
			defaultShowChildren: {
				// 默认展开所有，或展开几级
				type: [Boolean, Number],
				default: false
			},
			accordion: {
				// 手风琴模式
				type: Boolean,
				default: false
			},
			index: {
				// 当前层级
				type: Number,
				default: 1
			},
			isOnceShow: {
				// 是否显示过了
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				showChidren: [],
				once: false,
				onceShow: false
			};
		},
		computed: {
			selectionList: {
				get() {
					let list = [];
					// TODO 兼容 vue2
					// #ifdef VUE2
					list = this.value;
					// #endif

					// TODO　兼容　vue3
					// #ifdef VUE3
					list = this.modelValue;
					// #endif
					if (!this.once && list.length !== 0) {
						// 只执行一次，初始化时
						const flatArr = this.getValueByKeyForObject({
								value: list,
								source: this.treeData,
								valueKey: this.nodeKey,
								targetKey: this.children,
								treeDataAll: false,
								treeChildKey: this.children
							})
							.flat(Infinity)
							.filter((item) => item)
							.map((item) => {
								return item[this.nodeKey];
							});

						this.once = true;
						const r = [...list, ...flatArr];
						return [...new Set(r)];
					}

					return list;
				},
				set(value) {
					// TODO 兼容 vue2
					// #ifdef VUE2
					this.$emit('input', value);
					// #endif

					// TODO　兼容　vue3
					// #ifdef VUE3
					this.$emit('update:modelValue', value);
					// #endif
				}
			}
		},
		watch: {
			defaultShowChildren: {
				handler() {
					if (!this.isOnceShow) {
						if (this.defaultShowChildren === true) {
							this.showChidren = this.treeToArray(
								JSON.parse(JSON.stringify(this.treeData)),
								this.children
							).map((item) => item[this.nodeKey]);
						} else if (typeof this.defaultShowChildren === 'number') {
							if (this.index < this.defaultShowChildren) {
								const findIndex = (treeData, index) => {
									let r = [];
									treeData.forEach((item) => {
										r.push(item[this.nodeKey]);
										if (
											index < this.defaultShowChildren &&
											item[this.children] &&
											item[this.children].length
										) {
											r.push(...findIndex(item[this.children], index + 1));
										}
									});
									return r;
								};
								const list = findIndex(JSON.parse(JSON.stringify(this.treeData)), 0);
								this.showChidren = list;
							}
						}
						let t = setTimeout(() => {
							clearTimeout(t);
							t = null;
							this.onceShow = this.index === 1 ? true : this.isOnceShow;
						}, 10);
					}
				},
				immediate: true
			}
		},
		methods: {
			nodeClick(e) {
				//this.$emit('nodeClick', e);
				console.log(e);
				this.toggleChildrens(e[this.nodeKey]);
			},
			// 选择
			selectionClick(node) {
				const list = [...this.selectionList];
				const value = node[this.nodeKey];
				if (node[this.children] && node[this.children].length) {
					// 此项有子集
					if (list.includes(value)) {
						// 已选->改取消选择
						const index = list.findIndex((item) => item === value);
						list.splice(index, 1);
						// 子集的也要取消掉
						const childrenData = JSON.parse(JSON.stringify(node[this.children]));
						this.treeToArray(childrenData, this.children).forEach((item) => {
							const findex = list.findIndex((fitem) => fitem === item[this.nodeKey]);
							list.splice(findex, 1);
						});
						// 父级也要取消
						this.$emit('childCancel');
					} else {
						// 加入已选
						list.push(value);
						// 子集的也要全部加入
						const childrenData = JSON.parse(JSON.stringify(node[this.children]));
						this.treeToArray(childrenData, this.children).forEach((item) => {
							list.push(item[this.nodeKey]);
						});
						// 父级看情况选择不选择
						this.$emit('childChoosed');
					}
				} else {
					// 没有子集
					if (list.includes(value)) {
						// 已选->改取消选择
						const index = list.findIndex((item) => item === value);
						list.splice(index, 1);
						// 父级也要取消
						this.$emit('childCancel');
					} else {
						// 加入已选
						list.push(value);
						// 父级看情况选择不选择
						this.$emit('childChoosed');
					}
				}

				this.selectionList = [...new Set(list)];
			},
			childChoosed(node) {
				let t = setTimeout(() => {
					clearTimeout(t);
					t = null;
					const list = [...this.selectionList];
					const value = node[this.nodeKey];
					let join = true;
					node[this.children].forEach((item) => {
						if (!list.includes(item[this.nodeKey])) {
							join = false;
						}
					});
					if (join) {
						list.push(value);
					}
					// 父级也要考虑下选择不
					this.selectionList = [...new Set(list)];
					this.$emit('childChoosed');
				}, 10);
			},
			childCancel(node) {
				let t = setTimeout(() => {
					clearTimeout(t);
					t = null;
					const list = [...this.selectionList];
					const value = node[this.nodeKey];
					const index = list.findIndex((item) => item === value);
					if (index > -1) {
						list.splice(index, 1);
						this.selectionList = [...new Set(list)];
					} // 父级也要取消
					this.$emit('childCancel');
				}, 10);
			},
			// 展开收起
			toggleChildrens(key) {
				if (this.showChidren.includes(key)) {
					const index = this.showChidren.findIndex((item) => item === key);
					this.showChidren.splice(index, 1);
				} else {
					if (this.accordion) {
						this.showChidren = [key];
					} else {
						this.showChidren.push(key);
					}
				}
			},
			// 树转数组
			treeToArray(nodes, childKey) {
				var r = [];
				if (Array.isArray(nodes)) {
					for (var i = 0, l = nodes.length; i < l; i++) {
						r.push(nodes[i]); // 取每项数据放入一个新数组
						if (Array.isArray(nodes[i][childKey]) && nodes[i][childKey].length > 0)
							// 若存在leaf则递归调用，把数据拼接到新数组中，并且删除该leaf
							r = r.concat(this.treeToArray(nodes[i][childKey]));
						delete nodes[i][childKey];
					}
				}
				return r;
			},
			getValueByKeyForObject(options) {
				if (!options) {
					return [];
				}
				const {
					value,
					source,
					valueKey,
					targetKey,
					treeDataAll = false,
					treeChildKey = 'leaf'
				} = options;
				let treePrefix = '';
				let result = [];
				if (!(value ?? '') || !source || !valueKey || !targetKey) {
					requiredKey(options, ['value', 'source', 'valueKey', 'targetKey']);
					return [];
				}
				let val = value;
				if (typeof value === 'string') {
					val = [value];
				}
				// 树数据处理
				const treeDeal = (data) => {
					if (data[treeChildKey]) {
						// 是个树
						let r = this.getValueByKeyForObject({
							...options,
							source: data[treeChildKey]
						});
						if (treeDataAll && r.length) {
							// 需要返回父级数据
							treePrefix = data[targetKey];
							r = r.map((item) => {
								return treePrefix + '/' + item;
							});
						}
						result.push(...r);
					}
				};
				if (Object.prototype.toString.call(source) === '[object Object]') {
					// object类型，假设是树
					if (val.includes(source[valueKey])) {
						result.push(source[targetKey]);
					}
					treeDeal(source);
				} else {
					// 数组，也可能是数组树
					source.forEach((item) => {
						if (Object.prototype.toString.call(item) === '[object Object]') {
							// object类型，假设是树
							if (val.includes(item[valueKey])) {
								result.push(item[targetKey]);
							}
							treeDeal(item); // 树处理
						}
						if (Array.isArray(item)) {
							// 多维数组
							result.push(
								...this.getValueByKeyForObject({
									...options,
									source: item
								})
							);
						}
					});
				}
				return result;
			},
			requiredKey(obj, keys) {
				let r = [];
				keys.forEach((item) => {
					if (ZoIsEmpty(obj[item])) {
						console.error(`${item} is required!`);
						r.push(item);
					}
				});
				return r;
			}
		}
	};
</script>

<style scoped lang="scss">
	.mosowe-tree-list-component {
		font-size: 40rpx;
		color: #333;
		line-height: 70rpx;
		font-weight: 700;

		.mosowe-tree-title-wrap {
			margin-top: 10rpx;
			border-radius: 10rpx;
			border:1rpx solid #007aff;
			display: flex;
			align-items: center;

			.mosowe-tree-title-icon {
				flex: none;
				width: 20px;
				height: 20px;

				.icon {
					width: 100%;
					height: 100%;
					display: flex;
					position: relative;
					cursor: pointer;
					transition: all 0.3s;

					&::before {
						content: '';
						display: inline-block;
						width: 0;
						height: 0;
						border: 6px solid;
						border-color: transparent transparent transparent #999;
						margin: auto;
						margin-right: -2rpx;
					}

					&.open {
						transform: rotate(90deg);
						transform-origin: center;
					}
				}
			}

			.mosowe-tree-title-selection {
				flex: none;
				width: 30rpx;
				height: 30rpx;
				border: 1rpx solid #ccc;
				border-radius: 2rpx;
				margin-right: 18rpx;
				cursor: pointer;
				position: relative;

				&.part-select {
					background-color: $uni-color-primary;
					border-color: $uni-color-primary;

					&::after {
						content: '';
						display: block;
						width: 8px;
						height: 2px;
						background-color: #fff;
						border-radius: 2px;
						position: absolute;
						left: 50%;
						top: 50%;
						transform: translateX(-50%) translateY(-50%);
					}
				}

				&.all-select {
					background-color: $uni-color-primary;
					border-color: $uni-color-primary;

					&::after {
						content: '';
						display: block;
						width: 6px;
						height: 3px;
						border: 2px solid;
						border-color: transparent transparent #fff #fff;
						border-radius: 2px;
						position: absolute;
						left: 50%;
						top: 50%;
						transform: translateX(-50%) translateY(-80%) rotate(-45deg);
					}
				}

				&.disabled {
					background-color: rgba(0, 0, 0, 0.2);
					cursor: no-drop;
				}
			}

			.mosowe-tree-title-name {
				//	flex: 1;
				cursor: pointer;
			}
			.mosowe-tree-title-name-disable {
				//	flex: 1;
				color: #999;
				cursor: pointer;
			}
		}

		.mosowe-tree-children-wrap {
			padding-left: 10px;
		}
	}
</style>