refactor(components): 重构 playground组件

- 移除 Data、Scene 和 IodRelevant 组件中的 Drawer
- 优化 Data、Scene 和 IodRelevant 组件的结构
- 添加 Header 组件用于展示标题和关闭按钮
- 使用 Main 组件替代原来的 ShowCard 组件
- 调整样式和布局,提高组件的可复用性和可维护性
This commit is contained in:
zhaoweijie 2025-08-23 17:03:14 +08:00
parent 6fb71b01f0
commit e640b1254d
17 changed files with 664 additions and 410 deletions

View File

@ -1,18 +1,22 @@
import React from "react"; import React from "react"
import { Typography, Button } from "antd"; import { Typography } from "antd"
import { AcademicCapIcon, ChevronRightIcon } from "@heroicons/react/24/outline"; import { ChevronRightIcon } from "@heroicons/react/24/outline"
const { Title } = Typography; const { Title } = Typography
type Props = { type Props = {
Header: React.ReactNode; Header: React.ReactNode
showButton?: boolean; showButton?: boolean
onClick?: () => void; onClick?: () => void
}; }
export const DataNavigation: React.FC<Props> = ({ Header, showButton = true, onClick }) => { export const DataNavigation: React.FC<Props> = ({
Header,
showButton = true,
onClick
}) => {
return ( return (
<div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg mb-3"> <div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg">
{/* 左侧部分 */} {/* 左侧部分 */}
<div className="flex items-center"> <div className="flex items-center">
<Title <Title
@ -25,11 +29,13 @@ export const DataNavigation: React.FC<Props> = ({ Header, showButton = true, onC
{/* 右侧部分 */} {/* 右侧部分 */}
{showButton && ( {showButton && (
<div className="flex items-center text-[#3a3a3a] cursor-pointer space-x-0.5 hover:text-[#00c0ef] transition" onClick={onClick}> <div
className="flex items-center text-[#3a3a3a] cursor-pointer space-x-0.5 hover:text-[#3581e3] transition-colors duration-200"
onClick={onClick}>
<span className="text-[12px]"></span> <span className="text-[12px]"></span>
<ChevronRightIcon className="w-4 h-4" /> <ChevronRightIcon className="w-4 h-4" />
</div> </div>
)} )}
</div> </div>
) )
}; }

View File

@ -1,8 +1,9 @@
import React, { useMemo, useState } from "react" import React, { useEffect, useMemo } from "react"
import { DataNavigation } from "@/components/Common/DataNavigation.tsx" import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
import { Card, Drawer, Skeleton } from "antd" import { Card, Skeleton } from "antd"
import { useMessageOption } from "@/hooks/useMessageOption.tsx" import { useMessageOption } from "@/hooks/useMessageOption.tsx"
import { IodRegistryEntry } from "@/types/iod.ts" import { IodRegistryEntry } from "@/types/iod.ts"
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
// import { Drawer } from './Drawer.tsx' // import { Drawer } from './Drawer.tsx'
@ -32,45 +33,89 @@ const defaultData: IodRegistryEntry[] = [
} }
] ]
type ShowCardProps = { type HeaderProps = {
title: string
showButton?: boolean
onClick?: () => void
}
const Header: React.FC<HeaderProps> = ({
title,
showButton = true,
onClick
}) => (
<DataNavigation
Header={
<div className="flex items-center gap-0.5 text-[#3581e3]">
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="3572"
width="18"
height="18">
<path
d="M877.714286 54.857143H754.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H498.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H292.571429c-20.228571 0-36.571429 16.342857-36.571429 36.571428v137.142858h-109.714286c-20.228571 0-36.571429 16.342857-36.571428 36.571428v722.285714c0 20.228571 16.342857 36.571429 36.571428 36.571429h585.142857c20.228571 0 36.571429-16.342857 36.571429-36.571429v-109.714285h109.714286c20.228571 0 36.571429-16.342857 36.571428-36.571429V91.428571c0-20.228571-16.342857-36.571429-36.571428-36.571428zM685.714286 941.714286H192V310.857143h249.142857v198.857143c0 25.257143 20.457143 45.714286 45.714286 45.714285h198.857143v386.285715z m0-459.428572H514.285714V310.857143h0.228572L685.714286 482.057143v0.228571z m146.285714 313.142857h-64V448L548.571429 228.571429H338.285714v-91.428572h77.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h173.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h77.714286v658.285714z"
p-id="3573"
fill="#3581e3"></path>
</svg>
{title}
</div>
}
showButton={showButton}
onClick={onClick}
/>
)
type MainProps = {
loading: boolean loading: boolean
record: IodRegistryEntry data: IodRegistryEntry[]
truncate?: boolean truncate?: boolean
} }
const Main: React.FC<MainProps> = ({ data, loading, truncate = true }) => (
const ShowCard: React.FC<ShowCardProps> = ({ <div className="space-y-1.5 flex-1 overflow-y-auto">
loading, {data.map((item, index) => {
record, return (
truncate = true <Card
}) => ( className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"> key={item.doId}>
{loading ? ( {loading ? (
<Skeleton title={false} active /> <Skeleton title={false} active />
) : ( ) : (
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<h3 <h3
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`} className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.name}> title={item.name}>
{record.name} {item.name}
</h3> </h3>
<p <p
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`} className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.doId}> title={item.doId}>
{record.doId} {item.doId}
</p> </p>
<p <p
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`} className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
title={record.description}> title={item.description}>
{record.description} {item.description}
</p> </p>
</div> </div>
)} )}
</Card> </Card>
) )
export const PlaygroundData = () => { })}
</div>
)
type Props = {
className?: string
}
export const PlaygroundData: React.FC<Props> = ({className}) => {
const { messages, iodLoading, currentMessageId, iodSearch } = const { messages, iodLoading, currentMessageId, iodSearch } =
useMessageOption() useMessageOption()
const { setShowPlayground, setDetailHeader, setDetailMain } =
useIodPlaygroundContext()
const data = useMemo<IodRegistryEntry[]>(() => { const data = useMemo<IodRegistryEntry[]>(() => {
// 确保loading状态时数据大于3 // 确保loading状态时数据大于3
if (iodLoading) { if (iodLoading) {
@ -91,74 +136,27 @@ export const PlaygroundData = () => {
return messages.length > 0 ? "推荐数据" : "热点数据" return messages.length > 0 ? "推荐数据" : "热点数据"
}, [messages]) }, [messages])
const [open, setOpen] = useState(false) useEffect(() => {
setDetailHeader(
const showDrawer = () => { <Header
if (iodLoading) { title={title}
return showButton={false}
} onClick={() => setShowPlayground(false)}
setOpen(true) />
} )
setDetailMain(<Main loading={iodLoading} data={data} truncate={false} />)
const onClose = () => { }, [title, iodLoading, data])
setOpen(false)
}
return ( return (
<Card <Card
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden" className={`${className}`}
hoverable> hoverable>
<div className="h-full flex flex-col relative"> <div className="h-full flex flex-col gap-2 relative">
{/* 数据导航 */} {/* 数据导航 */}
<DataNavigation <Header title={title} onClick={() => setShowPlayground(false)} />
Header={
<div className="flex items-center gap-0.5 text-[#3581e3]">
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="3572"
width="18"
height="18">
<path
d="M877.714286 54.857143H754.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H498.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H292.571429c-20.228571 0-36.571429 16.342857-36.571429 36.571428v137.142858h-109.714286c-20.228571 0-36.571429 16.342857-36.571428 36.571428v722.285714c0 20.228571 16.342857 36.571429 36.571428 36.571429h585.142857c20.228571 0 36.571429-16.342857 36.571429-36.571429v-109.714285h109.714286c20.228571 0 36.571429-16.342857 36.571428-36.571429V91.428571c0-20.228571-16.342857-36.571429-36.571428-36.571428zM685.714286 941.714286H192V310.857143h249.142857v198.857143c0 25.257143 20.457143 45.714286 45.714286 45.714285h198.857143v386.285715z m0-459.428572H514.285714V310.857143h0.228572L685.714286 482.057143v0.228571z m146.285714 313.142857h-64V448L548.571429 228.571429H338.285714v-91.428572h77.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h173.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h77.714286v658.285714z"
p-id="3573"
fill="#3581e3"></path>
</svg>
{title}
</div>
}
onClick={showDrawer}
/>
{/* 数据列表 */} {/* 数据列表 */}
<div className="space-y-1.5 flex-1 overflow-y-auto"> <Main loading={iodLoading} data={data.slice(0, 3)} />
{data.slice(0, 3).map((item, index) => {
return (
<ShowCard key={item.doId} loading={iodLoading} record={item} />
)
})}
</div> </div>
</div>
{/* 抽屉 */}
<Drawer
title={title}
closable={{ "aria-label": "Close Button" }}
onClose={onClose}
open={open}
width="33.33%">
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
{data.map((item, index) => (
<ShowCard
key={item.doId}
loading={iodLoading}
record={item}
truncate={false}
/>
))}
</div>
</Drawer>
</Card> </Card>
) )
} }

View File

@ -74,8 +74,10 @@ export const PlaygroundHistory = () => {
children: qaPrompt.map((item) => { children: qaPrompt.map((item) => {
return { return {
key: item.id, key: item.id,
label: <span title={item.title}>{item.title}</span>, label: <div className="flex items-center gap-2">
icon: <p className="w-3.5">{item.icon}</p> <p className="w-5 h-5 [&_.ant-avatar]:!w-full [&_.ant-avatar]:!h-full [&_.ant-avatar]:relative [&_.ant-avatar]:-top-3">{item.icon}</p>
<span title={item.title}>{item.title}</span>
</div>,
} }
}) })
} }

View File

@ -150,10 +150,9 @@ const StatCard: React.FC<{
) )
} }
// 主组件
export const StatisticGrid: React.FC = () => { export const StatisticGrid: React.FC = () => {
return ( return (
<div className="p-6 min-h-screen"> <div className="p-6">
{/* 第一行3 个卡片 */} {/* 第一行3 个卡片 */}
<div className="grid grid-cols-3 gap-6 mb-6"> <div className="grid grid-cols-3 gap-6 mb-6">
<StatCard <StatCard
@ -203,7 +202,7 @@ export const StatisticGrid: React.FC = () => {
<ResearchPaperIcon className="w-6 h-6 text-white" color="#3581e3" /> <ResearchPaperIcon className="w-6 h-6 text-white" color="#3581e3" />
} }
number={1380026} number={1380026}
label="科研论文" label="数据论文"
/> />
<StatCard <StatCard
icon={ icon={
@ -218,7 +217,10 @@ export const StatisticGrid: React.FC = () => {
) )
} }
export const PlaygroundIodRelevant: React.FC = () => { type Props = {
className?: string
}
export const PlaygroundIodRelevant: React.FC<Props> = ({ className }) => {
const { messages, iodLoading, currentMessageId, iodSearch } = const { messages, iodLoading, currentMessageId, iodSearch } =
useMessageOption() useMessageOption()
@ -348,10 +350,11 @@ export const PlaygroundIodRelevant: React.FC = () => {
<Card <Card
hoverable hoverable
variant="outlined" variant="outlined"
className="flex flex-col h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] translate-y-[-2px] !bg-[#f0f9ff]"> className={`${className} translate-y-[-2px] !bg-[#d0e6ff] shadow-[#d0e6ff]/30`}>
<div className="h-full flex flex-col relative"> <div className="h-full flex flex-col relative">
{/* 花瓣效果 */} {/* 花瓣效果 */}
<div className={`absolute inset-0 pointer-events-none z-0 overflow-hidden ${showSearchData ? '' : ''}`}> <div
className={`absolute inset-0 pointer-events-none z-0 overflow-hidden ${showSearchData ? "" : ""}`}>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64"> <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64">
<CircleElement delay={0} playing={true} /> <CircleElement delay={0} playing={true} />
<CircleElement delay={1} playing={true} /> <CircleElement delay={1} playing={true} />
@ -364,20 +367,24 @@ export const PlaygroundIodRelevant: React.FC = () => {
<h2 className="text-xl font-semibold text-[#1a3c87] flex justify-center items-center"> <h2 className="text-xl font-semibold text-[#1a3c87] flex justify-center items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<SearchIcon /> <SearchIcon />
{messages.length > 0
? "科创数联网深度搜索"
: "科创数联网连接资源"}
</div> </div>
{/*<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">*/} {/*<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">*/}
{/* {data.length}个结果*/} {/* {data.length}个结果*/}
{/*</button>*/} {/*</button>*/}
</h2> </h2>
<p className="text-sm text-[#1a3c87] mt-1 text-center"> <p className="text-sm text-[#1a3c87] mt-1 text-center">
{messages.length > 0
? "下面是在科创数联网上进行深度搜索得到的相关数据、场景和团队"
: "下面是科创数联网连接的数据、场景和团队"}
</p> </p>
</div> </div>
{/* Content */} {/* Content */}
<div className="space-y-2 flex-1 overflow-y-auto"> <div className="space-y-2 flex-1 overflow-y-auto">
{showSearchData ? ( {messages.length ? (
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
<motion.div <motion.div
key="search-results" key="search-results"

View File

@ -1,7 +1,8 @@
import React, { useMemo, useState } from "react" import React, { useEffect, useMemo, useState } from "react"
import { DataNavigation } from "@/components/Common/DataNavigation.tsx" import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
import { Card, Drawer, Skeleton } from "antd" import { Card, Drawer, Skeleton } from "antd"
import { IodRegistryEntry } from "@/types/iod.ts" import { IodRegistryEntry } from "@/types/iod.ts"
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
const defaultData: IodRegistryEntry[] = [ const defaultData: IodRegistryEntry[] = [
{ {
@ -27,46 +28,86 @@ const defaultData: IodRegistryEntry[] = [
} }
] ]
type ShowCardProps = { type HeaderProps = {
title: string
showButton?: boolean
onClick?: () => void
}
const Header: React.FC<HeaderProps> = ({ title, showButton = true, onClick }) => (
<DataNavigation
Header={
<div className="flex items-center text-[#54c41d] gap-1">
<svg
className="icon"
viewBox="0 0 1025 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="6235"
width="18"
height="18">
<path
d="M980.34571 1.143792c-4.850903 0-9.824354 0.888481-14.797806 2.930966L229.773215 299.724504H20.428686c-11.233669 0-20.424853 9.446494-20.424853 21.180572V702.584302c0 11.74429 9.191184 21.180572 20.424853 21.180573h129.820365c-4.728353 14.808018-7.271248 30.51473-7.271248 46.46654 0 84.119757 68.678568 152.543014 153.176184 152.543014 70.721053 0 130.330986-47.998404 147.93721-112.847312l521.569043 209.59984c4.983664 1.919936 9.957116 2.930966 14.808019 2.930967 21.568645 0 40.839493-18.127057 40.839493-42.371358V43.525362C1021.195415 19.270849 1002.047116 1.143792 980.34571 1.143792zM296.153987 831.250663c-33.833769 0-61.274559-27.308028-61.274558-61.009035 0-14.297397 4.983664-27.951411 14.042086-38.807221l108.374269 43.525362c-2.553107 31.403211-28.972654 56.290895-61.141797 56.290894z m633.12959 74.550713L263.984844 638.501326l-16.462431-6.638077H91.915671V391.626129h155.606742l16.462431-6.638077 665.298733-267.30005v788.113374z m0 0"
fill="#54c41d"
p-id="6236"></path>
</svg>
{title}
</div>
}
showButton={showButton}
onClick={onClick}
/>
)
type MainProps = {
loading: boolean loading: boolean
record: IodRegistryEntry data: IodRegistryEntry[]
truncate?: boolean truncate?: boolean
} }
const Main: React.FC<MainProps> = ({ data, loading, truncate = true }) => (
const ShowCard: React.FC<ShowCardProps> = ({ <div className="space-y-1.5 flex-1 overflow-y-auto">
loading, {data.map((item, index) => {
record, return (
truncate = true <Card
}) => ( className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"> key={item.doId}>
{loading ? ( {loading ? (
<Skeleton title={false} active /> <Skeleton title={false} active />
) : ( ) : (
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<h3 <h3
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`} className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.name}> title={item.name}>
{record.name} {item.name}
</h3> </h3>
<p <p
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`} className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.doId}> title={item.doId}>
{record.doId} {item.doId}
</p> </p>
<p <p
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`} className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
title={record.description}> title={item.description}>
{record.description} {item.description}
</p> </p>
</div> </div>
)} )}
</Card> </Card>
) )
})}
</div>
)
export const PlaygroundScene = () => {
type Props = {
className?: string
}
export const PlaygroundScene: React.FC<Props> = ({ className }) => {
const { messages, iodLoading, currentMessageId, iodSearch } = const { messages, iodLoading, currentMessageId, iodSearch } =
useMessageOption() useMessageOption()
const { setShowPlayground, setDetailHeader, setDetailMain } =
useIodPlaygroundContext()
const data = useMemo<IodRegistryEntry[]>(() => { const data = useMemo<IodRegistryEntry[]>(() => {
// 确保loading状态时数据大于3 // 确保loading状态时数据大于3
if (iodLoading) { if (iodLoading) {
@ -87,74 +128,24 @@ export const PlaygroundScene = () => {
return messages.length > 0 ? "推荐场景" : "热点场景" return messages.length > 0 ? "推荐场景" : "热点场景"
}, [messages]) }, [messages])
const [open, setOpen] = useState(false) useEffect(() => {
setDetailHeader(
const showDrawer = () => { <Header title={title} showButton={false} onClick={() => setShowPlayground(false)} />
if (iodLoading) { )
return setDetailMain(<Main loading={iodLoading} data={data} truncate={false} />)
} }, [title, iodLoading, data])
setOpen(true)
}
const onClose = () => {
setOpen(false)
}
return ( return (
<Card <Card
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden" className={`${className}`}
hoverable> hoverable>
<div className="h-full flex flex-col relative"> <div className="h-full flex flex-col gap-2 relative">
{/* 数据导航 */} {/* 数据导航 */}
<DataNavigation <Header title={title} onClick={() => setShowPlayground(false)} />
Header={
<div className="flex items-center text-[#54c41d] gap-1">
<svg
className="icon"
viewBox="0 0 1025 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="6235"
width="18"
height="18">
<path
d="M980.34571 1.143792c-4.850903 0-9.824354 0.888481-14.797806 2.930966L229.773215 299.724504H20.428686c-11.233669 0-20.424853 9.446494-20.424853 21.180572V702.584302c0 11.74429 9.191184 21.180572 20.424853 21.180573h129.820365c-4.728353 14.808018-7.271248 30.51473-7.271248 46.46654 0 84.119757 68.678568 152.543014 153.176184 152.543014 70.721053 0 130.330986-47.998404 147.93721-112.847312l521.569043 209.59984c4.983664 1.919936 9.957116 2.930966 14.808019 2.930967 21.568645 0 40.839493-18.127057 40.839493-42.371358V43.525362C1021.195415 19.270849 1002.047116 1.143792 980.34571 1.143792zM296.153987 831.250663c-33.833769 0-61.274559-27.308028-61.274558-61.009035 0-14.297397 4.983664-27.951411 14.042086-38.807221l108.374269 43.525362c-2.553107 31.403211-28.972654 56.290895-61.141797 56.290894z m633.12959 74.550713L263.984844 638.501326l-16.462431-6.638077H91.915671V391.626129h155.606742l16.462431-6.638077 665.298733-267.30005v788.113374z m0 0"
fill="#54c41d"
p-id="6236"></path>
</svg>
{title}
</div>
}
onClick={showDrawer}
/>
{/* 数据列表 */} {/* 数据列表 */}
<div className="space-y-1.5 flex-1 overflow-y-auto"> <Main loading={iodLoading} data={data.slice(0, 3)} />
{data.slice(0, 3).map((item, index) => {
return (
<ShowCard key={item.doId} loading={iodLoading} record={item} />
)
})}
</div> </div>
</div>
{/* 抽屉 */}
<Drawer
title={title}
closable={{ "aria-label": "Close Button" }}
onClose={onClose}
open={open}
width="33.33%">
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
{data.map((item, index) => (
<ShowCard
key={item.doId}
loading={iodLoading}
record={item}
truncate={false}
/>
))}
</div>
</Drawer>
</Card> </Card>
) )
} }

View File

@ -1,43 +1,8 @@
import React, { useMemo, useState } from "react" import React, { useEffect, useMemo } from "react"
import { DataNavigation } from "@/components/Common/DataNavigation.tsx" import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
import { Card, Drawer, Skeleton } from "antd" import { Card, Skeleton } from "antd"
import { IodRegistryEntry } from "@/types/iod.ts" import { IodRegistryEntry } from "@/types/iod.ts"
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
type ShowCardProps = {
loading: boolean
record: IodRegistryEntry
truncate?: boolean
}
const ShowCard: React.FC<ShowCardProps> = ({
loading,
record,
truncate = true
}) => (
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
{loading ? (
<Skeleton title={false} active />
) : (
<div className="flex flex-col gap-0.5">
<h3
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.name}>
{record.name}
</h3>
<p
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
title={record.doId}>
{record.doId}
</p>
<p
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
title={record.description}>
{record.description}
</p>
</div>
)}
</Card>
)
const defaultData: IodRegistryEntry[] = [ const defaultData: IodRegistryEntry[] = [
{ {
@ -54,49 +19,20 @@ const defaultData:IodRegistryEntry[] = [
{ {
name: "清华大学智能系统实验室", name: "清华大学智能系统实验室",
description: "清华大学", description: "清华大学",
doId: "CSTR:15552.13.04.91.2021.614", doId: "CSTR:15552.13.04.91.2021.614"
}, }
] ]
export const PlaygroundTeam = () => { type HeaderProps = {
const { messages, iodLoading, currentMessageId, iodSearch } = useMessageOption() title: string
showButton?: boolean
const data = useMemo<IodRegistryEntry[]>(() => { onClick?: () => void
// 确保loading状态时数据大于3
if (iodLoading) {
return defaultData
} }
const Header: React.FC<HeaderProps> = ({
if (messages.length && iodSearch) { title,
const currentMessage = messages?.find( showButton = true,
(message) => message.id === currentMessageId onClick
) }) => (
return currentMessage?.iodSources.organization.data ?? []
}
return defaultData
}, [currentMessageId, messages, iodLoading])
const title = useMemo(() => {
return messages.length > 0 ? "推荐团队" : "热点团队"
}, [messages])
const [open, setOpen] = useState(false)
const showDrawer = () => {
setOpen(true)
}
const onClose = () => {
setOpen(false)
}
return (
<Card
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden"
hoverable>
<div className="h-full flex flex-col relative">
{/* 数据导航 */}
<DataNavigation <DataNavigation
Header={ Header={
<div className="flex items-center text-[#BE0BAC] gap-1"> <div className="flex items-center text-[#BE0BAC] gap-1">
@ -120,35 +56,109 @@ export const PlaygroundTeam = () => {
{title} {title}
</div> </div>
} }
onClick={showDrawer} showButton={showButton}
onClick={onClick}
/> />
)
{/* 场景列表 */} type MainProps = {
<div className="grid grid-cols-3 gap-3 flex-1 overflow-y-auto"> loading: boolean
{data.slice(0, 3).map((item, index) => ( data: IodRegistryEntry[]
<ShowCard key={item.doId} loading={iodLoading} record={item} /> truncate?: boolean
))} // 水平展示三个还是按列展示(页面和详情展示不一样)
flat?: boolean
}
const Main: React.FC<MainProps> = ({
data,
loading,
truncate = true,
flat = true
}) => (
<div
className={`${flat ? "grid grid-cols-3 gap-3" : "space-y-1.5"} flex-1 overflow-y-auto`}>
{data.map((item) => {
return (
<Card
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
key={item.doId}>
{loading ? (
<Skeleton title={false} active />
) : (
<div className="flex flex-col gap-0.5">
<h3
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
title={item.name}>
{item.name}
</h3>
<p
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
title={item.doId}>
{item.doId}
</p>
<p
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
title={item.description}>
{item.description}
</p>
</div> </div>
)}
</Card>
)
})}
</div> </div>
)
{/* 抽屉 */} type Props = {
<Drawer className?: string
}
export const PlaygroundTeam: React.FC<Props> = ({ className }) => {
const { messages, iodLoading, currentMessageId, iodSearch } =
useMessageOption()
const { setShowPlayground, setDetailHeader, setDetailMain } =
useIodPlaygroundContext()
const data = useMemo<IodRegistryEntry[]>(() => {
// 确保loading状态时数据大于3
if (iodLoading) {
return defaultData
}
if (messages.length && iodSearch) {
const currentMessage = messages?.find(
(message) => message.id === currentMessageId
)
return currentMessage?.iodSources.organization.data ?? []
}
return defaultData
}, [currentMessageId, messages, iodLoading])
const title = useMemo(() => {
return messages.length > 0 ? "推荐团队" : "热点团队"
}, [messages])
useEffect(() => {
setDetailHeader(
<Header
title={title} title={title}
closable={{ "aria-label": "Close Button" }} showButton={false}
onClose={onClose} onClick={() => setShowPlayground(false)}
open={open}
width="33.33%">
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
{data.map((item, index) => (
<ShowCard
key={item.doId}
loading={iodLoading}
record={item}
truncate={false}
/> />
))} )
setDetailMain(
<Main loading={iodLoading} data={data} truncate={false} flat={false} />
)
}, [title, iodLoading, data])
return (
<Card className={`${className}`} hoverable>
<div className="h-full flex flex-col gap-2 relative">
{/* 数据导航 */}
<Header title={title} onClick={() => setShowPlayground(false)} />
{/* 数据列表 */}
<Main loading={iodLoading} data={data.slice(0, 3)} />
</div> </div>
</Drawer>
</Card> </Card>
) )
} }

View File

@ -0,0 +1,21 @@
import React from "react"
export const BatteryIcon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
fill="currentColor"
fillRule="evenodd"
ref={ref}
{...props}>
<path
d="M604.16 112.64c13.568-35.0208-5.9904-57.7024-24.1152-80.8448H452.7616C436.2752 56.32 419.84 80.4864 434.176 112.64zM194.56 888.3712a213.4528 213.4528 0 0 0 21.504 2.304c195.7888 0 391.5776 0 587.3152 0.6656 25.1904 0 29.4912-10.24 29.3888-32.1024-0.6144-202.0864-3.1232-633.7024-3.1232-633.7024H194.56zM597.3504 307.712l7.1168 4.2496c-25.6 73.1136-50.8928 146.2272-78.4384 225.28h139.0592L437.9648 824.32l-8.6016-2.7648c17.92-71.0144 35.84-142.0288 54.9888-217.7536l-134.656-5.7856zM192.6656 926.72c-4.096 41.8304 14.7456 64.4096 54.3744 64.8192 66.56 0.6656 133.12 0 200.0384 0 105.8304 0 211.6608 0.3072 317.44 0 44.6464 0 69.6832-25.856 64.512-64.8704zM777.4208 141.0048c-20.992-1.6896-42.24-0.4096-63.3856-0.4096H257.4848c-41.3696 0-57.9584 12.3904-66.0992 49.3568h641.2288c-6.5024-32.5632-26.368-46.592-55.1936-48.9472z"
p-id="50919"></path>
</svg>
)
})

View File

@ -0,0 +1,26 @@
import React from "react"
export const CheckIcon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="41530"
fill="currentColor"
fillRule="evenodd"
ref={ref}
{...props}>
<path
d="M334.935114 642.334328l-185.247485-227.447917c-16.445757-20.169324-13.342784-49.957864 6.826541-66.713918L571.071355 10.569036c20.169324-16.445757 49.957864-13.342784 66.713919 6.826541l185.247484 227.447916c16.445757 20.169324 13.342784 49.957864-6.82654 66.713919L401.649033 649.160868c-20.479621 16.445757-50.268162 13.342784-66.713919-6.82654zM189.71598 693.843679L39.53209 509.216788c-14.273676-17.376648-11.481-43.131324 5.895648-57.404999l5.585352-4.654459c17.376648-14.273676 43.131324-11.481 57.404999 5.895648l150.494188 184.62689c14.273676 17.376648 11.481 43.131324-5.895649 57.405l-5.585351 4.654459c-17.686946 14.273676-43.441621 11.481-57.715297-5.895648zM877.024488 1024H275.668331a44.372513 44.372513 0 1 1 0-88.745026h601.356157a44.372513 44.372513 0 1 1 0 88.745026z"
p-id="41531"></path>
<path
d="M564.555112 345.06952l-77.264026-94.950972c-16.445757-20.169324-13.342784-49.957864 6.82654-66.713919l199.521161-162.595782c20.169324-16.445757 49.957864-13.342784 66.713918 6.82654l77.264026 94.950972c16.445757 20.169324 13.342784 49.957864-6.82654 66.713919l-199.521161 162.595782c-20.169324 16.445757-50.268162 13.342784-66.713918-6.82654zM646.163301 1020.58673l-94.950973-79.746405c51.509351-61.438864 77.574324-137.771999 72.919865-215.346322-4.344162-76.022837-37.85627-143.977945-94.330378-191.143134l79.746405-94.950972c82.849378 69.506594 131.87635 168.491431 138.392593 278.957268 6.205946 109.224648-29.78854 216.587512-101.777512 302.229565z"
p-id="41532"></path>
</svg>
)
})

View File

@ -0,0 +1,22 @@
import React from "react"
export const MedicineBottleFillIcon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
p-id="25925"
fill="currentColor"
fillRule="evenodd"
ref={ref}
{...props}>
<path
d="M725.333333 213.333333v85.333334a128 128 0 0 1 128 128v469.333333a42.666667 42.666667 0 0 1-42.666666 42.666667H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666667V426.666667a128 128 0 0 1 128-128V213.333333h426.666666z m-170.666666 256h-85.333334v85.333334H384v85.333333h85.290667L469.333333 725.333333h85.333334l-0.042667-85.333333H640v-85.333333h-85.333333v-85.333334z m256-384v85.333334H213.333333V85.333333h597.333334z"
p-id="25926"></path>
</svg>
)
})

View File

@ -0,0 +1,23 @@
import React from "react"
export const NewBottleIcon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="40222"
fill="currentColor"
fillRule="evenodd"
ref={ref}
{...props}>
<path
d="M848.818342 511.548501l-319.661376 308.373898c-14.899471 16.705467-35.216931 27.541446-56.888888 30.70194 16.705467-17.608466 29.798942-38.828924 37.474426-61.40388l175.633157-164.345679-105.199294-105.650794L717.883598 397.770723c4.96649-4.514991 8.126984-10.835979 8.126984-17.608466s-3.160494-13.093474-8.126984-17.608465c-9.029982-10.38448-24.832451-11.738977-35.216931-2.708995L542.250441 478.589065l-30.70194-30.70194v-30.70194L632.098765 295.731922s92.557319-92.557319 199.562611 13.544974c107.45679 106.102293 16.253968 201.820106 16.253968 201.820106h0.902998z m-339.075838 74.948853v-74.948853l30.70194-30.70194 38.828925 38.828924-69.530865 66.821869z m-200.465608 294.828925C216.719577 881.326279 139.964727 819.470899 139.964727 758.067019v-492.134038c0-61.40388 80.818342-123.259259 169.312169-123.25926S478.589065 204.077601 478.589065 265.932981v492.134038c0 61.40388-76.75485 123.259259-169.312169 123.25926zM447.887125 263.223986C425.763668 206.335097 370.229277 169.312169 309.276896 170.666667c-60.952381-1.354497-116.938272 35.216931-139.061728 92.557319v246.067019h61.40388v215.816579c-1.354497 11.738977 4.514991 23.026455 14.447971 29.347442 9.932981 6.320988 23.026455 6.320988 32.959436 0s15.802469-17.608466 14.447972-29.347442v-213.559083h153.961199l0.451499-248.324515z m-184.663139 30.70194c8.126984 0 16.253968 3.160494 22.123457 9.029982 5.869489 5.869489 9.029982 13.996473 9.029982 22.123457v184.663139H232.522046V326.433862c0-8.126984 2.708995-16.253968 9.029982-22.123456 5.869489-5.869489 13.996473-9.029982 22.123457-9.029983l-0.451499-1.354497z m0 0"
p-id="40223"></path>
</svg>
)
})

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
import React from "react"
export const Ship1Icon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
className="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="42511"
fill="currentColor"
fillRule="evenodd"
ref={ref}
{...props}>
<path
d="M532.48 241.777778h-28.444444a151.608889 151.608889 0 0 0-48.924445 8.533333l-142.222222 76.231111V199.111111a42.382222 42.382222 0 0 1 42.382222-42.097778H455.111111V102.115556a42.666667 42.666667 0 0 1 43.52-42.382223H540.444444a42.666667 42.666667 0 0 1 42.382223 42.382223v59.164444h101.546666a42.666667 42.666667 0 0 1 42.382223 42.382222v131.413334l-156.728889-85.333334a209.351111 209.351111 0 0 0-38.115556-8.533333z m309.191111 461.653333a768 768 0 0 0-220.16 85.333333c-113.777778 63.715556-118.613333 50.915556-207.644444 8.533334a961.422222 961.422222 0 0 0-203.093334-76.231111L151.608889 631.466667c-63.715556-80.497778-76.231111-119.466667-42.382222-139.946667L455.111111 309.475556a127.431111 127.431111 0 0 1 122.88 0l347.022222 182.044444c21.333333 17.066667 38.115556 55.182222-25.315555 139.946667z m122.595556 186.311111a42.097778 42.097778 0 0 1-42.382223 42.097778c-8.248889 0-12.515556 0-16.782222-3.982222a199.111111 199.111111 0 0 0-105.813333-34.133334c-63.431111 0-143.928889 59.448889-143.928889 59.448889S612.977778 995.555556 512 995.555556a220.728889 220.728889 0 0 1-143.928889-42.382223s-89.031111-63.431111-143.928889-59.448889a129.706667 129.706667 0 0 0-106.097778 34.133334 25.884444 25.884444 0 0 1-16.782222 3.982222 42.097778 42.097778 0 0 1-42.382222-42.097778 46.933333 46.933333 0 0 1 21.048889-42.382222s119.182222-136.248889 304.355555 0c0 0 59.448889 42.382222 101.546667 42.382222h42.382222a216.462222 216.462222 0 0 0 101.546667-42.382222c50.915556-33.848889 178.062222-118.613333 304.924444 0a47.502222 47.502222 0 0 1 28.444445 42.382222z m0 0"
p-id="42512"></path>
</svg>
)
})

View File

@ -1,5 +1,3 @@
import { Card, Col, Row } from "antd"
import { useMessageOption } from "@/hooks/useMessageOption.tsx" import { useMessageOption } from "@/hooks/useMessageOption.tsx"
import { useMutation, useQueryClient } from "@tanstack/react-query" import { useMutation, useQueryClient } from "@tanstack/react-query"
import { qaPrompt } from "@/libs/playground.tsx" import { qaPrompt } from "@/libs/playground.tsx"
@ -23,25 +21,10 @@ export const PlaygroundEmpty = () => {
} }
return ( return (
<div className="w-full pb-4 pt-[20%]"> <div className="w-full pb-4 pt-[20%] grid grid-cols-3 gap-3">
{/* 标题区域 */}
{/*<div className="mb-4">*/}
{/* <h2*/}
{/* className="text-xl font-bold text-gray-800"*/}
{/* style={{ lineHeight: "0" }}>*/}
{/* 数联网科创智能体*/}
{/* </h2>*/}
{/* <p className="text-sm text-gray-500">您好!请问有什么可以帮您?</p>*/}
{/*</div>*/}
{/* 卡片网格布局 */}
<Row gutter={[16, 16]} className="w-full">
{qaPrompt.map((item, index) => ( {qaPrompt.map((item, index) => (
<Col key={index} xs={24} sm={12} md={8}> <div
<Card className="p-6 bg-gradient-to-br from-blue-50/90 via-indigo-50/90 to-purple-50/90 backdrop-blur-xl border border-white/60 shadow-xl rounded-2xl cursor-pointer hover:shadow-blue-200/40 hover:from-blue-100/90 hover:to-indigo-100/90 transition-all duration-500 hover:-translate-y-1"
hoverable
style={{ backgroundColor: "#f3f4f6" }}
className="border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer"
onClick={() => handleQuestion(item.title)}> onClick={() => handleQuestion(item.title)}>
<div className="flex items-center"> <div className="flex items-center">
<div className="text-blue-500 mr-2 w-10">{item.icon}</div> <div className="text-blue-500 mr-2 w-10">{item.icon}</div>
@ -49,10 +32,8 @@ export const PlaygroundEmpty = () => {
{item.title} {item.title}
</div> </div>
</div> </div>
</Card> </div>
</Col>
))} ))}
</Row>
</div> </div>
) )
} }

View File

@ -249,13 +249,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
}, [iodSearch]) }, [iodSearch])
return ( return (
<div className="flex w-full flex-col items-center p-2 px-5 pt-1 pb-4"> <div className="flex w-full flex-col items-center pt-1 px-5 pb-4">
<div className="relative z-10 flex w-full flex-col items-center justify-center gap-2 text-base"> <div className="relative z-10 flex w-full flex-col items-center justify-center gap-2 text-base">
<div className="relative flex w-full flex-row justify-center gap-2 lg:w-5/5"> <div className="relative flex w-full flex-row justify-center gap-2 lg:w-5/5">
<div <div
className={` bg-neutral-50 dark:bg-[#262626] relative w-full max-w-[65rem] p-1 backdrop-blur-lg duration-100 border border-gray-300 rounded-xl dark:border-gray-600 className={`shadow-xl relative w-full max-w-[65rem] p-1 rounded-xl bg-gradient-to-br from-white/90 via-blue-50/90 to-cyan-50/90 backdrop-blur-lg border border-blue-100/70 cursor-pointer hover:shadow-blue-100/60 transition-all duration-500`}>
${temporaryChat ? "!bg-gray-200 dark:!bg-black " : ""}
`}>
<div <div
className={`border-b border-gray-200 dark:border-gray-600 relative ${ className={`border-b border-gray-200 dark:border-gray-600 relative ${
form.values.image.length === 0 ? "hidden" : "block" form.values.image.length === 0 ? "hidden" : "block"
@ -361,38 +359,23 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
/> />
</div> </div>
</Tooltip> </Tooltip>
{/*<Tooltip*/}
{/* title={t("tooltip.searchIod")}*/}
{/* className="ml-3">*/}
{/* <div className="inline-flex items-center gap-2">*/}
{/* <PiNetwork*/}
{/* className={`h-5 w-5 dark:text-gray-300 `}*/}
{/* />*/}
{/* <Switch*/}
{/* value={iodSearch}*/}
{/* onChange={(e) => setIodSearch(e)}*/}
{/* checkedChildren={t("form.webSearch.on")}*/}
{/* unCheckedChildren={t("form.webSearch.off")}*/}
{/* />*/}
{/* </div>*/}
{/*</Tooltip>*/}
<Dropdown <Dropdown
menu={{ items: iodSearchItems }} menu={{ items: iodSearchItems }}
placement="bottom" placement="bottom"
trigger={["click"]} trigger={["click"]}
arrow> arrow>
<Button <Button
color="purple" color="default"
variant="filled" variant="filled"
size="large" size="large"
className="w-full mt-4 hover:!bg-[#0057ff1a]" className="w-full mt-4 hover:!bg-[#0057ff1a]"
style={{ style={iodSearch ? {
color: "#0057ff", color: "#0057ff",
background: "#0057ff0f", background: "#0057ff0f",
border: "1px solid #0066ff26" border: "1px solid #0066ff26"
}}> } : {}}>
<PiNetwork className="h-5 w-5" /> <PiNetwork className="h-5 w-5" />
{iodSearch ? "开" : ""} {iodSearch ? "开" : ""}
</Button> </Button>
</Dropdown> </Dropdown>
</div> </div>

View File

@ -1,25 +1,121 @@
import React from "react" import React, { createContext, useContext, useState } from "react"
import { AnimatePresence, motion } from "framer-motion"
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx" import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
import { PlaygroundData } from "@/components/Common/Playground/Data.tsx" import { PlaygroundData } from "@/components/Common/Playground/Data.tsx"
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx" import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx" import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
import { Card } from "antd"
import { CloseOutlined } from "@ant-design/icons"
export const PlaygroundIod = () => { // 定义 Context 类型
return ( interface IodPlaygroundContextType {
<div showPlayground: boolean
className="w-[36%] h-full grid grid-rows-12 gap-3 pt-16 pr-5 pb-0" setShowPlayground: React.Dispatch<React.SetStateAction<boolean>>
style={{ paddingTop: "4rem" }}> detailHeader: React.ReactNode
<div className="w-full row-span-5"> setDetailHeader: React.Dispatch<React.SetStateAction<React.ReactNode>>
<PlaygroundIodRelevant /> detailMain: React.ReactNode
</div> setDetailMain: React.Dispatch<React.SetStateAction<React.ReactNode>>
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar"> }
<PlaygroundData />
<PlaygroundScene /> // 创建 Context
</div> const PlaygroundContext = createContext<IodPlaygroundContextType | undefined>(
<div className="w-full row-span-3 pb-3"> undefined
<PlaygroundTeam /> )
</div>
</div> // 创建自定义 hook 以便子组件使用
export const useIodPlaygroundContext = () => {
const context = useContext(PlaygroundContext)
if (context === undefined) {
throw new Error(
"usePlaygroundContext must be used within a PlaygroundProvider"
)
}
return context
}
export const PlaygroundIod = () => {
const [showPlayground, setShowPlayground] = useState<boolean>(true)
const [detailHeader, setDetailHeader] = useState(<></>)
const [detailMain, setDetailMain] = useState(<></>)
return (
<PlaygroundContext.Provider
value={{
showPlayground,
setShowPlayground,
detailMain,
setDetailMain,
detailHeader,
setDetailHeader
}}>
<div className="w-[36%] h-full pt-16 pr-5 pb-0">
<PlaygroundContent />
</div>
</PlaygroundContext.Provider>
)
}
// 子组件使用修改card的默认样式
const classNames =
"h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden !bg-[rgba(240,245,255,0.3)] backdrop-blur-sm border border-white/30 shadow-xl rounded-2xl"
// 将原来的返回内容移到这个组件中
const PlaygroundContent = () => {
const { showPlayground, detailMain, detailHeader, setShowPlayground } =
useIodPlaygroundContext()
return (
<AnimatePresence mode="popLayout">
{showPlayground ? (
<motion.div
key="playground"
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{
duration: 0.6,
ease: "easeInOut"
}}
className="h-full grid grid-rows-12 gap-3">
<div className="w-full row-span-5">
<PlaygroundIodRelevant className={classNames.replace('!bg-[rgba(240,245,255,0.3)]', '')} />
</div>
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
<PlaygroundData className={classNames} />
<PlaygroundScene className={classNames} />
</div>
<div className="w-full row-span-3 pb-3">
<PlaygroundTeam className={classNames} />
</div>
</motion.div>
) : (
<motion.div
key="alternative"
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{
duration: 0.6,
ease: "easeInOut"
}}
className="h-full pb-5">
<Card className="h-full shadow-xl shadow-gray-500/20 [&_.ant-card-body]:h-full">
<div className="flex flex-col h-full">
<div className="pb-6 flex items-center justify-between">
<div>{detailHeader}</div>
<CloseOutlined
size={30}
className="hover:text-red-500 cursor-pointer transition-colors duration-200 text-xl"
onClick={() => setShowPlayground(true)}
/>
</div>
{detailMain}
</div>
</Card>
</motion.div>
)}
</AnimatePresence>
) )
} }

View File

@ -25,7 +25,9 @@ const useDynamicTextareaSize = (
// Set max-height and adjust overflow behavior if maxHeight is provided // Set max-height and adjust overflow behavior if maxHeight is provided
currentTextarea.style.maxHeight = `${maxHeight}px`; currentTextarea.style.maxHeight = `${maxHeight}px`;
currentTextarea.style.overflowY = contentHeight > maxHeight ? "scroll" : "hidden"; currentTextarea.style.overflowY = contentHeight > maxHeight ? "scroll" : "hidden";
currentTextarea.style.height = `${Math.min(contentHeight, maxHeight)}px`; currentTextarea.style.height = `${Math.min(contentHeight, maxHeight) < 60 ? 60 : Math.min(contentHeight, maxHeight)}px`;
currentTextarea.style.fontWeight = "normal";
currentTextarea.style.color = "#374151";
} else { } else {
// Adjust height without max height constraint // Adjust height without max height constraint

View File

@ -1,12 +1,10 @@
import RocketSvg from "@/assets/icons/rocket.svg" import { Avatar } from "antd"
import BulbSvg from "@/assets/icons/bulb.svg" import { MedicineBottleFillIcon } from "@/components/Icons/MedicineBottleFill.tsx"
import EyeSvg from "@/assets/icons/eye.svg" import { CheckIcon } from "@/components/Icons/Check.tsx"
import ASvg from "@/assets/icons/a.svg" import { NewBottleIcon } from "@/components/Icons/NewBottle.tsx"
import BSvg from "@/assets/icons/b.svg" import { BatteryIcon } from "@/components/Icons/Battery.tsx"
import CSvg from "@/assets/icons/c.svg" import { ShipIcon } from "@/components/Icons/Ship.tsx"
import DSvg from "@/assets/icons/d.svg" import { Ship1Icon } from "@/components/Icons/Ship1.tsx"
import ESvg from "@/assets/icons/e.svg"
import FSvg from "@/assets/icons/f.svg"
export const qaPrompt = [ export const qaPrompt = [
// { // {
@ -23,29 +21,71 @@ export const qaPrompt =[
// }, // },
{ {
title: "新药临床研究如何提升实验安全性?", title: "新药临床研究如何提升实验安全性?",
icon: <img src={ASvg} alt="Rocket" className="w-10 my-0" />, icon: (
<Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<MedicineBottleFillIcon className="w-7" />}
/>
)
}, },
{ {
title: "如何加速新药申报和审批?", title: "如何加速新药申报和审批?",
icon: <img src={BSvg} alt="Rocket" className="w-10 my-0" />, icon: (
<Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<CheckIcon className="w-7" />}
/>
)
}, },
{ {
title: "如何研制与司美格鲁肽相似的新药?", title: "如何研制与司美格鲁肽相似的新药?",
icon: <img src={CSvg} alt="Rocket" className="w-10 my-0" />, icon: (
<Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<NewBottleIcon className="w-7" />}
/>
)
}, },
{ {
title: "如何解决固态电池的成本和寿命难题?", title: "如何解决固态电池的成本和寿命难题?",
icon: <img src={DSvg} alt="Rocket" className="w-10 my-0" />, icon: (
<Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<BatteryIcon className="w-7" />}
/>
)
}, },
{ {
title: "如何解决船舶制造中的材料腐蚀难题?", title: "如何解决船舶制造中的材料腐蚀难题?",
icon: <img src={ESvg} alt="Rocket" className="w-10 my-0" />, icon: (
<Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<ShipIcon className="w-7" />}
/>
)
}, },
{ {
title: "如何解决船舶制造中流体模拟和建模优化难题?", title: "如何解决船舶制造中流体模拟和建模优化难题?",
icon: <img src={FSvg} alt="Rocket" className="w-10 my-0" />, icon: (
}, <Avatar
className="!bg-[#3581e3b3]"
shape="square"
size={40}
icon={<Ship1Icon className="w-7" />}
/>
)
}
].map((item, index) => ({ ].map((item, index) => ({
...item, ...item,
id: index.toString(), id: index.toString()
})) }))