123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- import { StructureType, TopicType } from "@/enum";
- import { MindMapProjectInfo, TopicItem, HierarchyResult } from "@/types";
- import { Graph, Cell, Node } from "@antv/x6";
- import TopicComponent from "@/components/mindMap/Topic";
- import TopicBorder from "@/components/mindMap/Border";
- import SummaryBorder from "@/components/mindMap/SummaryBorder";
- import { topicData } from "@/config/data";
- import { uuid } from "@/utils";
- import { hierarchyMethodMap } from "@/pages/mindmap/hierarchy";
- import { createEdge } from "./edge";
- import { getTheme } from "./theme";
- import { topicMenu } from "@/utils/contentMenu";
- import {
- cacluculateExtremeValue,
- getBorderPositionAndSize,
- } from "@/utils/mindmapHander";
- /**
- * 渲染思维导图项目
- * @param graph
- */
- export const renderMindMap = ({
- topics,
- pageSetting,
- structure,
- theme,
- graph,
- setMindProjectInfo,
- returnCells = false
- }: {
- topics: TopicItem[];
- pageSetting: MindMapProjectInfo["pageSetting"];
- structure: StructureType;
- theme: string;
- graph: Graph;
- setMindProjectInfo: (info: MindMapProjectInfo) => void;
- returnCells?: boolean;
- }) => {
- const cells: Cell[] = [];
- topics.forEach((topic) => {
- // 遍历出层次结构
- const result: HierarchyResult = hierarchyMethodMap[structure]?.(
- topic,
- pageSetting
- );
- let originPosition = { x: topic?.x ?? -10, y: topic?.y ?? -10 };
- if (graph.hasCell(topic.id)) {
- const node = graph.getCellById(topic.id);
- if (node.isNode()) {
- originPosition = node.position();
- }
- }
- const offsetX = originPosition.x - result.x;
- const offsetY = originPosition.y - result.y;
- const traverse = (hierarchyItem: HierarchyResult, parent?: Node) => {
- if (hierarchyItem) {
- const { data, children, x, y } = hierarchyItem;
- const id = data?.id || uuid();
- // 创建主题
- const node = graph.createNode({
- ...TopicComponent,
- width: data.width,
- height: data.height,
- data: {
- ...data,
- opacity: 100,
- // 节点内部执行数据更新方法
- setMindProjectInfo,
- },
- id,
- x: offsetX + x,
- y: offsetY + y,
- tools: [
- {
- name: "contextmenu",
- args: {
- menu: topicMenu,
- },
- },
- ],
- });
- // 渲染边框
- if (data.border) {
- cells.push(
- createBorderComponent(hierarchyItem, offsetX, offsetY, graph)
- );
- }
- // 渲染概要
- if (data.summary) {
- const summaryCells = createSummaryCells(
- hierarchyItem,
- data.summary,
- structure,
- pageSetting,
- theme,
- graph,
- setMindProjectInfo,
- offsetX,
- offsetY
- );
- cells.push(...(summaryCells || []));
- }
- cells.push(node);
- parent && parent.addChild(node);
- if (data?.links) {
- cells.push(...data.links.map((item) => graph.createEdge(item)));
- }
- if (children) {
- children.forEach((item: HierarchyResult, index) => {
- const isBracket = [
- StructureType.leftBracket,
- StructureType.rightBracket,
- ].includes(structure);
- // 括号图不绘制中间连线
- if (!isBracket || index === 0 || index === children.length - 1) {
- const edge = createEdge(graph, id, item, structure, theme, {
- onlyOneChild: children.length === 1,
- });
- cells.push(edge);
- node.addChild(edge);
- }
- // 递归遍历
- traverse(item, node);
- });
- }
- }
- };
- traverse(result);
- });
- if(returnCells) return cells;
- const oldCells = graph.getCells();
- // 移除不要的节点及对应的边
- oldCells.forEach((cell) => {
- if (!cells.find((item) => cell.id === item.id)) {
- graph.removeCell(cell.id + "-edge");
- graph.removeCell(cell);
- }
- });
- // 添加或删除节点
- cells
- .filter((cell) => cell.isNode() && !graph.hasCell(cell.id))
- .forEach((cell) => {
- graph.addCell(cell);
- });
- // 更新老的节点
- cells
- .filter((cell) => cell.isNode() && graph.hasCell(cell.id))
- .forEach((cell) => {
- cell.isNode() && updateNode(cell, graph);
- });
- // 添加所需的节点
- const edgeCells = cells.filter((cell) => cell.isEdge());
- graph.removeCells(edgeCells);
- graph.addCell(edgeCells);
- };
- // 渲染概要
- const createSummaryCells = (
- hierarchyItem: HierarchyResult,
- summary: TopicItem['summary'],
- structure: StructureType,
- pageSetting: MindMapProjectInfo['pageSetting'],
- theme: string,
- graph: Graph,
- setMindProjectInfo: (info: MindMapProjectInfo) => void,
- offsetX: number,
- offsetY: number
- ): Cell[] => {
- let cells: Cell[] = [];
- if (summary) {
- const positionAndSize = cacluculateExtremeValue(
- hierarchyItem,
- hierarchyItem.children
- );
- const totalHeight = positionAndSize.maxY - positionAndSize.minY;
- const totalWidth = positionAndSize.maxX - positionAndSize.minX;
- // 概要边框
- const node = graph.createNode({
- ...SummaryBorder,
- data: summary.border,
- id: summary.topic.id + "-border",
- zIndex: 0,
- position: {
- x: offsetX + positionAndSize.minX - 2,
- y: offsetY + positionAndSize.minY - 2,
- },
- size: {
- width: totalWidth + 4,
- height: totalHeight + 4,
- },
- });
- cells.push(node);
- // 概要节点
- cells.push(...renderMindMap({
- topics: [{
- ...summary.topic,
- x: offsetX + hierarchyItem.x + totalWidth + 40,
- y: offsetY + hierarchyItem.y
- }],
- pageSetting,
- structure,
- theme,
- graph,
- setMindProjectInfo,
- returnCells: true
- }) || []);
- }
- return cells;
- }
- // 创建外框组件
- const createBorderComponent = (
- hierarchyItem: HierarchyResult,
- offsetX: number,
- offsetY: number,
- graph: Graph
- ) => {
- const positionAndSize = getBorderPositionAndSize(hierarchyItem);
- return graph.createNode({
- ...TopicBorder,
- id: hierarchyItem.id + "-border",
- data: {
- ...hierarchyItem.data.border,
- origin: hierarchyItem.id,
- },
- zIndex: 0,
- position: {
- x: offsetX + positionAndSize.x,
- y: offsetY + positionAndSize.y,
- },
- size: {
- width: positionAndSize.width,
- height: positionAndSize.height,
- },
- });
- };
- const updateNode = (node: Node, graph: Graph) => {
- const oldCell = graph.getCellById(node.id);
- if (oldCell.isNode()) {
- oldCell.setData(node.data);
- oldCell.position(node.position().x, node.position().y);
- oldCell.setSize(node.size().width, node.size().height);
- // oldCell.setAttrs(node.attrs);
- // const cells = node.children?.map(item => graph.getCellById(item.id));
- // oldCell.setChildren(cells ?? null);
- }
- };
- /**
- * 添加分支主题
- */
- export const addTopic = (
- type: TopicType,
- setMindProjectInfo: (info: MindMapProjectInfo) => void,
- node?: Node,
- otherData: Record<string, any> = {}
- ): TopicItem | undefined => {
- const projectInfo = getMindMapProjectByLocal();
- if (!projectInfo || !setMindProjectInfo) return;
- const topic = buildTopic(
- type,
- {
- ...(otherData || {}),
- parentId: node?.id,
- isSummary: node?.data?.isSummary,
- summarySource: node?.data?.summarySource
- },
- node
- );
- if (node) {
- const parentId = node.id;
- const traverse = (topics: TopicItem[]) => {
- topics.forEach((item) => {
- if (item.id === parentId) {
- if (item.children) {
- item.children?.push(topic);
- } else {
- item.children = [topic];
- }
- }
- if (item.children) {
- traverse(item.children);
- }
- if (item.summary) {
- traverse([item.summary.topic])
- }
- });
- };
- traverse(projectInfo?.topics || []);
- } else {
- projectInfo.topics.push(topic);
- }
- setMindProjectInfo(projectInfo);
- return topic;
- };
- const topicMap = {
- [TopicType.main]: {
- label: "中心主题",
- width: 206,
- height: 70,
- },
- [TopicType.branch]: {
- label: "分支主题",
- width: 104,
- height: 40,
- },
- [TopicType.sub]: {
- label: "子主题",
- width: 76,
- height: 27,
- },
- };
- /**
- * 构建一个主题数据
- * @param type 主题类型
- * @param options 配置项
- * @returns
- */
- export const buildTopic = (
- type: TopicType,
- options: Record<string, any> = {},
- parentNode?: Node
- ): TopicItem => {
- const projectInfo = getMindMapProjectByLocal();
- const theme = getTheme(
- projectInfo?.theme,
- type === TopicType.sub ? parentNode : undefined
- );
- const id = uuid();
- return {
- ...topicData,
- id,
- type,
- label: topicMap[type].label || "自由主题",
- width: topicMap[type].width || 206,
- height: topicMap[type].height || 70,
- fill: {
- ...topicData.fill,
- ...theme[type]?.fill,
- },
- text: {
- ...topicData.text,
- ...theme[type]?.text,
- },
- stroke: {
- ...topicData.stroke,
- ...theme[type]?.stroke,
- },
- edge: {
- ...topicData.edge,
- color: theme[type]?.edge.color,
- },
- ...options,
- children: (options?.children || topicData.children || []).map(
- (item: TopicItem) => {
- return {
- ...item,
- parentId: id,
- };
- }
- ),
- };
- };
- /**
- * 从本地获取项目信息
- * @returns
- */
- export const getMindMapProjectByLocal = (): MindMapProjectInfo | null => {
- return JSON.parse(localStorage.getItem("minMapProjectInfo") || "null");
- };
- /**
- * 更新主题数据
- * @param id 主题id
- * @param value 更新的数据
- * @param setMindProjectInfo 更新项目信息方法
- */
- export const updateTopic = (
- id: string,
- value: Partial<TopicItem>,
- setMindProjectInfo: (info: MindMapProjectInfo) => void
- ) => {
- const projectInfo = getMindMapProjectByLocal();
- if (!projectInfo || !setMindProjectInfo) return;
- const traverse = (topics: TopicItem[]) => {
- topics.forEach((item) => {
- if (item.id === id) {
- Object.assign(item, value);
- }
- if (item.children) {
- traverse(item.children);
- }
- });
- };
- traverse(projectInfo?.topics || []);
- setMindProjectInfo(projectInfo);
- };
|