| @@ -76,6 +76,7 @@ | |||||
| "react-cropper": "^2.3.3", | "react-cropper": "^2.3.3", | ||||
| "react-dev-inspector": "^1.8.1", | "react-dev-inspector": "^1.8.1", | ||||
| "react-dom": "^18.2.0", | "react-dom": "^18.2.0", | ||||
| "react-draggable": "^4.4.6", | |||||
| "react-helmet-async": "^1.3.0", | "react-helmet-async": "^1.3.0", | ||||
| "react-highlight": "^0.15.0" | "react-highlight": "^0.15.0" | ||||
| }, | }, | ||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| @@ -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 ( | |||||
| <div | |||||
| className={classNames(styles['robot-frame'], { [styles['robot-frame--visible']]: visible })} | |||||
| > | |||||
| <div className={styles['robot-frame__header']}> | |||||
| <Button icon={<ExpandOutlined />} type="text" onClick={openUrl}></Button> | |||||
| <Button icon={<CloseOutlined />} type="text" onClick={onClose}></Button> | |||||
| </div> | |||||
| <iframe className={styles['robot-frame__iframe']} src={url} allow="microphone"></iframe> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default RobotFrame; | |||||
| @@ -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<HTMLElement>) => { | |||||
| if (isDragging) { | |||||
| e.preventDefault(); | |||||
| e.stopPropagation(); | |||||
| } else { | |||||
| onClick(); | |||||
| } | |||||
| }; | |||||
| return { | |||||
| handleStart, | |||||
| handleDrag, | |||||
| handleStop, | |||||
| handleClick, | |||||
| }; | |||||
| }; | |||||
| @@ -1,4 +1,5 @@ | |||||
| .workspace { | .workspace { | ||||
| position: relative; | |||||
| height: 100%; | height: 100%; | ||||
| padding: 20px 30px 10px; | padding: 20px 30px 10px; | ||||
| overflow-y: auto; | overflow-y: auto; | ||||
| @@ -43,4 +44,14 @@ | |||||
| min-width: 326px; | min-width: 326px; | ||||
| height: 700px; | height: 700px; | ||||
| } | } | ||||
| &__robot-img { | |||||
| position: fixed; | |||||
| right: 30px; | |||||
| bottom: 20px; | |||||
| width: 64px; | |||||
| height: 64px; | |||||
| background-color: white; | |||||
| cursor: pointer; | |||||
| } | |||||
| } | } | ||||
| @@ -1,7 +1,10 @@ | |||||
| import RobotFrame from '@/components/RobotFrame'; | |||||
| import { useDraggable } from '@/hooks/draggable'; | |||||
| import { getWorkspaceOverviewReq } from '@/services/workspace'; | import { getWorkspaceOverviewReq } from '@/services/workspace'; | ||||
| import { ExperimentInstance } from '@/types'; | import { ExperimentInstance } from '@/types'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import Draggable from 'react-draggable'; | |||||
| import AssetsManagement from './components/AssetsManagement'; | import AssetsManagement from './components/AssetsManagement'; | ||||
| import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart'; | import ExperimentChart, { type ExperimentStatistics } from './components/ExperimentChart'; | ||||
| import ExperitableTable from './components/ExperimentTable'; | import ExperitableTable from './components/ExperimentTable'; | ||||
| @@ -20,6 +23,10 @@ type OverviewData = { | |||||
| function Workspace() { | function Workspace() { | ||||
| const [overviewData, setOverviewData] = useState<OverviewData>(); | const [overviewData, setOverviewData] = useState<OverviewData>(); | ||||
| const [robotFrameVisible, setRobotFrameVisible] = useState(false); | |||||
| const { handleStart, handleStop, handleDrag, handleClick } = useDraggable(() => | |||||
| setRobotFrameVisible((prev) => !prev), | |||||
| ); | |||||
| const users: number[] = new Array(8).fill(0); | const users: number[] = new Array(8).fill(0); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| getWorkspaceOverview(); | getWorkspaceOverview(); | ||||
| @@ -64,6 +71,19 @@ function Workspace() { | |||||
| <AssetsManagement></AssetsManagement> | <AssetsManagement></AssetsManagement> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <Draggable onStart={handleStart} onStop={handleStop} onDrag={handleDrag}> | |||||
| <img | |||||
| className={styles['workspace__robot-img']} | |||||
| src={require('@/assets/img/robot.png')} | |||||
| onClick={handleClick} | |||||
| onDragStart={(e) => e.preventDefault()} | |||||
| ></img> | |||||
| </Draggable> | |||||
| <RobotFrame | |||||
| visible={robotFrameVisible} | |||||
| onClose={() => setRobotFrameVisible(false)} | |||||
| ></RobotFrame> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||