diff --git a/react-ui/package.json b/react-ui/package.json index cab066e..e7651de 100644 --- a/react-ui/package.json +++ b/react-ui/package.json @@ -76,6 +76,7 @@ "react-cropper": "^2.3.3", "react-dev-inspector": "^1.8.1", "react-dom": "^18.2.0", + "react-draggable": "^4.4.6", "react-helmet-async": "^1.3.0", "react-highlight": "^0.15.0" }, diff --git a/react-ui/src/assets/img/robot.png b/react-ui/src/assets/img/robot.png new file mode 100644 index 0000000..e91be9a Binary files /dev/null and b/react-ui/src/assets/img/robot.png differ diff --git a/react-ui/src/components/RobotFrame/index.less b/react-ui/src/components/RobotFrame/index.less new file mode 100644 index 0000000..e3e5662 --- /dev/null +++ b/react-ui/src/components/RobotFrame/index.less @@ -0,0 +1,34 @@ +.robot-frame { + position: fixed; + top: 55px; + right: -610px; + z-index: 100; + width: 600px; + height: calc(100% - 55px); + background-color: #fff; + box-shadow: -6px 0 16px 0 rgba(0, 0, 0, 0.08), -3px 0 6px -4px rgba(0, 0, 0, 0.12), + -9px 0 28px 8px rgba(0, 0, 0, 0.05); + transition: right 0.3s ease-in-out; + + // 增加优先级 + &&--visible { + right: 0; + } + + &__header { + display: flex; + gap: 10px; + align-items: center; + justify-content: flex-end; + width: 100%; + height: 60px; + padding: 0 15px; + border-bottom: 1px solid #e8e8e8; + } + + &__iframe { + width: 100%; + height: calc(100% - 60px); + border: 0; + } +} diff --git a/react-ui/src/components/RobotFrame/index.tsx b/react-ui/src/components/RobotFrame/index.tsx new file mode 100644 index 0000000..49789aa --- /dev/null +++ b/react-ui/src/components/RobotFrame/index.tsx @@ -0,0 +1,30 @@ +import { CloseOutlined, ExpandOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import classNames from 'classnames'; +import styles from './index.less'; + +type RobotFrameProps = { + onClose: () => void; + visible: boolean; +}; + +function RobotFrame({ onClose, visible }: RobotFrameProps) { + const url = 'http://172.20.32.181:30080/chat/l4S79c4rly0o1pn7'; + const openUrl = () => { + window.open(url, '_blank'); + }; + + return ( +
+
+ + +
+ +
+ ); +} + +export default RobotFrame; diff --git a/react-ui/src/hooks/draggable.ts b/react-ui/src/hooks/draggable.ts new file mode 100644 index 0000000..b093ea6 --- /dev/null +++ b/react-ui/src/hooks/draggable.ts @@ -0,0 +1,37 @@ +// 处理 react-draggable 组件拖动结束时,响应了点击事件的 +import { useState } from 'react'; + +export const useDraggable = (onClick: () => void) => { + const [isDragging, setIsDragging] = useState(false); + + const handleStart = () => { + setIsDragging(false); + }; + + const handleDrag = () => { + if (!isDragging) { + setIsDragging(true); + } + }; + + const handleStop = () => { + // 延迟设置 isDragging 为 false 是为了确保在点击事件触发之前它仍然为 true + setTimeout(() => setIsDragging(false), 0); + }; + + const handleClick = (e: React.MouseEvent) => { + if (isDragging) { + e.preventDefault(); + e.stopPropagation(); + } else { + onClick(); + } + }; + + return { + handleStart, + handleDrag, + handleStop, + handleClick, + }; +}; diff --git a/react-ui/src/pages/Workspace/index.less b/react-ui/src/pages/Workspace/index.less index f7f95a2..3c7bd42 100644 --- a/react-ui/src/pages/Workspace/index.less +++ b/react-ui/src/pages/Workspace/index.less @@ -1,4 +1,5 @@ .workspace { + position: relative; height: 100%; padding: 20px 30px 10px; overflow-y: auto; @@ -43,4 +44,14 @@ min-width: 326px; height: 700px; } + + &__robot-img { + position: fixed; + right: 30px; + bottom: 20px; + width: 64px; + height: 64px; + background-color: white; + cursor: pointer; + } } diff --git a/react-ui/src/pages/Workspace/index.tsx b/react-ui/src/pages/Workspace/index.tsx index 55acd26..f5e6fe9 100644 --- a/react-ui/src/pages/Workspace/index.tsx +++ b/react-ui/src/pages/Workspace/index.tsx @@ -1,7 +1,10 @@ +import RobotFrame from '@/components/RobotFrame'; +import { useDraggable } from '@/hooks/draggable'; import { getWorkspaceOverviewReq } from '@/services/workspace'; import { ExperimentInstance } from '@/types'; import { to } from '@/utils/promise'; import { useEffect, useState } from 'react'; +import Draggable from 'react-draggable'; import AssetsManagement from './components/AssetsManagement'; import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart'; import ExperitableTable from './components/ExperimentTable'; @@ -20,6 +23,10 @@ type OverviewData = { function Workspace() { const [overviewData, setOverviewData] = useState(); + const [robotFrameVisible, setRobotFrameVisible] = useState(false); + const { handleStart, handleStop, handleDrag, handleClick } = useDraggable(() => + setRobotFrameVisible((prev) => !prev), + ); const users: number[] = new Array(8).fill(0); useEffect(() => { getWorkspaceOverview(); @@ -64,6 +71,19 @@ function Workspace() { + + e.preventDefault()} + > + + + setRobotFrameVisible(false)} + > ); }