<template>
	<div class="test">
		<div
			class="control"
			:class="{ active: item.id === activeId }"
			v-for="(item, index) in list"
			:key="item.id"
			@click="onSelect(item.id)"
		>
			<div>
				<b>{{ item.name }}</b>
				<button
					v-if="index !== 0"
					@click="onMove(item.id, list[index - 1].id)"
				>
					←
				</button>
				<button
					v-if="index !== list.length - 1"
					@click="onMove(item.id, list[index + 1].id)"
				>
					→
				</button>
			</div>
			<div>
				<span>偏移X</span>
				<input type="number" v-model="item.location[0]" />
			</div>
			<div>
				<span>偏移Y</span>
				<input type="number" v-model="item.location[1]" />
			</div>
			<div>
				<span>角度</span>
				<input type="number" v-model="item.angle" />
			</div>
			<div>
				<span>缩放</span>
				<input type="number" v-model="item.scale[0]" />
			</div>
			<div>
				<span>水平间距</span>
				<input type="number" v-model="item.object.gapX" />
			</div>
			<div>
				<span>垂直间距</span>
				<input type="number" v-model="item.object.gapY" />
			</div>
			<hr />
			<hr />
			<div>
				<span>缩放X</span>
				<input type="number" v-model="item.object.scaleX" step="0.1" />
			</div>
			<div>
				<span>缩放Y</span>
				<input type="number" v-model="item.object.scaleY" step="0.1" />
			</div>
			<div>
				<span>X</span>
				<input type="number" v-model="item.object.left" />
			</div>
			<div>
				<span>Y</span>
				<input type="number" v-model="item.object.top" />
			</div>
			<div>
				<span>角度</span>
				<input type="number" v-model="item.object.angle" />
			</div>

			<div>
				<span>透明度</span>
				<input
					type="range"
					v-model="item.object.opacity"
					step="0.1"
					min="0"
					max="1"
				/>
			</div>
		</div>
		<div class="box">
			<pingpu
				:list="list"
				ref="pingpuRef"
				@change-canvas="updateCanvas"
			/>
		</div>
	</div>
</template>

<script lang="ts" setup>
	import { ref, onMounted, watch } from "vue";
	import pingpu from "./pingpu.vue";
	const ACTIONS = {
		SELECT: "select",
		UPDATE: "update",
		DELETE: "delete",
		SORT: "sort",
	};

	const convertDotNotationToBracket = (str) =>
		str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
	const activeId = ref("1");
	const list = ref([
		{
			id: "1",
			ifSingle: false,
			level2Type: "Pattern",
			designType: "Library",
			path: "/src/assets/images/canvas/yinhua1.jpg",
			location: [0, 0],
			scale: [1, 1],
			angle: 0,
			name: "Print1",
			priority: 1,
			object: {
				top: 0,
				left: 0,
				scaleX: 1,
				scaleY: 1,
				opacity: 1,
				angle: 0,
				flipX: false,
				flipY: false,
				blendMode: "multiply",
				gapX: 10,
				gapY: 20,
			},
		},
		{
			id: "2",
			ifSingle: false,
			level2Type: "Pattern",
			designType: "Library",
			path: "/src/assets/images/canvas/yinhua1.jpg",
			location: [0, 0],
			scale: [2, 2],
			angle: -45,
			name: "Print2",
			priority: 1,
			object: {
				top: 150,
				left: 250,
				scaleX: 0.5,
				scaleY: 0.5,
				opacity: 1,
				angle: 45,
				flipX: false,
				flipY: false,
				blendMode: "multiply",
				gapX: 0,
				gapY: 0,
			},
		},
	]);
	// 深拷贝
	const deepCopy = (obj) => JSON.parse(JSON.stringify(obj));
	const oldList = ref(deepCopy(list.value));
	const pingpuRef = ref(null);
	const updateCanvas = (arr) => {
		oldList.value = deepCopy(list.value);
		arr.forEach((item) => {
			list.value.forEach((v) => {
				if (item.action === ACTIONS.UPDATE) {
					if (v.id === item.id) {
						if (item.action === ACTIONS.UPDATE) {
							try {
								const key = item.key;
								const str = /^\[/.test(item.key)
									? "v" + key
									: "v." + key;
								eval(`${str} = item.value`);
							} catch (error) {
								console.error(error);
							}
						}
					}
				} else if (item.action === ACTIONS.SELECT) {
					activeId.value = item.id;
				}
			});
		});
	};

	const onSelect = (id) => {
		activeId.value = id;
		pingpuRef.value.updataList([
			{
				id: id,
				action: ACTIONS.SELECT,
			},
		]);
	};
	const onMove = (id, id2) => {
		const obj1 = list.value.find((v) => v.id === id);
		const obj2 = list.value.find((v) => v.id === id2);
		const index1 = list.value.indexOf(obj1);
		const index2 = list.value.indexOf(obj2);
		if (index1 < index2) {
			list.value.splice(index2, 0, list.value.splice(index1, 1)[0]);
		} else {
			list.value.splice(index1, 0, list.value.splice(index2, 1)[0]);
		}
		const ids = list.value.map((v) => v.id);
		pingpuRef.value.updataList([
			{
				action: ACTIONS.SORT,
				ids,
			},
		]);
	};
	watch(
		() => list.value,
		() => updateList(),
		{ deep: true }
	);
	// 监听列表变化属性变更
	const updateList = () => {
		const changeList = [];
		oldList.value.forEach((oldItem) => {
			const newItem = list.value.find((v) => v.id === oldItem.id);
			if (newItem) {
				const arr = findDiffProps(oldItem, newItem);
				arr.forEach((item) => {
					changeList.push({
						...item,
						id: oldItem.id,
						action: ACTIONS.UPDATE,
						key: convertDotNotationToBracket(item.key),
					});
				});
			} else {
				changeList.push({
					id: oldItem.id,
					action: ACTIONS.DELETE,
				});
			}
		});
		oldList.value = deepCopy(list.value);
		if (changeList.length) {
			pingpuRef.value.updataList(changeList);
		}
	};
	// 递归查找不同的属性
	const findDiffProps = (obj1, obj2, diffProps = [], path = "") => {
		for (const key in obj1) {
			if (typeof obj1[key] === "object") {
				findDiffProps(obj1[key], obj2[key], diffProps, `${path}${key}.`);
			} else if (obj1[key] !== obj2[key]) {
				diffProps.push({
					key: `${path}${key}`,
					value: obj2[key],
				});
			}
		}
		return diffProps;
	};
</script>

<style lang='less' scoped>
	.test {
		> .control {
			display: inline-block;
			border: 1px solid #000;
			padding: 10px;
			margin: 10px;
			border-radius: 10px;
			&.active {
				border-color: rgb(17, 68, 223);
				box-shadow: 0 0 5px rgb(17, 68, 223);
			}
			> div {
				display: flex;
				align-items: center;
				margin-bottom: 5px;
				> * {
					margin-right: 10px;
					&:last-child {
						margin-right: 0;
					}
				}
				> span {
					width: 80px;
				}
				> input:not([type="range"]) {
					padding-left: 10px;
					border-radius: 5px;
				}
			}
		}
		> .box {
			width: 400px;
			height: 500px;
			overflow: hidden;
			border: 1px solid #000;
		}
	}
</style>