diff --git a/packages/client/src/tiptap/core/wrappers/excalidraw/index.module.scss b/packages/client/src/tiptap/core/wrappers/excalidraw/index.module.scss index f9bdb17..e7293ea 100644 --- a/packages/client/src/tiptap/core/wrappers/excalidraw/index.module.scss +++ b/packages/client/src/tiptap/core/wrappers/excalidraw/index.module.scss @@ -30,4 +30,16 @@ align-items: center; } } + + .handlerWrap { + position: absolute; + right: 10px; + bottom: 10px; + z-index: 2; + padding: 2px 4px; + background-color: var(--semi-color-bg-2); + border: 1px solid var(--node-border-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + } } diff --git a/packages/client/src/tiptap/core/wrappers/excalidraw/index.tsx b/packages/client/src/tiptap/core/wrappers/excalidraw/index.tsx index 83bffeb..c8c66d1 100644 --- a/packages/client/src/tiptap/core/wrappers/excalidraw/index.tsx +++ b/packages/client/src/tiptap/core/wrappers/excalidraw/index.tsx @@ -2,15 +2,17 @@ import React from 'react'; import { useCallback, useEffect, useRef, useState } from 'react'; import VisibilitySensor from 'react-visibility-sensor'; -import { Space, Spin, Typography } from '@douyinfe/semi-ui'; +import { Button, Space, Spin, Typography } from '@douyinfe/semi-ui'; import { NodeViewWrapper } from '@tiptap/react'; import { Excalidraw } from 'tiptap/core/extensions/excalidraw'; -import { getEditorContainerDOMSize } from 'tiptap/prose-utils'; +import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant'; +import { clamp, getEditorContainerDOMSize } from 'tiptap/prose-utils'; import cls from 'classnames'; -import { IconMind } from 'components/icons'; +import { IconMind, IconZoomIn, IconZoomOut } from 'components/icons'; import { Resizeable } from 'components/resizeable'; +import { Tooltip } from 'components/tooltip'; import deepEqual from 'deep-equal'; import { useToggle } from 'hooks/use-toggle'; @@ -30,6 +32,7 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => { const [loading, toggleLoading] = useToggle(true); const [error, setError] = useState(null); const [visible, toggleVisible] = useToggle(false); + const [zoom, setZoomState] = useState(100); const onResize = useCallback( (size) => { @@ -47,6 +50,14 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => { [toggleVisible] ); + const setZoom = useCallback((type: 'minus' | 'plus') => { + return () => { + setZoomState((currentZoom) => + clamp(type === 'minus' ? currentZoom - ZOOM_STEP : currentZoom + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM) + ); + }; + }, []); + useEffect(() => { let isUnmount = false; @@ -114,6 +125,8 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => { display: 'flex', justifyContent: 'center', alignItems: 'center', + transform: `scale(${zoom / 100})`, + transition: `all ease-in-out .3s`, }} dangerouslySetInnerHTML={{ __html: Svg?.outerHTML ?? '' }} /> @@ -127,6 +140,27 @@ export const _ExcalidrawWrapper = ({ editor, node, updateAttributes }) => { 绘图 + +
+ +
diff --git a/packages/client/src/tiptap/core/wrappers/image/index.module.scss b/packages/client/src/tiptap/core/wrappers/image/index.module.scss index edb7b58..5f062ec 100644 --- a/packages/client/src/tiptap/core/wrappers/image/index.module.scss +++ b/packages/client/src/tiptap/core/wrappers/image/index.module.scss @@ -1,9 +1,23 @@ .wrap { + position: relative; display: flex; + height: 100%; padding: 8px 16px; cursor: pointer; border: 1px solid var(--node-border-color); border-radius: var(--border-radius); justify-content: space-between; align-items: center; + + .handlerWrap { + position: absolute; + right: 10px; + bottom: 10px; + z-index: 2; + padding: 2px 4px; + background-color: var(--semi-color-bg-2); + border: 1px solid var(--node-border-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + } } diff --git a/packages/client/src/tiptap/core/wrappers/image/index.tsx b/packages/client/src/tiptap/core/wrappers/image/index.tsx index b7cfb05..e64e4c3 100644 --- a/packages/client/src/tiptap/core/wrappers/image/index.tsx +++ b/packages/client/src/tiptap/core/wrappers/image/index.tsx @@ -1,17 +1,21 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { LazyLoadImage } from 'react-lazy-load-image-component'; -import { Spin, Typography } from '@douyinfe/semi-ui'; +import { Button, Spin, Typography } from '@douyinfe/semi-ui'; import { NodeViewWrapper } from '@tiptap/react'; +import { MAX_ZOOM, MIN_ZOOM, ZOOM_STEP } from 'tiptap/core/menus/mind/constant'; import { + clamp, extractFileExtension, extractFilename, getEditorContainerDOMSize, getImageWidthHeight, } from 'tiptap/prose-utils'; +import { IconZoomIn, IconZoomOut } from 'components/icons'; import { Resizeable } from 'components/resizeable'; +import { Tooltip } from 'components/tooltip'; import { useToggle } from 'hooks/use-toggle'; import { uploadFile } from 'services/file'; @@ -25,6 +29,7 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { const { width: maxWidth } = getEditorContainerDOMSize(editor); const $upload = useRef(); const [loading, toggleLoading] = useToggle(false); + const [zoom, setZoomState] = useState(100); const onResize = useCallback( (size) => { @@ -64,6 +69,14 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { [updateAttributes, toggleLoading] ); + const setZoom = useCallback((type: 'minus' | 'plus') => { + return () => { + setZoomState((currentZoom) => + clamp(type === 'minus' ? currentZoom - ZOOM_STEP : currentZoom + ZOOM_STEP, MIN_ZOOM, MAX_ZOOM) + ); + }; + }, []); + useEffect(() => { if (!src && !hasTrigger) { selectFile(); @@ -93,7 +106,44 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { ) : ( - +
+
+ +
+ +
+ +
+
)} diff --git a/packages/server/src/dtos/update-wiki.dto.ts b/packages/server/src/dtos/update-wiki.dto.ts index 3603b3f..52f59eb 100644 --- a/packages/server/src/dtos/update-wiki.dto.ts +++ b/packages/server/src/dtos/update-wiki.dto.ts @@ -10,7 +10,7 @@ export class UpdateWikiDto { @IsString({ message: '知识库描述类型错误(正确类型为:String)' }) @IsNotEmpty({ message: '知识库描述不能为空' }) - @MinLength(3, { message: '知识库描述至少3个字符' }) + @MinLength(1, { message: '知识库描述至少1个字符' }) @IsOptional() description: string;