|
@@ -1,401 +0,0 @@
|
|
|
-import { register } from "@antv/x6-react-shape";
|
|
|
-import { Graph, Node, EventArgs } from "@antv/x6";
|
|
|
-import { useEffect, useMemo, useState } from "react";
|
|
|
-import { Button, InputNumber, Popover } from "antd";
|
|
|
-import { Icon } from "umi";
|
|
|
-
|
|
|
-const component = ({ node, graph }: { node: Node; graph: Graph }) => {
|
|
|
- const { line, origin, summarySource, type } = node.getData();
|
|
|
- const [width, setWidth] = useState(node.size().width);
|
|
|
- const [height, setHeight] = useState(node.size().height);
|
|
|
- const [showSetting, setShowSetting] = useState(false);
|
|
|
-
|
|
|
- const handleResizeChange = () => {
|
|
|
- setWidth(node.size().width);
|
|
|
- setHeight(node.size().height);
|
|
|
- }
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- const handleSelect = (args: EventArgs["node:selected"]) => {
|
|
|
- setShowSetting(args.node.id === origin);
|
|
|
- };
|
|
|
- graph.on("node:selected", handleSelect);
|
|
|
- graph.on("blank:click", () => setShowSetting(false));
|
|
|
- node.on("change:size", handleResizeChange);
|
|
|
- return () => {
|
|
|
- graph.off("node:selected", handleSelect);
|
|
|
- graph.off("blank:click", () => setShowSetting(false));
|
|
|
- node.off("change:size", handleResizeChange);
|
|
|
- };
|
|
|
- }, []);
|
|
|
-
|
|
|
- const linePosition = useMemo((): "left" | "right" | "top" | "bottom" => {
|
|
|
- const originNode = graph.getCellById(origin);
|
|
|
- if (originNode && originNode.isNode()) {
|
|
|
- const x = originNode.position().x + originNode.size().width / 2;
|
|
|
- const y = originNode.position().y + originNode.size().height / 2;
|
|
|
- if (
|
|
|
- x < node.position().x
|
|
|
- && y > node.position().y
|
|
|
- && y < node.position().y + height
|
|
|
- ) {
|
|
|
- return "left";
|
|
|
- }
|
|
|
- if (
|
|
|
- y > node.position().y
|
|
|
- && x > node.position().x
|
|
|
- && x < node.position().x + width
|
|
|
- ) {
|
|
|
- return "bottom";
|
|
|
- }
|
|
|
- if (
|
|
|
- y < node.position().y
|
|
|
- && x > node.position().x
|
|
|
- && x < node.position().x + width
|
|
|
- ) {
|
|
|
- return "top";
|
|
|
- }
|
|
|
- }
|
|
|
- return "right";
|
|
|
- }, [origin, width, height]);
|
|
|
-
|
|
|
- const lineStyle = useMemo(() => {
|
|
|
- switch (linePosition) {
|
|
|
- case "right":
|
|
|
- return {
|
|
|
- right: "-40px",
|
|
|
- };
|
|
|
- case "left":
|
|
|
- return {
|
|
|
- left: "-40px",
|
|
|
- };
|
|
|
- case "top":
|
|
|
- return {
|
|
|
- top: "-40px",
|
|
|
- };
|
|
|
- default:
|
|
|
- return {
|
|
|
- bottom: "-40px",
|
|
|
- };
|
|
|
- }
|
|
|
- }, [linePosition]);
|
|
|
-
|
|
|
- const rightLine = useMemo(() => {
|
|
|
- if (type === 2) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 36 ${height / 2}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }
|
|
|
- if (type === 3) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- A 10 ${height / 2} 0 0 1 20 ${height - line.width}
|
|
|
- M 30 ${height / 2}
|
|
|
- L 40 ${height / 2}
|
|
|
- `;
|
|
|
- }
|
|
|
- if (type === 4) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- Q 30 ${line.width} 30 10
|
|
|
- L 30 ${height / 2 - 10}
|
|
|
- Q 30 ${height / 2} 40 ${height / 2}
|
|
|
- Q 30 ${height / 2} 30 ${height / 2 + 10}
|
|
|
- L 30 ${height - 10}
|
|
|
- Q 30 ${height - line.width} 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 30 ${line.width}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 40 ${height / 2}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 30 ${height - line.width}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }, [type, width, height]);
|
|
|
-
|
|
|
- const leftLine = useMemo(() => {
|
|
|
- if (type === 2) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 4 ${height / 2}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }
|
|
|
- if (type === 3) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- A 10 ${height / 2} 0 0 0 20 ${height - line.width}
|
|
|
- M 10 ${height / 2}
|
|
|
- L 0 ${height / 2}
|
|
|
- `;
|
|
|
- }
|
|
|
- if (type === 4) {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- Q 10 ${line.width} 10 10
|
|
|
- L 10 ${height / 2 - 10}
|
|
|
- Q 10 ${height / 2} 0 ${height / 2}
|
|
|
- Q 10 ${height / 2} 10 ${height / 2 + 10}
|
|
|
- L 10 ${height - 10}
|
|
|
- Q 10 ${height - line.width} 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 10 ${line.width}
|
|
|
- L 10 ${height / 2}
|
|
|
- L 00 ${height / 2}
|
|
|
- L 10 ${height / 2}
|
|
|
- L 10 ${height - line.width}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }, [type, width, height]);
|
|
|
-
|
|
|
- const topLine = useMemo(() => {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 30 ${line.width}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 40 ${height / 2}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 30 ${height - line.width}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }, [type, width, height]);
|
|
|
-
|
|
|
- const bottomLine = useMemo(() => {
|
|
|
- return `
|
|
|
- M 20 ${line.width}
|
|
|
- L 30 ${line.width}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 40 ${height / 2}
|
|
|
- L 30 ${height / 2}
|
|
|
- L 30 ${height - line.width}
|
|
|
- L 20 ${height - line.width}
|
|
|
- `;
|
|
|
- }, [type, width, height]);
|
|
|
-
|
|
|
- const path = useMemo(() => {
|
|
|
- switch(linePosition) {
|
|
|
- case 'bottom': return bottomLine;
|
|
|
- case 'top': return topLine;
|
|
|
- case 'left': return leftLine;
|
|
|
- case 'right': return rightLine;
|
|
|
- }
|
|
|
- }, [type, width, height]);
|
|
|
-
|
|
|
- // 删除概要
|
|
|
- const handleRemove = () => {
|
|
|
- const parentNode = graph.getCellById(summarySource);
|
|
|
- parentNode?.setData(
|
|
|
- {
|
|
|
- summary: undefined,
|
|
|
- },
|
|
|
- {
|
|
|
- deep: false,
|
|
|
- }
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- const handleChange = (key: string, value: any) => {
|
|
|
- node.setData({
|
|
|
- [key]: value,
|
|
|
- });
|
|
|
- const parentNode = graph.getCellById(summarySource);
|
|
|
- if (parentNode) {
|
|
|
- parentNode.setData({
|
|
|
- summary: {
|
|
|
- ...parentNode.data.summary,
|
|
|
- border: {
|
|
|
- ...parentNode.data.summary.border,
|
|
|
- [key]: value,
|
|
|
- },
|
|
|
- },
|
|
|
- });
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const colors = [
|
|
|
- "#bf1e1b",
|
|
|
- "#63abf7",
|
|
|
- "#71cb2d",
|
|
|
- "#ff9f1a",
|
|
|
- "#30bfbf",
|
|
|
- "#000",
|
|
|
- ];
|
|
|
-
|
|
|
- const ColorBtn = ({
|
|
|
- color,
|
|
|
- value,
|
|
|
- onClick,
|
|
|
- }: {
|
|
|
- color: string;
|
|
|
- value: string;
|
|
|
- onClick: () => void;
|
|
|
- }) => {
|
|
|
- return (
|
|
|
- <div
|
|
|
- className={`relative w-18px h-18px cursor-pointer rounded-4px flex items-center justify-center hover:opacity-80`}
|
|
|
- style={{
|
|
|
- background: color,
|
|
|
- border: !color ? "1px solid #000" : "",
|
|
|
- }}
|
|
|
- onClick={onClick}
|
|
|
- >
|
|
|
- {color === value && (
|
|
|
- <i className="iconfont icon-zhengque-1 color-#fff" />
|
|
|
- )}
|
|
|
- {!color && (
|
|
|
- <i className="absolute left-0 top-0 block w-1px h-24px bg-#de0f18 origin-top-left rotate--45" />
|
|
|
- )}
|
|
|
- </div>
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- <>
|
|
|
- <div className="relative text-0 w-full h-full">
|
|
|
- {showSetting && (
|
|
|
- <svg
|
|
|
- className="w-full h-full"
|
|
|
- viewBox={`0 0 ${width} ${height}`}
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
- >
|
|
|
- <path
|
|
|
- fill="none"
|
|
|
- d={`
|
|
|
- M ${0},${0}
|
|
|
- L ${width},${0}
|
|
|
- L ${width},${height}
|
|
|
- L ${0},${height} Z
|
|
|
- `}
|
|
|
- stroke={line.color}
|
|
|
- strokeWidth={2}
|
|
|
- />
|
|
|
- </svg>
|
|
|
- )}
|
|
|
-
|
|
|
- <svg
|
|
|
- className="absolute w-40px h-full"
|
|
|
- style={lineStyle}
|
|
|
- viewBox={`0 0 ${40} ${height}`}
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
- >
|
|
|
- <path
|
|
|
- d={path}
|
|
|
- fill="none"
|
|
|
- stroke={line.color}
|
|
|
- strokeWidth={line.width}
|
|
|
- />
|
|
|
- </svg>
|
|
|
-
|
|
|
- {showSetting && (
|
|
|
- <Popover
|
|
|
- trigger={["hover"]}
|
|
|
- content={
|
|
|
- <div className="w-240px">
|
|
|
- <div className="flex justify-between items-center m-b-20px">
|
|
|
- <span className="text-14px font-bold">概要设置</span>
|
|
|
- <span
|
|
|
- className="text-12px cursor-pointer color-#666 hover:color-red"
|
|
|
- onClick={handleRemove}
|
|
|
- >
|
|
|
- 删除概要
|
|
|
- <i className="iconfont icon-shanchu m-l-4px" />
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <div className="flex items-center gap-8px m-b-8px">
|
|
|
- <span className="text-12px color-#333">概要宽度</span>
|
|
|
- <InputNumber
|
|
|
- className="w-100px"
|
|
|
- size="small"
|
|
|
- min={1}
|
|
|
- max={4}
|
|
|
- precision={0}
|
|
|
- value={line.width}
|
|
|
- onChange={(val) =>
|
|
|
- handleChange("line", { ...line, width: val })
|
|
|
- }
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div className="flex items-center gap-8px m-b-8px">
|
|
|
- <span className="text-12px color-#333">概要颜色</span>
|
|
|
- {colors.map((color) => (
|
|
|
- <ColorBtn
|
|
|
- key={color}
|
|
|
- color={color}
|
|
|
- value={line.color}
|
|
|
- onClick={() => handleChange("line", { ...line, color })}
|
|
|
- />
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- <div className="flex items-center gap-8px m-b-8px">
|
|
|
- <span className="text-12px color-#333">概要样式</span>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<Icon icon="local:1" height="16px" />}
|
|
|
- className={type === 1 ? "active" : ""}
|
|
|
- onClick={() => handleChange("type", 1)}
|
|
|
- ></Button>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<Icon icon="local:2" height="16px" />}
|
|
|
- className={type === 1 ? "active" : ""}
|
|
|
- onClick={() => handleChange("type", 2)}
|
|
|
- ></Button>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<Icon icon="local:3" height="16px" />}
|
|
|
- className={type === 1 ? "active" : ""}
|
|
|
- onClick={() => handleChange("type", 3)}
|
|
|
- ></Button>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<Icon icon="local:4" height="16px" />}
|
|
|
- className={type === 1 ? "active" : ""}
|
|
|
- onClick={() => handleChange("type", 4)}
|
|
|
- ></Button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- }
|
|
|
- >
|
|
|
- <i
|
|
|
- className="z-9 iconfont icon-more text-12px absolute top-25% cursor-pointer"
|
|
|
- style={{
|
|
|
- color: line.color || "#000",
|
|
|
- [linePosition]: '-36px'
|
|
|
- }}
|
|
|
- />
|
|
|
- </Popover>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </>
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
-// 主题节点
|
|
|
-register({
|
|
|
- shape: "mind-map-summary-border",
|
|
|
- effect: ["data"],
|
|
|
- component: component,
|
|
|
-});
|
|
|
-
|
|
|
-export default {
|
|
|
- shape: "mind-map-summary-border",
|
|
|
- data: {
|
|
|
- line: {
|
|
|
- width: 2,
|
|
|
- color: "#63abf7",
|
|
|
- },
|
|
|
- type: 1,
|
|
|
- },
|
|
|
-};
|