Browse Source

feat: 添加风格设置 修改标题修改bug

liaojiaxing 6 months ago
parent
commit
77a51c77c3

+ 0 - 10
apps/designer/src/components/CustomTag.tsx

@@ -1,10 +0,0 @@
-import { Tag, TagProps } from 'antd'
-import React from 'react'
-
-export default function CustomTag(props: TagProps) {
-  return (
-    <div className='inline'>
-      <Tag {...props}/>
-    </div>
-  )
-}

+ 122 - 0
apps/designer/src/components/mindMap/CustomTag.tsx

@@ -0,0 +1,122 @@
+import { Tag, TagProps, Popover, Input, Button, ColorPicker } from "antd";
+import React, { useState } from "react";
+
+export default function CustomTag(
+  props: TagProps & {
+    hideAdd?: boolean;
+    onAdd?: () => void;
+    onDelete?: () => void;
+    onChangeTag: (tagInfo: { color: string; name: string }) => void;
+  }
+) {
+  const [title, setTitle] = useState(props.title);
+  const [open, setOpen] = useState(false);
+
+  const colors = [
+    "#e75e5b",
+    "#e78f49",
+    "#5ab7af",
+    "#52aaee",
+    "#6e4dbe",
+    "#d6f0f8",
+  ];
+
+  const ColorBtn = ({
+    color,
+    value,
+    onClick,
+  }: {
+    color?: string;
+    value: string;
+    onClick: () => void;
+  }) => {
+    return (
+      <div
+        className={`relative w-24px h-24px 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>
+    );
+  };
+
+  const handleChange = (key: string, value: string) => {
+    props?.onChangeTag?.({
+      name: props.title || '',
+      color: props.color || '',
+      [key]: value,
+    });
+  };
+
+  return (
+    <div className="inline">
+      <Popover
+        trigger={"click"}
+        open={open}
+        placement="bottom"
+        onOpenChange={() => setOpen(!open)}
+        content={
+          <div>
+            <Input
+              placeholder="标签名"
+              value={title}
+              onChange={(e) => {
+                setTitle(e.target.value);
+              }}
+              onBlur={() => {
+                handleChange("name", title || props.title || '空标签');
+              }}
+              onPressEnter={() => {
+                handleChange("name", title || props.title || '空标签');
+              }}
+            />
+
+            <div className="flex gap-8px my-16px">
+              {colors.map((color) => (
+                <ColorBtn
+                  key={color}
+                  color={color}
+                  value={props.color as string}
+                  onClick={() => handleChange("color", color)}
+                />
+              ))}
+              <ColorPicker format="hex" onChange={(value) => handleChange("color", value.toHexString())}>
+                <Button type="text" icon={<i className="iconfont icon-bi" style={{color: props.color}}/>} size="small"></Button>
+              </ColorPicker>
+            </div>
+            <div className="flex justify-between">
+              {!props.hideAdd && (
+                <span
+                  className="text-12px cursor-pointer color-#067bef"
+                  onClick={props?.onAdd}
+                >
+                  添加到我的标签
+                </span>
+              )}
+              <span
+                className="text-12px cursor-pointer color-#9aa5b8 hover:color-#eb5555"
+                onClick={() => {
+                  props?.onDelete?.();
+                  setOpen(false);
+                }}
+              >
+                删除此标签
+              </span>
+            </div>
+          </div>
+        }
+      >
+        <Tag {...props} onClick={() => setOpen(true)} />
+      </Popover>
+    </div>
+  );
+}

+ 80 - 0
apps/designer/src/components/mindMap/Text.tsx

@@ -0,0 +1,80 @@
+import React, { useMemo, useRef } from "react";
+import { Input, InputRef } from "antd";
+import { Node } from "@antv/x6";
+import { useSafeState } from "ahooks";
+export default function Text(props: {
+  value: string;
+  styles: React.CSSProperties & {
+    bold: boolean;
+    italic: boolean;
+  };
+  node: Node;
+  fixedWidth: boolean;
+  placeholder?: string;
+  txtStyle?: React.CSSProperties;
+  onChange?: (value: string) => void;
+}) {
+  const { value, styles, node, placeholder, txtStyle } = props;
+  const [isEditing, setIsEditing] = useSafeState(false);
+  const inputRef = useRef<InputRef>(null);
+
+  const style = useMemo(() => {
+    return {
+      ...styles,
+      fontWeight: styles.bold ? "bold" : undefined,
+      fontStyle: styles.italic ? "italic" : undefined,
+      minHeight: "12px",
+    };
+  }, [styles]);
+
+  const handleChange = (val: string) => {
+    node.setData({ label: val });
+    props.onChange?.(val);
+  };
+
+  const handleSetEditing = (edit: boolean) => {
+    if (node.data?.lock) {
+      return;
+    }
+    node.setData({
+      ignoreDrag: edit,
+    });
+    if (edit) {
+      setTimeout(() => {
+        inputRef.current?.focus({ cursor: "all" });
+      }, 100);
+    }
+    setIsEditing(edit);
+  };
+
+  // useEffect(() => {
+  //   // 处理字体加载
+  //   // @ts-ignore
+  //   // WebFont.load({
+  //   //   google: {
+  //   //     families: [styles.fontFamily]
+  //   //   }
+  //   // })
+  // }, [styles.fontFamily]);
+
+  return (
+    <div style={txtStyle}>
+      {isEditing ? (
+        <Input.TextArea
+          ref={inputRef}
+          placeholder={placeholder}
+          value={value}
+          variant="borderless"
+          style={style}
+          onChange={(e) => handleChange(e.target.value)}
+          onBlur={() => handleSetEditing(false)}
+          autoSize
+        />
+      ) : (
+        <div style={style} onDoubleClick={() => handleSetEditing(true)}>
+          {value}
+        </div>
+      )}
+    </div>
+  );
+}

+ 59 - 46
apps/designer/src/components/mindMap/Topic.tsx

@@ -2,7 +2,6 @@ import { register } from "@antv/x6-react-shape";
 import { EventArgs, Graph, Node } from "@antv/x6";
 import { topicData } from "@/config/data";
 import { useSizeHook, useShapeProps } from "@/hooks";
-import CustomInput from "../CustomInput";
 import { useEffect, useMemo, useRef, useState } from "react";
 import { PlusOutlined } from "@ant-design/icons";
 import { TopicType } from "@/enum";
@@ -10,11 +9,12 @@ import { addTopic } from "@/pages/mindmap/mindMap";
 
 import Link from "./Link";
 import ExtraModule from "./ExtraModule";
-import CustomTag from "@/components/CustomTag";
+import CustomTag from "@/components/mindMap/CustomTag";
 import { TopicItem } from "@/types";
 import { selectTopic } from "@/utils/mindmapHander";
 import { Tooltip, Popover } from "antd";
 import LinkForm from "./LinkForm";
+import Text from "./Text";
 const component = ({ node, graph }: { node: Node; graph: Graph }) => {
   const {
     fill,
@@ -36,8 +36,11 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     linkTopicId,
   } = node.getData();
   const { size, ref } = useSizeHook();
-  const { fillContent, strokeColor, strokeWidth } =
-    useShapeProps(fill, size, stroke);
+  const { fillContent, strokeColor, strokeWidth } = useShapeProps(
+    fill,
+    size,
+    stroke
+  );
   const [selected, setSelected] = useState(false);
   const [showPopover, setShowPopover] = useState(false);
   const [popoverContent, setPopoverContent] = useState<React.ReactNode>();
@@ -96,37 +99,29 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
     // graph.select(node);
     graph.on("node:selected", handleSelect);
     graph.on("node:unselected", handleSelect);
-    graph.on("node:resized", handleResize);
     return () => {
       graph.off("node:selected", handleSelect);
       graph.off("node:unselected", handleSelect);
-      graph.off("node:resized", handleResize);
-    };
-  }, []);
-
-  useEffect(() => {
-    let timer = setInterval(() => {
-      const { clientHeight = 0, clientWidth = 0 } = ref.current || {};
-      if (clientHeight && (size.width !== clientWidth || size.height !== clientHeight)) {
-        node.setData({
-          width: clientWidth,
-          height: clientHeight,
-        });
-      }
-    }, 1000);
-
-    return () => {
-      clearInterval(timer);
     };
   }, []);
 
-  const handleResize = () => {
-    node.setData({
-      fixedWidth: true,
-      width: node.size().width,
-    });
+  const changeSize = () => {
+    const { clientHeight = 0, clientWidth = 0 } = ref.current || {};
+    if (
+      clientHeight &&
+      (size.width !== clientWidth || size.height !== clientHeight)
+    ) {
+      node.setData({
+        width: clientWidth,
+        height: clientHeight,
+      });
+    }
   };
 
+  useEffect(() => {
+    changeSize();
+  }, [node.data]);
+
   const childrenCount = useMemo(() => {
     let count = 0;
     const traverse = (topics: TopicItem[]) => {
@@ -176,9 +171,34 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
       }
     );
   };
-  
+
+  const handleChangeTag = (tagInfo: { color?: string; title?: string }, index: number) => {
+    node.setData({
+      tags: tags.map((item: { color?: string; title?: string }, i: number) => {
+        if (index === i) {
+          return {
+            ...item,
+            ...tagInfo
+          }
+        }
+        return item;
+      }, {
+        deep: false
+      })
+    })
+  }
+
+  const handleDeleteTag = (index: number) => {
+    tags.splice(index, 1);
+    node.setData({
+      tags
+    }, {
+      deep: false
+    });
+  }
+
   return (
-    <>
+    <div>
       {selected && (
         <div
           style={{
@@ -196,9 +216,7 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
           className="content relative text-0"
           ref={ref}
           style={{
-            // minWidth: "100%",
-            // width: "fit-content",
-            // minHeight: "100%",
+            width: fixedWidth ? "100%" : "fit-content",
             opacity: opacity / 100,
             border: `solid ${strokeWidth}px ${strokeColor}`,
             background: fillContent,
@@ -216,11 +234,8 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
           )}
 
           {/* 图标、标题、链接等 */}
-          <div
-            className="flex-1 flex items-center justify-center"
-            ref={titleRef}
-          >
-            <div className="flex items-center text-20px">
+          <div className="tit flex items-center justify-center" ref={titleRef}>
+            <div className="flex items-center text-20px flex-1">
               {icons?.map((icon: string) => {
                 return (
                   <svg key={icon} className="icon mr-6px" aria-hidden="true">
@@ -229,22 +244,18 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
                 );
               })}
             </div>
-            <CustomInput
+            <Text
               value={label}
               node={node}
+              fixedWidth={fixedWidth}
               styles={{
                 ...text,
-                position: "relative",
-                padding: "0",
               }}
               txtStyle={{
                 position: "relative",
-                flexShrink: 0,
-                transform: "translateY(50%)",
-                width: "auto",
                 ...(fixedWidth
                   ? { maxWidth: `calc(100% - ${2 * padding.x}px)` }
-                  : {}),
+                  : { width: "max-content" }),
               }}
             />
             <div
@@ -278,13 +289,15 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
           </div>
           {/* 标签 */}
           <div className="flex items-center justify-center" ref={tagRef}>
-            {tags?.map((item: { name: string; color: string }) => {
+            {tags?.map((item: { name: string; color: string }, index: number) => {
               return (
                 <CustomTag
                   className="text-14px"
                   key={item.name}
                   title={item.name}
                   color={item.color}
+                  onChangeTag={(tag) => handleChangeTag(tag, index)}
+                  onDelete={() => handleDeleteTag(index)}
                 >
                   {item.name}
                 </CustomTag>
@@ -352,7 +365,7 @@ const component = ({ node, graph }: { node: Node; graph: Graph }) => {
           )}
         </div>
       </Popover>
-    </>
+    </div>
   );
 };
 

+ 65 - 74
apps/designer/src/events/mindMapEvent.ts

@@ -188,85 +188,79 @@ export const bindMindMapEvents = (
   /**
    * 节点数据更改
    */
-  graph.on("node:change:*", (args) => {
+  graph.on("node:change:data", (args) => {
     const { current, previous } = args;
-    console.log(args.key, args)
-    if (args.key === "data") {
-      // 收折子项 setMindProjectInfo更新会重新渲染
-      if (current.collapsed !== previous.collapsed) {
-        setMindProjectInfo &&
-          updateTopic(
-            args.cell.id,
-            { collapsed: current.collapsed },
-            setMindProjectInfo
-          );
-        return;
-      }
-      if (current?.links && current.links.length !== previous?.links?.length) {
-        setMindProjectInfo &&
-          updateTopic(
-            args.cell.id,
-            { links: current.links },
-            setMindProjectInfo
-          );
-      }
-      if (current?.border !== previous?.border) {
-        setMindProjectInfo &&
-          updateTopic(
-            args.cell.id,
-            { border: current.border },
-            setMindProjectInfo
-          );
-      }
-      console.log(args)
-      if (current?.summary !== previous?.summary) {
-        setMindProjectInfo &&
-          updateTopic(
-            args.cell.id,
-            { summary: current.summary },
-            setMindProjectInfo
-          );
-      }
-      if(current?.extraModules !== previous?.extraModules) {
-        setMindProjectInfo &&
-          updateTopic(
-            args.cell.id,
-            { extraModules: current.extraModules },
-            setMindProjectInfo
-          );
-      }
-      // 本地缓存更新不会重新渲染
-      if(args.cell.id.includes('-border')) {
-        updateTopic(args.current.origin, {border: current}, (info) => {
-          localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
-        });
-      } else {
-         updateTopic(args.cell.id, current, (info) => {
-          localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
-        });
-      }
+    console.log("修改数据:", args);
+    // 收折子项 setMindProjectInfo更新会重新渲染
+    if (current.collapsed !== previous.collapsed) {
+      setMindProjectInfo &&
+        updateTopic(
+          args.cell.id,
+          { collapsed: current.collapsed },
+          setMindProjectInfo
+        );
+      return;
+    }
+    if (current?.links && current.links.length !== previous?.links?.length) {
+      setMindProjectInfo &&
+        updateTopic(args.cell.id, { links: current.links }, setMindProjectInfo);
+    }
+    if (current?.border !== previous?.border) {
+      setMindProjectInfo &&
+        updateTopic(
+          args.cell.id,
+          { border: current.border },
+          setMindProjectInfo
+        );
     }
-    if (args.key === "position") {
-      if (args.cell.isNode() && !args.cell.data.parentId) {
+    if (current?.summary !== previous?.summary) {
+      setMindProjectInfo &&
         updateTopic(
           args.cell.id,
-          { ...args.cell.data, x: current.x, y: current.y },
-          (info) => {
-            // localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
-            setMindProjectInfo && setMindProjectInfo(info)
-          }
+          { summary: current.summary },
+          setMindProjectInfo
+        );
+    }
+    if (current?.extraModules !== previous?.extraModules) {
+      setMindProjectInfo &&
+        updateTopic(
+          args.cell.id,
+          { extraModules: current.extraModules },
+          setMindProjectInfo
         );
-      }
     }
-    if (args.key === "size") {
+    // 本地缓存更新不会重新渲染
+    if (args.cell.id.includes("-border")) {
+      updateTopic(args.current.origin, { border: current }, (info) => {
+        localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
+      });
+    } else {
+      updateTopic(args.cell.id, current, (info) => {
+        localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
+      });
+    }
+  });
+
+  graph.on("node:resized", (args) => {
+    console.log('修改完成:', args)
+    args.node.setData({
+      fixedWidth: true,
+      width: args.node.size().width,
+      height: args.node.size().height,
+    })
+  });
+
+  graph.on("node:change:position", (args) => {
+    const { current } = args;
+    console.log('修改位置:')
+
+    if (args.cell.isNode() && !args.cell.data.parentId && args.cell.data.type !== TopicType.main) {
       updateTopic(
         args.cell.id,
-        {
-          width: current.width,
-          height: current.height,
-        },
+        { ...args.cell.data, x: current?.x, y: current?.y },
         (info) => {
-          localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
+          // localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
+          setMindProjectInfo && setMindProjectInfo(info);
         }
       );
     }
@@ -603,10 +597,7 @@ const handleSwitchPosition = (
   // 后代节点样式
   if (source.children)
     source.children = traverseNode(source.children, (topic) => {
-      const theme = getTheme(
-        mindmapProjectInfo.theme,
-        source
-      );
+      const theme = getTheme(mindmapProjectInfo.theme, source);
       topic.type = TopicType.sub;
       topic.fill = {
         ...topic.fill,

+ 0 - 34
apps/designer/src/pages/mindmap/components/Config/NodeStyle.tsx

@@ -31,7 +31,6 @@ export default function GraphStyle() {
       fontSize: 14,
       lineHeight: 1.25,
       textAlign: "center",
-      textVAlign: "middle",
       bold: false,
       italic: false,
       textDecoration: "none",
@@ -347,39 +346,6 @@ export default function GraphStyle() {
               />
             </Tooltip>
           </div>
-          <div className="bg-white rounded-s flex justify-between items-center">
-            <Tooltip title="顶部对齐">
-              <Button
-                type="text"
-                icon={<VerticalAlignTopOutlined />}
-                disabled={!selectedCell?.length}
-                className={formModel.text.textVAlign === "top" ? "active" : ""}
-                onClick={() => handleSetFormModel("text.textVAlign", "top")}
-              />
-            </Tooltip>
-            <Tooltip title="垂直居中">
-              <Button
-                type="text"
-                icon={<VerticalAlignMiddleOutlined />}
-                className={
-                  formModel.text.textVAlign === "middle" ? "active" : ""
-                }
-                disabled={!selectedCell?.length}
-                onClick={() => handleSetFormModel("text.textVAlign", "middle")}
-              />
-            </Tooltip>
-            <Tooltip title="底部对齐">
-              <Button
-                type="text"
-                icon={<VerticalAlignBottomOutlined />}
-                className={
-                  formModel.text.textVAlign === "bottom" ? "active" : ""
-                }
-                disabled={!selectedCell?.length}
-                onClick={() => handleSetFormModel("text.textVAlign", "bottom")}
-              />
-            </Tooltip>
-          </div>
         </div>
       </section>
       <Divider className="my-8px" />

+ 70 - 7
apps/designer/src/pages/mindmap/components/Config/TagConfig.tsx

@@ -3,12 +3,13 @@ import { useLocalStorageState } from "ahooks";
 import { Tabs, Input } from "antd";
 import React, { useEffect, useState } from "react";
 import { useModel } from "umi";
-import CustomTag from "@/components/CustomTag";
+import CustomTag from "@/components/mindMap/CustomTag";
 
 interface TagItem {
   name: string;
   color: string;
 }
+// 我的标签 存服务器
 const defaultTags: TagItem[] = [
   { name: "待定", color: "#6e4dbe" },
   { name: "已确认", color: "rgb(80, 194, 139)" },
@@ -40,7 +41,7 @@ export default function TagConfig() {
   }, [selectedCell]);
   const handleAddTag = (tag: TagItem, ignore = false) => {
     if (!tag) return;
-    if(currentTags.find(item => item.name === tag.name)) return;
+    if (currentTags.find((item) => item.name === tag.name)) return;
 
     selectedCell.forEach((cell) => {
       if (cell.isNode()) {
@@ -50,7 +51,7 @@ export default function TagConfig() {
         });
       }
     });
-    
+
     setCurrentTags((state) => [...state, tag]);
     setNewTag((state) => ({ ...state, name: "" }));
     if (!recentTags?.find((item) => item.name === tag.name) && !ignore) {
@@ -58,17 +59,73 @@ export default function TagConfig() {
     }
   };
 
+  const handleCurrentChange = (tag: TagItem, index: number) => {
+    const firstNode = selectedCell.find((item) => item.isNode());
+    console.log(tag)
+    const tags = (firstNode?.data?.tags || []).map((item: TagItem, i: number) => {
+      if (index === i) {
+        return {
+          ...item,
+          ...tag
+        };
+      }
+      return item
+    });
+    setCurrentTags(tags);
+    firstNode?.setData({
+      tags,
+    }, {
+      deep: false
+    })
+  };
+
+  const handleCurrentDelete = (index: number) => {
+    const firstNode = selectedCell.find((item) => item.isNode());
+    const tags =  firstNode?.data?.tags?.filter((item: TagItem, i: number) => index !== i);
+    setCurrentTags(tags);
+    firstNode?.setData({
+      tags
+    }, {
+      deep: false
+    })
+  }
+
+  const handleRecentChange = (tag: TagItem, index: number) => {
+    setRecentTags(recentTags?.map((item: TagItem, i: number) => {
+      if (i === index) {
+        return {
+          ...item,
+          ...tag
+        };
+      } else {
+        return item;
+      }
+    }))
+  };
+
+  const handleRecentDelete = (index: number) => {
+    setRecentTags(recentTags?.filter((item: TagItem, i: number) => i !== index))
+  }
+
   return (
     <Tabs>
       <Tabs.TabPane key="1" tab="标签">
-        <div className={`px-8px ${!selectedCell.length ? "opacity-50 pointer-events-none" : ""}`}>
+        <div
+          className={`px-8px ${!selectedCell.length ? "opacity-50 pointer-events-none" : ""}`}
+        >
           <div className="text-14px color-#000 font-bold my-8px mt-0">
             当前标签
           </div>
           <div className="flex flex-wrap gap-4px">
             {currentTags.length ? (
-              currentTags.map((item) => (
-                <CustomTag key={item.name} color={item.color}>
+              currentTags.map((item, index) => (
+                <CustomTag
+                  key={item.name}
+                  title={item.name}
+                  color={item.color}
+                  onChangeTag={(tag) => handleCurrentChange(tag, index)}
+                  onDelete={() => handleCurrentDelete(index)}
+                >
                   {item.name}
                 </CustomTag>
               ))
@@ -101,11 +158,14 @@ export default function TagConfig() {
           <div className="text-12px color-#999 my-8px mt-16px">最近标签</div>
           <div className="flex flex-wrap gap-4px">
             {recentTags?.length ? (
-              recentTags.map((item) => (
+              recentTags.map((item, index) => (
                 <CustomTag
                   key={item.name}
                   color={item.color}
+                  title={item.name}
                   onClick={() => handleAddTag(item)}
+                  onChangeTag={(tag) => handleRecentChange(tag, index)}
+                  onDelete={() => handleRecentDelete(index)}
                 >
                   {item.name}
                 </CustomTag>
@@ -119,9 +179,12 @@ export default function TagConfig() {
           <div className="flex flex-wrap gap-4px">
             {defaultTags.map((item) => (
               <CustomTag
+                hideAdd
                 key={item.name}
                 color={item.color}
+                title={item.name}
                 onClick={() => handleAddTag(item, true)}
+                onChangeTag={() => {}}
               >
                 {item.name}
               </CustomTag>

+ 14 - 3
apps/designer/src/pages/mindmap/components/HeaderToolbar/index.tsx

@@ -1,4 +1,4 @@
-import React, { useMemo } from "react";
+import React, { useMemo, useState } from "react";
 import { Button, Input, Dropdown, Tooltip, MenuProps, Divider } from "antd";
 import { LeftOutlined, MenuOutlined } from "@ant-design/icons";
 import logo from "@/assets/logo.png";
@@ -26,6 +26,15 @@ export default function index() {
     return selectedCell.filter(cell => cell.isNode());
   }, [selectedCell]);
 
+  const [title, setTitle] = useState<string>(mindProjectInfo?.name || "");
+
+  const handleChangeTitle = () => {
+    mindProjectInfo && setMindProjectInfo({
+      ...mindProjectInfo,
+      name: title,
+    })
+  }
+
   // 创建新作品 todo
   const createNew = (type: string) => {};
   // 预览 todo
@@ -195,10 +204,12 @@ export default function index() {
           <Input
             className="text-16px max-w-100px"
             variant="borderless"
-            value={mindProjectInfo.name}
+            value={title}
             onChange={(e) =>
-              setMindProjectInfo({ ...mindProjectInfo, name: e.target.value })
+              setTitle(e.target.value)
             }
+            onBlur={handleChangeTitle}
+            onPressEnter={handleChangeTitle}
           />
         )}
       </div>