echarts樹狀圖與vue3

父組件 - 使用RadialTreeView

<template><div class="page-container"><div class="header-title">美國產品圖譜 (ECharts Radial Tree)</div><RadialTreeView :chart-data="radialData" background-color="#E6E6FA" /></div>
</template><script setup lang="ts">
import RadialTreeView from "@/views/knowledgeGraph/radialTreeView/RadialTreeView.vue";
import {mockRadialData,TreeNode,
} from "@/views/knowledgeGraph/radialTreeView/mockRadialTreeData"; // 確保路徑正確
import { ref } from "vue";const radialData = ref<TreeNode>(mockRadialData);// If you need to update data later:
// setTimeout(() => {
//   const newData = JSON.parse(JSON.stringify(mockRadialData)); // Deep copy
//   newData.children[0].name = 'Updated Customers';
//   newData.children[0].children.push({ name: 'New Customer Segment', id: 'new_cust', nodeType: 'customer' });
//   radialData.value = newData;
// }, 5000);
</script><style scoped>
.page-container {width: 100%;height: 90vh; /* Adjust as needed */display: flex;flex-direction: column;padding: 20px;box-sizing: border-box;background-color: #f0f2f5; /* A light background for the page */
}.header-title {font-size: 24px;font-weight: bold;text-align: center;margin-bottom: 20px;color: #333;
}/* RadialTreeView will take full width/height of its parent if not constrained */
/* Ensure its container in this page has defined dimensions or it fills the space */
</style>

子組件-創建RadialTreeView

<template><div class="radial-tree-view-container"><div ref="chartRef" class="chart-container"></div></div>
</template><script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
import * as echarts from "echarts/core";
import { TreeChart, TreeSeriesOption } from "echarts/charts";
import {TooltipComponent,TooltipComponentOption,LegendComponent,LegendComponentOption,
} from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import type { EChartsOption, ECBasicOption } from "echarts/types/dist/shared";echarts.use([TreeChart, TooltipComponent, LegendComponent, CanvasRenderer]);type ECOption = EChartsOption &ECBasicOption & {series?: TreeSeriesOption | TreeSeriesOption[];} & TooltipComponentOption &LegendComponentOption;interface TreeNode {name: string;id: string;value?: number; // Can be used for sizing or other metricsitemStyle?: {color?: string;borderColor?: string;borderWidth?: number;};label?: {show?: boolean;position?: any;formatter?: string | ((_params: any) => string);color?: string;fontSize?: number;overflow?: string;width?: number;ellipsis?: string;};lineStyle?: {color?: string;width?: number;curveness?: number;};children?: TreeNode[];// Custom propertiesnodeType:| "country"| "category"| "brand"| "product"| "regulation"| "customer"| "patent"| "contributor";isCollapsed?: boolean; // For manual collapse/expand if needed beyond ECharts defaultsymbol?: string; // e.g., 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none' or 'image://path'symbolSize?: number | number[];emphasis?: {focus?: string;scale?: boolean | number;itemStyle?: {color?: string;borderColor?: string;borderWidth?: number;};label?: {color?: string;fontSize?: number;};lineStyle?: {color?: string;width?: number;};};
}const props = defineProps<{chartData: TreeNode;backgroundColor?: string;
}>();const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;// --- Style Definitions (based on UI) ---
const COLORS = {country: "#FFD700", // Yellow for country (中心節點)categoryPurple: "#DA70D6", // Purple for main categories (一級節點)brandRed: "#FF6347", // Red for brands (二級節點)productBlue: "#87CEFA", // Light blue for products (二級節點)contributorGreen: "#32CD32", // Green for contributors (特殊二級節點)defaultNode: "#A9A9A9", // Default colorconnector: "#B0B0B0", // Connector line colortextPrimary: "#333333",textSecondary: "#FFFFFF",
};const SYMBOL_SIZES = {country: 80,category: 60,item: 45,contributor: 50,
};function getNodeStyle(node: TreeNode) {let color = COLORS.defaultNode;let symbolSize = SYMBOL_SIZES.item;let labelColor = COLORS.textPrimary;let borderColor = "rgba(0,0,0,0)";let borderWidth = 0;switch (node.nodeType) {case "country":color = COLORS.country;symbolSize = SYMBOL_SIZES.country;labelColor = COLORS.textPrimary; // Text color for country nodebreak;case "category":case "regulation": // Assuming regulation, customer, patent are like categoriescase "customer":case "patent":color = COLORS.categoryPurple;symbolSize = SYMBOL_SIZES.category;labelColor = COLORS.textSecondary; // White text for purple nodesbreak;case "brand":color = COLORS.brandRed;symbolSize = SYMBOL_SIZES.item;labelColor = COLORS.textSecondary;break;case "product":color = COLORS.productBlue;symbolSize = SYMBOL_SIZES.item;labelColor = COLORS.textPrimary; // Darker text for light blue nodesbreak;case "contributor":color = COLORS.contributorGreen;symbolSize = SYMBOL_SIZES.contributor;labelColor = COLORS.textSecondary;borderColor = "#FF4500"; // Special border for contributorborderWidth = 2;break;}return {itemStyle: { color, borderColor, borderWidth },symbolSize,label: {show: true,color: labelColor,fontSize:node.nodeType === "country"? 16: node.nodeType === "category"? 14: 12,formatter: (params: any) => {// Basic word wrapping for long namesconst name = params.name;const maxLength = 10; // Max characters per lineif (name.length > maxLength) {let result = "";for (let i = 0; i < name.length; i += maxLength) {result += name.substring(i, i + maxLength) + "\n";}return result.trim();}return name;},// position: ["center", "center"], // 修改此處為數組形式,避免類型沖突overflow: "break", // Allow breaking lineswidth: symbolSize * 0.8, // Label width related to symbol size},emphasis: {focus: "descendant", // Highlight node and its descendantsscale: 1.1,itemStyle: {borderColor: "#FFF",borderWidth: 2,},label: {fontSize:node.nodeType === "country"? 18: node.nodeType === "category"? 16: 14,},},};
}function processDataForEcharts(node: TreeNode): TreeNode {const style = getNodeStyle(node);const processedNode: TreeNode = {...node,...style, // Apply generated stylessymbol: "circle", // All nodes are circles as per UI};if (node.children && node.children.length > 0) {processedNode.children = node.children.map((child) =>processDataForEcharts(child));}return processedNode;
}const initChart = () => {if (chartRef.value) {chartInstance = echarts.init(chartRef.value);setChartOptions();}
};const setChartOptions = () => {if (!chartInstance || !props.chartData) return;const processedData: any = processDataForEcharts(props.chartData);const option: ECOption = {backgroundColor: props.backgroundColor || "transparent", // Or a light blue like in the UItooltip: {trigger: "item",triggerOn: "mousemove",formatter: (params: any) => {// params.data should be the original TreeNode data before ECharts processing if neededreturn `${params.data.nodeType}: ${params.name}`;},},series: [{type: "tree",data: [processedData],layout: "radial", // Key for radial layoutsymbol: "circle", // Default symbol// symbolSize: (value: any, params: any) => {//   // Dynamic symbol size based on nodeType or value if needed//   // This is now handled by getNodeStyle and processDataForEcharts//   return params.data.symbolSize || 50;// },roam: true, // Allows zoom and panlabel: {position: "inside" as any, // Default label positionverticalAlign: "middle",align: "center",fontSize: 12,},// symbolOffset: [50, "-50%"],edgeSymbol: ["none", "arrow"],edgeSymbolSize: [4, 10],lineStyle: {color: COLORS.connector,width: 1.5,curveness: 0, // Slight curve for aesthetics// 配置箭頭:起點無符號,終點用箭頭symbol: ["none", "arrow"], // 起始點和結束點的符號symbolSize: [10, 15], // 符號大小(箭頭尺寸)},initialTreeDepth: 2, // Expand to 2 levels initially (Country -> Category)expandAndCollapse: true,animationDurationUpdate: 750,emphasis: {// Default emphasis for all nodes, can be overridden per nodefocus: "descendant",scale: true,},// Adjust radial layout parametersradial: {// These might need tweaking to match the UI's spread// radius: ['5%', '80%'], // Inner and outer radius// nodeGap: 30, // Gap between nodes in the same level// layerGap: 150, // Gap between layers},top: "5%",left: "5%",bottom: "5%",right: "5%",},],};chartInstance.setOption(option, true); // true to not merge with previous options
};onMounted(() => {nextTick(() => {initChart();});window.addEventListener("resize", resizeChart);
});onBeforeUnmount(() => {window.removeEventListener("resize", resizeChart);chartInstance?.dispose();
});watch(() => props.chartData,() => {setChartOptions();},{ deep: true }
);watch(() => props.backgroundColor,() => {setChartOptions();}
);const resizeChart = () => {chartInstance?.resize();
};// Expose a resize method if needed by parent
defineExpose({resizeChart,
});
</script><style scoped>
.radial-tree-view-container {width: 100%;height: 100%;min-height: 500px; /* Ensure a minimum height for the chart */
}.chart-container {width: 100%;height: 100%;
}
</style>

mock數據

export interface TreeNode {name: string;id: string;value?: number;itemStyle?: {color?: string;borderColor?: string;borderWidth?: number;};label?: {show?: boolean;position?: string;formatter?: string | ((params: any) => string);color?: string;fontSize?: number;overflow?: "truncate" | "break" | "breakAll";width?: number;ellipsis?: string;};lineStyle?: {color?: string;width?: number;curveness?: number;};children?: TreeNode[];nodeType:| "country"| "category"| "brand"| "product"| "regulation"| "customer"| "patent"| "contributor";isCollapsed?: boolean;symbol?: string;symbolSize?: number | number[];emphasis?: {focus?: "ancestor" | "descendant" | "self" | "none";scale?: boolean | number;itemStyle?: {color?: string;borderColor?: string;borderWidth?: number;};label?: {color?: string;fontSize?: number;};lineStyle?: {color?: string;width?: number;};};
}export const mockRadialData: TreeNode = {name: "美國",id: "usa",nodeType: "country",children: [{name: "客戶",id: "customer_category",nodeType: "category",children: [{ name: "10000 客戶", id: "customer_1", nodeType: "customer" }, // Simplified, assuming this is a leaf{name: "消費者畫像",id: "consumer_profile",nodeType: "category", // Sub-categorychildren: [{ name: "年齡段", id: "age_group", nodeType: "product" }, // Using 'product' type for generic items for now{ name: "學歷層次", id: "education_level", nodeType: "product" },{name: "貢獻者: 劉先生",id: "contrib_liu",nodeType: "contributor",},],},],},{name: "法規",id: "regulation_category",nodeType: "category",children: [{ name: "10000 法規", id: "regulation_1", nodeType: "regulation" },],},{name: "品牌",id: "brand_main_category",nodeType: "category",children: [{ name: "10000 品牌", id: "brand_count_node", nodeType: "brand" }, // A summary node{name: "品牌A",id: "brand_a",nodeType: "brand",children: [{ name: "品牌形象", id: "brand_a_image", nodeType: "product" },{ name: "品牌故事", id: "brand_a_story", nodeType: "product" },{name: "貢獻者: 劉先生",id: "contrib_brand_a_liu",nodeType: "contributor",},],},{name: "品牌B",id: "brand_b",nodeType: "brand",children: [{ name: "品牌定位", id: "brand_b_position", nodeType: "product" },{ name: "目標群體", id: "brand_b_target", nodeType: "product" },],},],},{name: "專利",id: "patent_category",nodeType: "category",children: [{ name: "10000 專利", id: "patent_1", nodeType: "patent" }],},{name: "產品",id: "product_main_category",nodeType: "category",children: [{ name: "10000 產品", id: "product_count_node", nodeType: "product" },{name: "產品1 (USB-C)",id: "product_1",nodeType: "product",children: [{ name: "設計風格", id: "product_1_design", nodeType: "product" },{ name: "充電方式", id: "product_1_charge", nodeType: "product" },{ name: "外觀尺寸", id: "product_1_size", nodeType: "product" },{name: "貢獻者: 李小姐",id: "contrib_prod_1_li",nodeType: "contributor",},],},{name: "產品2",id: "product_2",nodeType: "product",children: [{ name: "材料", id: "product_2_material", nodeType: "product" },{ name: "重量", id: "product_2_weight", nodeType: "product" },],},],},{name: "價格", // In UI, this looks like a categoryid: "price_category",nodeType: "category",children: [{ name: "10000 價格區間", id: "price_range_1", nodeType: "product" }, // Using 'product' for generic item],},{name: "消費者", // In UI, this looks like a categoryid: "consumer_category_alt",nodeType: "category",children: [{ name: "10000 消費者", id: "consumer_1_alt", nodeType: "customer" },],},],
};

圖例顯示

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82763.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82763.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82763.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C# 日志管理功能代碼

一、功能概述 本應用通過 AsyncFileLogger 類提供了靈活的日志控制功能&#xff0c;可在運行時通過 UI 界面啟用或禁用日志記錄。日志系統具有以下特點&#xff1a; 可控制開關&#xff1a;通過按鈕隨時啟用或禁用日志&#xff0c;無需重啟應用異步寫入&#xff1a;日志記錄采…

CSS 性能優化

目錄 CSS 性能優化CSS 提高性能的方法1. 選擇器優化1.1 選擇器性能原則1.2 選擇器優化示例 2. 重排&#xff08;Reflow&#xff09;和重繪&#xff08;Repaint&#xff09;優化2.1 重排和重繪的概念2.2 觸發重排的操作2.3 觸發重繪的操作2.4 優化重排和重繪的方法 3. 資源優化3…

【JJ斗地主-注冊安全分析報告】

前言 由于網站注冊入口容易被黑客攻擊&#xff0c;存在如下安全問題&#xff1a; 暴力破解密碼&#xff0c;造成用戶信息泄露短信盜刷的安全問題&#xff0c;影響業務及導致用戶投訴帶來經濟損失&#xff0c;尤其是后付費客戶&#xff0c;風險巨大&#xff0c;造成虧損無底洞 …

SON.stringify()和JSON.parse()之間的轉換

1.JSON.stringify() 作用&#xff1a;將對象、數組轉換成字符串 const obj {code: "500",message: "出錯了", }; const jsonString JSON.stringify(obj); console.log(jsonString);//"{"code":"Mark Lee","message"…

MongoDB $type 操作符詳解

MongoDB $type 操作符詳解 引言 MongoDB 是一款流行的開源文檔型數據庫,它提供了豐富的查詢操作符來滿足不同的數據查詢需求。在 MongoDB 中,$type 操作符是一個非常有用的查詢操作符,它允許用戶根據文檔中字段的類型來查詢文檔。本文將詳細介紹 MongoDB 的 $type 操作符,…

RagFlow優化代碼解析(一)

引子 前文寫到RagFlow的環境搭建&推理測試&#xff0c;感興趣的童鞋可以移步&#xff08;RagFlow環境搭建&推理測試-CSDN博客&#xff09;。前文也寫過RagFLow參數配置&測試的文檔&#xff0c;詳見&#xff08;RagFlow環境搭建&推理測試-CSDN博客&#xff09;…

永磁同步電機控制算法--模糊PI轉速控制器

一、原理介紹 在常規的PID控制系統的基礎上提出了一種模糊PID以及矢量變換方法相結合的控制系統&#xff0c;經過仿真分析對比證明&#xff1a; 模糊PID控制系統能夠有效的提高永磁同步電機的轉速響應速度&#xff0c;降低轉矩脈動&#xff0c;增強了整體控制系統的抗干擾能力…

MySQL基本操作(續)

第3章&#xff1a;MySQL基本操作&#xff08;續&#xff09; 3.3 表操作 表是關系型數據庫中存儲數據的基本結構&#xff0c;由行和列組成。在MySQL中&#xff0c;表操作包括創建表、查看表結構、修改表和刪除表等。本節將詳細介紹這些操作。 3.3.1 創建表 在MySQL中&#…

探索未知驚喜,盲盒抽卡機小程序系統開發新啟航

在消費市場不斷追求新鮮感與驚喜體驗的當下&#xff0c;盲盒抽卡機以其獨特的魅力&#xff0c;迅速成為眾多消費者熱衷的娛樂與消費方式。我們緊跟這一潮流趨勢&#xff0c;專注于盲盒抽卡機小程序系統的開發&#xff0c;致力于為商家和用戶打造一個充滿趣味與驚喜的數字化平臺…

89.實現添加收藏的功能的后端實現

實現完查看收藏列表之后&#xff0c;實現的是添加收藏的功能 我的設想是&#xff1a;在對話界面中&#xff0c;如果用戶認為AI的回答非常好&#xff0c;可以通過點擊該回答對應的氣泡中的圖標&#xff0c;對該內容進行添加 所以后端實現為&#xff1a; service類中添加&…

OD 算法題 B卷【猴子吃桃】

文章目錄 猴子吃桃 猴子吃桃 猴子喜歡吃桃&#xff0c;桃園有N棵桃樹&#xff0c;第i棵桃樹上有Ni個桃&#xff0c;看守將在H(>N)小時后回來&#xff1b;猴子可以決定吃桃的速度K(個/小時)&#xff0c;每個小時他會選擇一棵桃樹&#xff0c;從中吃掉K個桃&#xff0c;如果這…

ubuntu 端口復用

需求描述&#xff1a;復用服務器的 80端口&#xff0c;同時處理 ssh 和 http 請求&#xff0c;也就是 ssh 連接和 http 訪問服務器的時候都可以指定 80 端口&#xff0c;然后服務器可以正確分發請求給 ssh 或者 http。 此時&#xff0c;ssh 監聽的端口為 22&#xff0c;而 htt…

Hive中ORC存儲格式的優化方法

優化Hive中的ORC(Optimized Row Columnar)存儲格式可顯著提升查詢性能、降低存儲成本。以下是詳細的優化方法,涵蓋參數配置、數據組織、寫入優化及監控調優等維度: 一、ORC核心參數優化 1. 存儲與壓縮參數 SET orc.block.size=268435456; -- 塊大小(默認256MB)…

Vim 設置搜索高亮底色

在 Vim 中&#xff0c;默認搜索命中會高亮顯示&#xff0c;方便用戶快速定位關鍵字。但有些用戶希望自定義搜索匹配的底色或前景色&#xff0c;以適應不同的配色方案或提高可讀性。本文將詳細介紹如何修改 Vim 的搜索高亮顏色。 一、Vim 搜索高亮機制 Vim 用內置的高亮組&…

【計算機網絡】非阻塞IO——poll實現多路轉接

&#x1f525;個人主頁&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收錄專欄&#x1f308;&#xff1a;計算機網絡 &#x1f339;往期回顧&#x1f339;&#xff1a;【計算機網絡】非阻塞IO——select實現多路轉接 &#x1f516;流水不爭&#xff0c;爭的是滔滔不息 一、…

vscode使用系列之快速生成html模板

一.歡迎來到我的酒館 vscode&#xff0c;yyds! 目錄 一.歡迎來到我的酒館二.vscode下載安裝1.關于vscode你需要知道2.開始下載安裝 三.vscode快速創建html模板 二.vscode下載安裝 1.關于vscode你需要知道 Q&#xff1a;為什么使用vscode? A&#xff1a;使用vscode寫…

【C/C++】不同防止頭文件重復包含的措施

文章目錄 #pragma once vs #ifndef 文件宏1 原理層面區別&#xff08;core&#xff09;2 關鍵區別與優缺點分析3 總結與最佳實踐 #pragma once vs #ifndef 文件宏 在 C/C 中&#xff0c;#pragma once 和傳統的文件宏守衛 (#ifndef HEADER_H #define HEADER_H ... #endif) 都用…

java-springboot文件上傳校驗之只允許上傳excel文件,且檢查不能是腳本或者有害文件或可行性文件

四重驗證機制&#xff1a; 文件擴展名檢查&#xff08;.xlsx/.xls&#xff09;MIME類型檢查文件魔數驗證&#xff08;真實文件類型&#xff09;可執行文件特征檢測 防御措施&#xff1a; 使用try-with-resources確保流關閉限制文件大小防止DoS攻擊使用Apache POI的FileMagic進…

不確定性分析在LEAP能源-環境系統建模中的整合與應用

本內容突出與實例結合&#xff0c;緊密結合國家能源統計制度及《省級溫室氣體排放編制指南》&#xff0c;深入淺出地介紹針對不同級別研究對象時如何根據數據結構、可獲取性、研究目的&#xff0c;構建合適的能源生產、轉換、消費、溫室氣體排放&#xff08;以碳排放為主&#…

高性能分布式消息隊列系統(四)

八、客戶端模塊的實現 客戶端實現的總體框架 在 RabbitMQ 中&#xff0c;應用層提供消息服務的核心實體是 信道&#xff08;Channel&#xff09;。 用戶想要與消息隊列服務器交互時&#xff0c;通常不會直接操作底層的 TCP 連接&#xff0c;而是通過信道來進行各種消息的發布…