<template>
	<view class="container" v-if="dataAry.length > 0">
		<block v-for="(strObj, index) in dataAry" :key="index">
			<view
				v-if="strObj.show"
				class="line"
				:style="{
					paddingLeft: strObj.indent + 'px',
					fontSize: fontSize+'rpx'
				}"
			>
				<text
					v-if="collapsable && strObj.hasChild"
					:data-index="index"
					@longtap="expandAll"
					@tap="doExpand"
					:class="clsgrp[clstype][strObj.expand]"
				></text>
				<view style="display: flex; flex-flow: row;">
					<text class="key" :class="`${strObj.ktype} ` + (kidx === index?'active':'')" @tap="selectk(index)">{{ strObj.key }}</text>
					<text v-if="strObj.comma">{{ `${strObj.comma}` }}</text>
					<text v-if="strObj.value" class="val" :class="`${strObj.type} ` + (vidx === index?'active':'')" @tap="selectv(index)">{{ strObj.value }}</text>
					<text v-if="strObj.tail" class="val">{{ strObj.tail }}</text>
					<block v-if="!!strObj.suffix">
						<text class="val">{{ '...' }}</text>
						<text class="val">{{ strObj.suffix }}</text>
					</block>
				</view>
			</view>
		</block>
	</view>
</template>

<script>
	export default {
		name: 'XdJson',
		props: {
			list:{
				required: true
			},
			collapsable: {
				type: Boolean,
				default: true
			},
			fontSize: {
				type: Number,
				default: 30
			},
			icon: {
				type: String,
				default: 'plusminus'
			},
			level: {
				type: Number,
				default: 1
			}
		},
		data() {
			return {
				kidx: -1,
				vidx: -1,
				kt: 0,
				vt: 0,
				showCopy: false,
				basecode: '0123456789abcdefghijklmnopqrstuvwxyz',
				dataAry: [],
				clstype: ['arrow','plusminus'].includes(this.icon)?this.icon:'plusminus',
				clsgrp: {
					'arrow': {
						true: 'arrow-down',
						false: 'arrow-right'
					},
					'plusminus': {
						true: 'minus',
						false: 'plus'
					},
				},
				obj: {},
			};
		},
		created() {
			this.obj = this.$xdUniHelper.cloneDeep(this.list);
			this.init();
		},
		watch: {
			collapsable(val) {
				if (!val) {
					this.expandAll()
				}
			},
			icon(val){
				if (!!val){
					this.clstype = ['arrow','plusminus'].includes(val)?val:'plusminus'
				}
			},
			list(value){
				this.obj = this.$xdUniHelper.cloneDeep(value);
				this.init();
			}
		},
		methods: {
			init(){
				let __this = this;
				let str = JSON.stringify(this.obj, null, ' ');
				let pl = 0;
				let idn = str.split(/\n/g).length - 1;
				let reg = /"(.+)"\s*:\s*("?.+"?),?/;
				let dataAry = str
					.split(/\n/g)
					.reverse()
					.map((t,index) => {
						let res = {
							text: t
						};
						let regRes = reg.exec(t);
						if (regRes) {
							res.key = regRes[1].trim();
							res.value = regRes[2].replace(/,$/, '');
							res.tail = regRes[2] === res.value ? '' : ',';
							res.comma = ':';
							if ('{' === res.value) {
								res.type = 'Object'
							} else if ('[' === res.value) {
								res.type = 'Array'
							} else {
								try {
									let t = new Function('', 'return Object.prototype.toString.call(' + res.value + ')')()
									res.type = /\[object (.*)\]$/.exec(t)[1]
								} catch (e) {
								}
							}
						} else {
							res.key = t;
							res.type
							res.value = '';
							res.comma = '';
							if (!['{', '}', '[', ']'].includes(t.trim().replace(/,$/, '').replace(/\"/g, ''))) {
								try {
									let kt = new Function('', 'return Object.prototype.toString.call(' + t + ')')()
									res.ktype = /\[object (.*)\]$/.exec(kt)[1]
								} catch (e) {
								}
							}
						}
						res.indent = t.search(/[^\s]/) * this.fontSize;
						res.hasChild = res.indent < pl
										|| res.value === '{}'
										|| res.value === '[]';
						res.expand = true;
						res.show = true;
						res.suffix = '';
						res.index = idn;
						res.pl = pl = res.indent;
						res.level = res.indent/this.fontSize;
						idn--;
						return res;
					});
				pl = -1;
				let pcode = '';

				//计算数据
				this.dataAry = dataAry.reverse().map(item => {
					const cl = item.indent / this.fontSize;
					if (cl > pl) {
						item.treeCode = pcode + this.basecode.charAt(0);
					} else if (cl === pl) {
						var lastIdx = this.basecode.indexOf(pcode.charAt(pcode.length - 1)) + 1;
						item.treeCode = pcode.substr(0, pcode.length - 1) + this.basecode.charAt(lastIdx);
					} else {
						item.treeCode = pcode.substr(0, pcode.length - 1);
					}
					pl = cl;
					pcode = item.treeCode;
					return item;
				});

				this.dataAry.map(item => {
					if (item.level === this.level && item.hasChild) {
						this.doExpand({currentTarget: {dataset: {index: item.index}}});
					}
				});
			},

			getExpandStatus(code) {
				return this.dataAry.find(item => item.treeCode == code).expand;
			},
			isTail(idx) {
				let code = this.dataAry[idx].treeCode;
				let index = this.dataAry.findIndex(item => item.treeCode == code);
				return index < idx;
			},
			getParentsExpandStatus(code) {
				if (code === '0') return true;
				let parents = this.dataAry.filter(item => code.startsWith(item.treeCode) && item.treeCode != code);
				let res = true;
				parents.forEach(item => {
					if (!item.expand) {
						res = false;
					}
				});
				return res;
			},
			expandAll(e) {
				let index = 0;
				if (e) {
					const {
						index: k
					} = e.currentTarget.dataset;
					index = parseInt(k);
				}
				const code = this.dataAry[index].treeCode;
				this.dataAry = this.dataAry.map(item => {
					if (item.treeCode.startsWith(code)) {
						item.suffix = '';
						item.show = true;
						item.expand = true;
					}
					return item;
				});
			},
			print() {

			},
			doExpand(e) {

				let index = 0;
				if (e) {
					const {
						index: k
					} = e.currentTarget.dataset;
					index = parseInt(k);
				}
				let suffixText = '';
				const code = this.dataAry[index].treeCode;
				const expand = !this.dataAry[index].expand; //点击之后的展开状态
				this.dataAry[index].expand = expand; //更新展开状态
				const dataAry = this.dataAry.map((item, idx) => {
					if (item.treeCode.startsWith(code) && idx != index) {
						//排除本身
						if (!expand) {
							//如果折叠，子项全部隐藏
							item.show = false;
						} else {
							//如果展开，子项的尾巴是否显示看他的父级展开状态
							item.show = this.getParentsExpandStatus(item.treeCode);
						}
						if (item.treeCode == code) {
							// last one
							suffixText = expand ? '' : `${item.text.trim()}`;
						}
						// 隐藏结尾的 ] } 等
						if (this.isTail(idx) && !this.getExpandStatus(item.treeCode)) {
							item.show = false;
						}
					}
					// 处理后缀
					if (item.expand) {
						item.suffix = '';
					}
					return item;
				});
				dataAry[index].suffix = suffixText;
				this.dataAry = dataAry;
			},
			selectk(idx) {
				this.vidx = idx;
				clearTimeout(this.vt);
				this.vt = setTimeout(() => {
					this.vidx = -1;
				}, 600);
				if (this.collapsable)
				this.doExpand({
					currentTarget: {
						dataset: {
							index: idx
						}
					}
				})
			},
			selectv(idx) {
				this.kidx = idx;
				clearTimeout(this.kt);
				this.kt = setTimeout(() => {
					this.kidx = -1;
				}, 600);
			}
		}
	};
</script>

<style scoped lang="less">
	.container {
		width: 100%;
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		align-content: center;
		justify-content: flex-start;
		box-sizing: border-box;
	}

	.container .line {
		display: flex;
		position: relative;
		align-items: center;
		justify-content: flex-start;
		width: inherit;
		box-sizing: border-box;
	}

	text {
		margin: 0 unit(4, rpx);
	}

	.key,
	.val {
		height: min-content;
		display: inline-block;
		transition-duration: 0.3s;
		border-bottom: solid unit(4, rpx) transparent;
		font-weight: bold;
	}

	.key.active,
	.val.active {
		transition-duration: 0.3s;
		border-bottom: solid unit(4, rpx) rgba(255, 90, 20, 0.8);
	}

	.key {
		color: #666;
	}

	.val {
		color: #444;
	}

	.String {
		color: rgba(198, 24, 22, 1);
	}

	.Number {
		color: blue;
	}

	.Object {
		color: green;
	}

	.expand {
		display: inline-flex;
		width: unit(50, rpx);
		height: unit(50, rpx);
		padding: 0;
		align-items: center;
		transform: scale(0.8);
		justify-items: center;
		justify-content: center;
		border: solid 1px lightgray;
		box-sizing: border-box;
		color: gray;
		font-size: unit(24, rpx);
	}

	.arrow-right,
	.arrow-down {
		display: inline-block;
		width: unit(40, rpx);
		height: unit(40, rpx);
		background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M-1-1h8.575v8.575H-1z'/%3E%3Cg%3E%3Cpath stroke='%2300f' d='M11.482 11.865L2.132.896l16.812 10.969L2.132 22.833l9.35-10.968z' fill-opacity='null' stroke-opacity='null' stroke-width='null' fill='%2300f'/%3E%3C/g%3E%3C/svg%3E");
		background-size: cover;
		margin-right: unit(20, rpx);
	}

	.arrow-down {
		transform: rotate(90deg);
		-ms-transform: rotate(90deg);
		-moz-transform: rotate(90deg);
		-webkit-transform: rotate(90deg);
		-o-transform: rotate(90deg);
	}

	.minus,
	.plus {
		display: inline-block;
		width: unit(40, rpx);
		height: unit(40, rpx);
		background-size: cover;
	}

	.plus {
		background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDJBNUU1NzlGOTIwMTFFQzkxOTdBQUQ3REFFMzQwMDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDJBNUU1N0FGOTIwMTFFQzkxOTdBQUQ3REFFMzQwMDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEMkE1RTU3N0Y5MjAxMUVDOTE5N0FBRDdEQUUzNDAwNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEMkE1RTU3OEY5MjAxMUVDOTE5N0FBRDdEQUUzNDAwNSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgPTjN8AAAD0SURBVHja7Fm7DcIwEL0zWYGKnoYGynTskDHIEIElYAcYgga5hQ0yBkKKD+hjCeGE8KJ3raunez/bmvuV+ftVUMdpI9DjXhCwAZga+AYC+AZEAjSArA8N2Lp9rXp2nfPVqaGLGFvDbxsFdyF4CklAzwH4JLbADVADSS6kDhtAgM+BhjY6LhEf5vvo2WZWdg5A88vS/OP2cSUeamJVfAR1eiLUAIMsiULgzypZjELfXsBPi6MU06LVGaq6sm29+42NwrxKMInZhVIBgA//Bxhk6S7E/wFqIM2F+D8wOIXA63QfFOqjdbLM/bGImQO80KTMU4ABAEqmWWmC0CI7AAAAAElFTkSuQmCC");
	}

	.minus {
		background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDJBNUU1N0RGOTIwMTFFQzkxOTdBQUQ3REFFMzQwMDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDJBNUU1N0VGOTIwMTFFQzkxOTdBQUQ3REFFMzQwMDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEMkE1RTU3QkY5MjAxMUVDOTE5N0FBRDdEQUUzNDAwNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEMkE1RTU3Q0Y5MjAxMUVDOTE5N0FBRDdEQUUzNDAwNSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvdQnucAAACwSURBVHja7JkhFsMwDEPtdBcqLNvRy0J3nJHG3fiCDBIt37TIz/pS+uRH3aO+X6Y6xS+TnvJZQXuB8BC/QBO/gFmDgbEuFOoQazP8lVAgocE2qp4D8kkc5AAM5FzIi/YCTT4HLmwUiDPz6H2I51x/On7+hvUPntObwQBBlpKQq7tQR0I96udjwNQhJom5wOIM0A8QZGkXoh+AgZwL0Q8MlxD9AI+5xZOYfgAGUnMLMAClHDziOQApkAAAAABJRU5ErkJggg==");
	}
</style>
