<template>
	<div class="relation-graph-wrapper">
		<div class="graph-controls">
			<div class="graph-chart-controls">
				<div
					v-for="item in controlBtns"
					:key="item.type"
					:class="['control-btn', { 'control-btn-active': currentLayoutType === item.type }]"
					@click="handleClickControlBtn(item.type)"
				>
					<img :src="item.icon" alt="" />
				</div>
			</div>
			<div class="graph-chart-legend">
				<div class="legend-item">
					<div class="legend-dot"></div>
					<span class="legend-text">已被制裁实体</span>
				</div>
				<div class="legend-item">
					<div class="legend-dot"></div>
					<span class="legend-text">未被制裁实体</span>
				</div>
			</div>
		</div>
		<div ref="containerRef" class="graph-container"></div>
		<!-- <GraphChart v-if="currentLayoutType === 1" :nodes="graphNodes" :links="graphLinks" /> -->
		<div v-if="selectedNode" class="node-popup">
			<div class="popup-header">
				<img :src="selectedNode.image || defaultIcon" alt="" class="popup-icon" />
				<div class="popup-title">{{ selectedNode.name }}</div>
				<el-icon class="close-icon" @click="selectedNode = null">
					<Close />
				</el-icon>
			</div>
			<div class="popup-body">
				<div v-if="selectedNode.isSanctioned" class="tag-row">
					<span class="red-dot"></span>
					<span class="red-text">被制裁实体</span>
				</div>
				<div v-if="selectedNode.description && selectedNode.description.length > 0" class="desc-row">
					<p class="desc" v-for="item in selectedNode.description" :key="item">{{ item }}</p>
				</div>
				<p v-else class="desc">暂无描述</p>
			</div>
		</div>
	</div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from "vue";
import G6 from "@antv/g6";
import { Close } from "@element-plus/icons-vue";
import echartsIcon01 from "../assets/echartsIcon01.png";
import echartsIcon02 from "../assets/echartsIcon02.png";
import echartsIcon03 from "../assets/echartsIcon03.png";
import defaultIcon from "../assets/echartsIcon03.png";

// import GraphChart from "@/components/base/GraphChart/index.vue";
import GraphChart from "@/views/companyPages2/component/SupplyChain/GraphChart.vue";
import { getSingleSanctionEntityInfo } from "@/api/exportControlV2.0";

const props = defineProps({
	graphData: {
		type: Object,
		default: () => ({ nodes: [], links: [] })
	},
	treeData: {
		type: Object,
		default: () => null
	},
	controlActive: {
		type: Number,
		default: 1
	}
});

const emit = defineEmits(["nodeClick", "layoutChange"]);

const containerRef = ref(null);
const graphInstance = ref(null);
const currentLayoutType = ref(1);
const selectedNode = ref(null);

const controlBtns = [
	{ type: 1, icon: echartsIcon01, name: "力导向布局" },
	{ type: 2, icon: echartsIcon02, name: "树布局" },
	{ type: 3, icon: echartsIcon03, name: "环状布局" }
];

const initGraph = (layoutType = 1) => {
	if (!containerRef.value) return;

	destroyGraph();

	nextTick(() => {
		const container = containerRef.value;
		const width = container.offsetWidth || 800;
		const height = container.offsetHeight || 600;

		if (layoutType === 2) {
			initTreeGraph(width, height);
		} else if (layoutType === 3) {
			initCircularGraph(width, height);
		} else {
			initNormalGraph(layoutType, width, height);
		}
	});
};

// const initNormalGraph = (layoutType, width, height) => {
// 	const data = processGraphData(props.graphData);
// 	console.log("初始数据", props.graphData);
// 	if (!data.nodes || data.nodes.length === 0) return;

// 	const layout = {
// 		type: "none",
// 		center: [width / 2, height / 2],
// 		preventOverlap: true,
// 		nodeSpacing: 80,
// 		linkDistance: 250,
// 		nodeStrength: -800,
// 		edgeStrength: 0.1,
// 		collideStrength: 0.8,
// 		alphaDecay: 0.01,
// 		alphaMin: 0.001
// 	};

// 	graphInstance.value = new G6.Graph({
// 		container: containerRef.value,
// 		width,
// 		height,
// 		fitView: true,
// 		fitViewPadding: 100,
// 		fitCenter: true,
// 		animate: true,
// 		animateCfg: {
// 			duration: 300,
// 			easing: "easeLinear"
// 		},
// 		minZoom: 0.1,
// 		maxZoom: 10,
// 		modes: {
// 			default: [
// 				"drag-canvas",
// 				"zoom-canvas",
// 				"drag-node",
// 				{
// 					type: "activate-relations",
// 					trigger: "mouseenter",
// 					resetSelected: true
// 				}
// 			]
// 		},
// 		layout,
// 		defaultNode: {
// 			type: "image",
// 			size: 40,
// 			clipCfg: {
// 				show: true,
// 				type: "circle",
// 				r: 20
// 			},
// 			labelCfg: {
// 				position: "bottom",
// 				offset: 10,
// 				style: {
// 					fill: "#333",
// 					fontSize: 11,
// 					fontFamily: "Microsoft YaHei",
// 					textAlign: "center",
// 					background: {
// 						fill: "rgba(255, 255, 255, 0.95)",
// 						padding: [4, 6, 4, 6],
// 						radius: 4
// 					}
// 				}
// 			}
// 			// 注意：节点边框样式在 processGraphData 中单独设置，不在这里设置
// 		},
// 		defaultEdge: {
// 			type: "quadratic",
// 			style: {
// 				stroke: "red",
// 				lineWidth: 3,
// 				opacity: 0.9,
// 				shadowColor: "rgba(231, 243, 255, 1)",
// 				shadowBlur: 4,
// 				endArrow: {
// 					path: "M 0,0 L 12,6 L 12,-6 Z",
// 					fill: "#5B8FF9"
// 				}
// 			},
// 			labelCfg: {
// 				autoRotate: true,
// 				style: {
// 					fill: "rgba(137, 193, 255, 1)",
// 					fontSize: 10,
// 					fontFamily: "Microsoft YaHei",
// 					background: {
// 						fill: "rgba(231, 243, 255, 1)",
// 						padding: [2, 4, 2, 4],
// 						radius: 5
// 					}
// 				}
// 			}
// 		},
// 		nodeStateStyles: {
// 			active: {
// 				shadowColor: "#1459BB",
// 				shadowBlur: 15,
// 				stroke: "#1459BB",
// 				lineWidth: 3
// 			},
// 			inactive: {
// 				opacity: 0.3
// 			}
// 		},
// 		edgeStateStyles: {
// 			active: {
// 				stroke: "#1459BB",
// 				lineWidth: 4
// 			},
// 			inactive: {
// 				opacity: 0.15
// 			}
// 		}
// 	});

// 	graphInstance.value.data(data);
// 	graphInstance.value.render();
// 	bindGraphEvents();
// };

const initNormalGraph = (layoutType, width, height) => {
	const data = processGraphData(props.graphData);
	console.log("初始数据", props.graphData);
	if (!data.nodes || data.nodes.length === 0) return;

	// 中心节点坐标
	const centerX = width / 2;
	const centerY = height / 2;
	const upperY = centerY - 240; // 上方节点 Y 坐标
	const lowerY = centerY + 240; // 下方节点 Y 坐标
	const nodeSpacing = 100; // 节点水平间距

	// 分离中心节点和其他节点
	const centerNode = data.nodes.find(n => n.isCenter);
	const otherNodes = data.nodes.filter(n => !n.isCenter);
	const totalNodes = otherNodes.length;
	const upperCount = Math.ceil(totalNodes / 2); // 向上取整，上方多一个
	const lowerCount = totalNodes - upperCount;

	// 为上方节点分配坐标
	const upperNodes = otherNodes.slice(0, upperCount);
	upperNodes.forEach((node, index) => {
		const totalWidth = (upperCount - 1) * nodeSpacing;
		const startX = centerX - totalWidth / 2;
		node.x = startX + index * nodeSpacing;
		node.y = upperY;
		// 固定位置，防止力导向布局移动
		node.fx = node.x;
		node.fy = node.y;
	});

	// 为下方节点分配坐标
	const lowerNodes = otherNodes.slice(upperCount);
	lowerNodes.forEach((node, index) => {
		const totalWidth = (lowerCount - 1) * nodeSpacing;
		const startX = centerX - totalWidth / 2;
		node.x = startX + index * nodeSpacing;
		node.y = lowerY;
		// 固定位置，防止力导向布局移动
		node.fx = node.x;
		node.fy = node.y;
	});

	// 设置中心节点坐标
	if (centerNode) {
		centerNode.x = centerX;
		centerNode.y = centerY;
		// 固定中心节点位置
		centerNode.fx = centerX;
		centerNode.fy = centerY;
	}

	const layout = {
		type: "none", // 使用预设坐标，不进行力导向布局
		center: [centerX, centerY]
	};

	graphInstance.value = new G6.Graph({
		container: containerRef.value,
		width,
		height,
		fitView: true,
		fitViewPadding: 100,
		fitCenter: true,
		animate: true,
		animateCfg: {
			duration: 300,
			easing: "easeLinear"
		},
		minZoom: 0.1,
		maxZoom: 10,
		modes: {
			default: [
				"drag-canvas",
				"zoom-canvas",
				"drag-node",
				{
					type: "activate-relations",
					trigger: "mouseenter",
					resetSelected: true
				}
			]
		},
		layout,
		defaultNode: {
			type: "image",
			size: 40,
			clipCfg: {
				show: true,
				type: "circle",
				r: 20
			},
			labelCfg: {
				position: "bottom",
				offset: 10,
				style: {
					fill: "#333",
					fontSize: 11,
					fontFamily: "Microsoft YaHei",
					textAlign: "center",
					background: {
						fill: "rgba(255, 255, 255, 0.95)",
						padding: [4, 6, 4, 6],
						radius: 4
					}
				}
			}
		},
		defaultEdge: {
			type: "line",
			style: {
				stroke: "red",
				lineWidth: 3,
				opacity: 0.9,
				shadowColor: "rgba(231, 243, 255, 1)",
				shadowBlur: 4,
				endArrow: {
					path: "M 0,0 L 12,6 L 12,-6 Z",
					fill: "#5B8FF9"
				}
			},
			labelCfg: {
				autoRotate: true,
				style: {
					fill: "rgba(137, 193, 255, 1)",
					fontSize: 10,
					fontFamily: "Microsoft YaHei",
					background: {
						fill: "rgba(231, 243, 255, 1)",
						padding: [2, 4, 2, 4],
						radius: 5
					}
				}
			}
		},
		nodeStateStyles: {
			active: {
				shadowColor: "#1459BB",
				shadowBlur: 15,
				stroke: "#1459BB",
				lineWidth: 3
			},
			inactive: {
				opacity: 0.3
			}
		},
		edgeStateStyles: {
			active: {
				stroke: "#1459BB",
				lineWidth: 4
			},
			inactive: {
				opacity: 0.15
			}
		}
	});

	graphInstance.value.data(data);
	graphInstance.value.render();
	bindGraphEvents();
};
const initCircularGraph = (width, height) => {
	const data = processGraphData(props.graphData);

	if (!data.nodes || data.nodes.length === 0) return;

	const centerX = width / 2;
	const centerY = height / 2;
	const radius = Math.min(width, height) / 2 - 120;

	const otherNodes = data.nodes.filter(n => !n.isCenter);
	const nodeCount = otherNodes.length;

	otherNodes.forEach((node, index) => {
		const angle = (2 * Math.PI * index) / nodeCount - Math.PI / 2;
		node.x = centerX + radius * Math.cos(angle);
		node.y = centerY + radius * Math.sin(angle);
	});

	const centerNode = data.nodes.find(n => n.isCenter);
	if (centerNode) {
		centerNode.x = centerX;
		centerNode.y = centerY;
		centerNode.fx = centerX;
		centerNode.fy = centerY;
	}

	graphInstance.value = new G6.Graph({
		container: containerRef.value,
		width,
		height,
		fitView: false,
		fitCenter: false,
		animate: true,
		animateCfg: {
			duration: 300,
			easing: "easeLinear"
		},
		minZoom: 0.1,
		maxZoom: 10,
		modes: {
			default: [
				"drag-canvas",
				"zoom-canvas",
				"drag-node",
				{
					type: "activate-relations",
					trigger: "mouseenter",
					resetSelected: true
				}
			]
		},
		defaultNode: {
			type: "image",
			size: 40,
			clipCfg: {
				show: true,
				type: "circle",
				r: 20
			},
			labelCfg: {
				position: "bottom",
				offset: 10,
				style: {
					fill: "#333",
					fontSize: 11,
					fontFamily: "Microsoft YaHei",
					textAlign: "center",
					background: {
						fill: "rgba(255, 255, 255, 0.95)",
						padding: [4, 6, 4, 6],
						radius: 4
					}
				}
			}
		},
		defaultEdge: {
			type: "line",
			style: {
				stroke: "#5B8FF9",
				lineWidth: 3,
				opacity: 0.9,
				endArrow: {
					path: "M 0,0 L 12,6 L 12,-6 Z",
					fill: "#5B8FF9"
				}
			},
			labelCfg: {
				autoRotate: true,
				style: {
					fill: "#333",
					fontSize: 10,
					fontFamily: "Microsoft YaHei",
					background: {
						fill: "#fff",
						padding: [2, 4, 2, 4],
						radius: 2
					}
				}
			}
		},
		nodeStateStyles: {
			active: {
				shadowColor: "#1459BB",
				shadowBlur: 15,
				stroke: "#1459BB",
				lineWidth: 3
			},
			inactive: {
				opacity: 0.3
			}
		},
		edgeStateStyles: {
			active: {
				stroke: "#1459BB",
				lineWidth: 4
			},
			inactive: {
				opacity: 0.15
			}
		}
	});

	graphInstance.value.data(data);
	graphInstance.value.render();
	bindGraphEvents();
};

const initTreeGraph = (width, height) => {
	const treeDataSource = convertGraphToTree(props.graphData);

	if (!treeDataSource) return;

	graphInstance.value = new G6.TreeGraph({
		container: containerRef.value,
		width,
		height,
		fitView: true,
		fitViewPadding: 80,
		animate: true,
		animateCfg: {
			duration: 300,
			easing: "easeLinear"
		},
		minZoom: 0.1,
		maxZoom: 10,
		modes: {
			default: [
				"drag-canvas",
				"zoom-canvas",
				"drag-node",
				{
					type: "collapse-expand",
					onChange: function onChange(item, collapsed) {
						const data = item.getModel();
						data.collapsed = collapsed;
						return true;
					}
				}
			]
		},
		layout: {
			type: "compactBox",
			direction: "LR",
			getId: function getId(d) {
				return d.id;
			},
			getHeight: function getHeight() {
				return 16;
			},
			getWidth: function getWidth() {
				return 16;
			},
			getVGap: function getVGap() {
				return 30;
			},
			getHGap: function getHGap() {
				return 120;
			}
		},
		defaultNode: {
			type: "image",
			size: 40,
			clipCfg: {
				show: true,
				type: "circle",
				r: 20
			},
			labelCfg: {
				position: "right",
				offset: 10,
				style: {
					fill: "#333",
					fontSize: 11,
					fontFamily: "Microsoft YaHei",
					background: {
						fill: "rgba(255, 255, 255, 0.95)",
						padding: [4, 6, 4, 6],
						radius: 4
					}
				}
			}
		},
		defaultEdge: {
			type: "cubic-horizontal",
			style: {
				stroke: "#5B8FF9",
				lineWidth: 3
			}
		},
		nodeStateStyles: {
			active: {
				shadowColor: "#1459BB",
				shadowBlur: 15,
				stroke: "#1459BB",
				lineWidth: 3
			}
		}
	});

	graphInstance.value.data(treeDataSource);
	graphInstance.value.render();
	graphInstance.value.fitView();
	bindGraphEvents();
};

const convertGraphToTree = graphData => {
	if (!graphData || !graphData.nodes || graphData.nodes.length === 0) {
		return null;
	}

	const nodes = graphData.nodes;
	const links = graphData.links || graphData.edges || [];
	const centerNode = nodes[0];
	const centerId = String(centerNode.id || "0");

	const childIdSet = new Set();
	const childrenNodes = [];

	links.forEach(link => {
		const source = String(link.source);
		const target = String(link.target);

		if (source === centerId && !childIdSet.has(target)) {
			const node = nodes.find(n => String(n.id) === target);
			if (node) {
				childIdSet.add(target);
				childrenNodes.push({
					id: target,
					label: node.name || "",
					img: node.image || defaultIcon,
					size: node.symbolSize || 40,
					name: node.name,
					image: node.image,
					isSanctioned: node.isSanctioned
				});
			}
		} else if (target === centerId && !childIdSet.has(source)) {
			const node = nodes.find(n => String(n.id) === source);
			if (node) {
				childIdSet.add(source);
				childrenNodes.push({
					id: source,
					label: node.name || "",
					img: node.image || defaultIcon,
					size: node.symbolSize || 40,
					name: node.name,
					image: node.image,
					isSanctioned: node.isSanctioned
				});
			}
		}
	});

	if (childrenNodes.length === 0) {
		nodes.slice(1).forEach(node => {
			const nodeId = String(node.id);
			if (!childIdSet.has(nodeId)) {
				childIdSet.add(nodeId);
				childrenNodes.push({
					id: nodeId,
					label: node.name || "",
					img: node.image || defaultIcon,
					size: node.symbolSize || 40,
					name: node.name,
					image: node.image,
					isSanctioned: node.isSanctioned
				});
			}
		});
	}

	return {
		id: centerId,
		label: centerNode.name || "",
		img: centerNode.image || defaultIcon,
		size: centerNode.symbolSize || 60,
		name: centerNode.name,
		image: centerNode.image,
		isSanctioned: centerNode.isSanctioned,
		children: childrenNodes
	};
};

const processGraphData = rawData => {
	if (!rawData || !rawData.nodes || rawData.nodes.length === 0) {
		return { nodes: [], edges: [] };
	}
	console.log("数据处理 =>", rawData);
	const nodeMap = new Map();
	const nodes = [];

	rawData.nodes.forEach((node, index) => {
		const nodeId = String(node.id || index);

		if (nodeMap.has(nodeId)) {
			return;
		}
		nodeMap.set(nodeId, true);

		const isCenter = index === 0;
		const size = node.symbolSize || (isCenter ? 60 : 40);
		// 根据 isSanctioned 设置节点边框
		const strokeColor = !node.isSanctioned ? "rgba(231, 243, 255, 1)" : "transparent";
		const lineWidth = !node.isSanctioned ? 10 : 0;
		const fontColor = node.isSanctioned ? "rgba(5, 95, 194, 1)" : "#333";
		nodes.push({
			id: nodeId,
			label: node.name || "",
			img: node.image || defaultIcon,
			size,
			isCenter,
			clipCfg: {
				show: true,
				type: "circle",
				r: size / 2
			},
			style: {
				cursor: "pointer",
				stroke: strokeColor,
				lineWidth: lineWidth
			},
			labelCfg: {
				position: "bottom",
				offset: 12,
				style: {
					fill: fontColor,
					fontSize: isCenter ? 13 : 11,
					fontWeight: isCenter ? "bold" : "normal",
					fontFamily: "Microsoft YaHei",
					textAlign: "center"
				}
			},
			...node,
			id: nodeId
		});
	});

	const edgeMap = new Map();
	const edges = [];

	const rawEdges = rawData.links || rawData.edges || [];
	rawEdges.forEach((edge, index) => {
		const source = String(edge.source);
		const target = String(edge.target);
		const edgeKey = `${source}-${target}`;
		const isSanctioned = rawData.nodes[index].isSanctioned;
		const strokeColor = isSanctioned ? "rgba(5, 95, 194, 1)" : "#5B8FF9";
		const lineColor = isSanctioned ? "rgba(5, 95, 194, 1)" : "rgba(59, 65, 75, .3)";

		if (edgeMap.has(edgeKey)) {
			return;
		}
		if (!nodeMap.has(source) || !nodeMap.has(target)) {
			return;
		}
		edgeMap.set(edgeKey, true);

		edges.push({
			id: `edge-${index}`,
			source,
			target,
			label: edge.name || "",
			// 添加边的边框效果（使用 shadow 模拟）
			style: {
				stroke: lineColor,
				lineWidth: 2,
				opacity: 0.9,
				shadowColor: "rgba(231, 243, 255, 1)",
				shadowBlur: 4,
				endArrow: {
					path: "M 0,0 L 12,6 L 12,-6 Z",
					fill: lineColor
				}
			}
		});
	});

	return { nodes, edges };
};

const bindGraphEvents = () => {
	if (!graphInstance.value) return;

	graphInstance.value.on("node:click", evt => {
		const node = evt.item;
		const model = node.getModel();
		console.log("点击节点model", model);
		const id = model.id.split("-")[1];
		let desList = [];
		getSingleSanctionEntityInfo(id).then(res => {
			console.log("制裁实体信息", res);
			if (res.data.sanInfoList && res.data.sanInfoList.length > 0) {
				desList = res.data.sanInfoList.map(item => item.sanReason);
			}
		});
		selectedNode.value = { ...model, description: desList };
		emit("nodeClick", model);
	});

	graphInstance.value.on("canvas:click", () => {
		selectedNode.value = null;
	});
};

// 将传入的 graphData 转换为 GraphChart.vue 需要的格式
// GraphChart.vue 需要节点有 x, y 坐标，所以我们需要一个简单的布局算法
const graphNodes = ref([]);
const graphLinks = ref([]);
const applyStarLayout = (rawNodes, rawLinks) => {
	if (!rawNodes?.length) return { nodes: [], links: [] };

	const centerX = 0;
	const centerY = 0;
	const radius = 200; // 环绕半径

	const nodes = rawNodes.map((node, index) => {
		let x, y;
		if (index === 0) {
			// 中心节点
			x = centerX;
			y = centerY;
		} else {
			// 其他节点均匀分布在圆周上
			const angle = (2 * Math.PI * (index - 1)) / (rawNodes.length - 1);
			x = centerX + radius * Math.cos(angle);
			y = centerY + radius * Math.sin(angle);
		}

		return {
			id: String(node.id ?? index),
			name: node.name || "",
			type: node.isSanctioned ? "sanctioned" : "normal", // 用于区分样式（如果需要）
			symbolSize: node.symbolSize || (index === 0 ? 60 : 40),
			symbol: `image://${node.image || defaultIcon}`,
			isSanctioned: node.isSanctioned,
			image: node.image || defaultIcon,
			x: x,
			y: y,
			...node // 透传其他字段
		};
	});

	const links = (rawLinks || []).map(link => ({
		source: String(link.source),
		target: String(link.target),
		type: link.name || "relation", // GraphChart.vue 使用 type 字段
		label: {
			show: true,
			formatter: link.name || ""
		}
	}));

	return { nodes, links };
};
const handleClickControlBtn = btn => {
	currentLayoutType.value = btn;
	emit("layoutChange", btn);
	initGraph(btn);
};

const destroyGraph = () => {
	if (graphInstance.value) {
		graphInstance.value.destroy();
		graphInstance.value = null;
	}
};

const handleResize = () => {
	if (graphInstance.value && containerRef.value) {
		const width = containerRef.value.offsetWidth;
		const height = containerRef.value.offsetHeight;
		graphInstance.value.changeSize(width, height);
		graphInstance.value.fitView();
	}
};

watch(
	() => props.graphData,
	newVal => {
		const { nodes, links } = applyStarLayout(newVal.nodes, newVal.links);
		graphNodes.value = JSON.parse(JSON.stringify(nodes));
		graphLinks.value = JSON.parse(JSON.stringify(links));
	},
	{ deep: true, immediate: true }
);

watch(
	() => props.graphData,
	() => {
		initGraph(currentLayoutType.value);
	},
	{ deep: true }
);

watch(
	() => props.treeData,
	() => {
		if (currentLayoutType.value === 2) {
			initGraph(2);
		}
	},
	{ deep: true }
);

watch(
	() => props.controlActive,
	newVal => {
		if (newVal !== currentLayoutType.value) {
			handleClickControlBtn(newVal);
		}
	}
);

onMounted(() => {
	initGraph(1);
	window.addEventListener("resize", handleResize);
});

onUnmounted(() => {
	window.removeEventListener("resize", handleResize);
	destroyGraph();
});

defineExpose({
	refresh: () => initGraph(currentLayoutType.value),
	changeLayout: type => handleClickControlBtn(type),
	getGraph: () => graphInstance.value
});
</script>

<style lang="scss" scoped>
.relation-graph-wrapper {
	position: relative;
	width: 100%;
	height: 100%;
}

.graph-container {
	width: 100%;
	height: 100%;
}

.graph-controls {
	position: absolute;
	top: 16px;
	// right: 16px;
	display: flex;
	gap: 8px;
	z-index: 10;
	width: 100%;
	display: flex;
	justify-content: space-between;
	padding: 0 20px;
	.graph-chart-controls {
		display: flex;
		gap: 0px;
		box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
	}

	.control-btn {
		width: 32px;
		height: 32px;
		border-radius: 4px;
		// border: 1px solid rgba(234, 236, 238, 1);
		background: rgba(255, 255, 255, 1);
		display: flex;
		align-items: center;
		justify-content: center;
		cursor: pointer;

		img {
			width: 16px;
			height: 16px;
		}

		&:hover {
			border-color: rgba(5, 95, 194, 0.5);
		}
	}

	.control-btn-active {
		border-color: rgba(5, 95, 194, 1);
		background: rgba(231, 243, 255, 1);
	}

	.graph-chart-legend {
		display: flex;
		gap: 8px;
		.legend-item {
			display: flex;
			align-items: center;
			gap: 4px;
			font-size: 14px;
			font-family: "Microsoft YaHei";
			color: rgba(59, 65, 75, 1);
			.legend-dot {
				width: 12px;
				height: 12px;
				border-radius: 50%;
				background: rgba(5, 95, 194, 1);
			}
		}
		.legend-item:last-child {
			.legend-dot {
				background: #333;
			}
		}
	}
}

.node-popup {
	position: absolute;
	bottom: 16px;
	left: 16px;
	width: 320px;
	background: rgba(255, 255, 255, 1);
	border-radius: 8px;
	box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.1);
	border: 1px solid rgba(234, 236, 238, 1);
	z-index: 20;

	.popup-header {
		display: flex;
		align-items: center;
		padding: 12px 16px;
		border-bottom: 1px solid rgba(234, 236, 238, 1);

		.popup-icon {
			width: 32px;
			height: 32px;
			margin-right: 8px;
			border-radius: 50%;
			object-fit: cover;
		}

		.popup-title {
			flex: 1;
			font-size: 16px;
			font-weight: 700;
			font-family: "Microsoft YaHei";
			color: rgba(59, 65, 75, 1);
			white-space: nowrap;
			overflow: hidden;
			text-overflow: ellipsis;
		}

		.close-icon {
			cursor: pointer;
			color: rgba(132, 136, 142, 1);
			font-size: 16px;

			&:hover {
				color: rgba(5, 95, 194, 1);
			}
		}
	}

	.popup-body {
		padding: 12px 16px;

		.tag-row {
			display: flex;
			align-items: center;
			margin-bottom: 8px;

			.red-dot {
				width: 6px;
				height: 6px;
				border-radius: 50%;
				background: rgba(245, 63, 63, 1);
				margin-right: 8px;
			}

			.red-text {
				font-size: 14px;
				font-family: "Microsoft YaHei";
				color: rgba(245, 63, 63, 1);
			}
		}

		.desc {
			font-size: 14px;
			font-family: "Microsoft YaHei";
			line-height: 22px;
			color: rgba(95, 101, 108, 1);
			margin: 0;
		}
	}
}
</style>
